1 /* GStreamer
2  * Copyright (C) 2008-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /* Implementation of SMPTE 422M - Mapping JPEG2000 codestreams into the MXF
21  * Generic Container
22  */
23 
24 /* TODO:
25  *  - parse the jpeg2000 sub-descriptor, see SMPTE 422M 7.2
26  *  - Add support for XYZ colorspace
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <gst/gst.h>
34 #include <gst/video/video.h>
35 #include <string.h>
36 
37 #include "mxfjpeg2000.h"
38 #include "mxfessence.h"
39 
40 GST_DEBUG_CATEGORY_EXTERN (mxf_debug);
41 #define GST_CAT_DEFAULT mxf_debug
42 
43 static gboolean
mxf_is_jpeg2000_essence_track(const MXFMetadataTimelineTrack * track)44 mxf_is_jpeg2000_essence_track (const MXFMetadataTimelineTrack * track)
45 {
46   guint i;
47 
48   g_return_val_if_fail (track != NULL, FALSE);
49 
50   if (track->parent.descriptor == NULL)
51     return FALSE;
52 
53   for (i = 0; i < track->parent.n_descriptor; i++) {
54     MXFMetadataFileDescriptor *d = track->parent.descriptor[i];
55     MXFUL *key;
56 
57     if (!d)
58       continue;
59 
60     key = &d->essence_container;
61     /* SMPTE 422M 5.4 */
62     if (mxf_is_generic_container_essence_container_label (key) &&
63         key->u[12] == 0x02 && key->u[13] == 0x0c &&
64         (key->u[14] == 0x01 || key->u[14] == 0x02))
65       return TRUE;
66   }
67 
68   return FALSE;
69 }
70 
71 static GstFlowReturn
mxf_jpeg2000_handle_essence_element(const MXFUL * key,GstBuffer * buffer,GstCaps * caps,MXFMetadataTimelineTrack * track,gpointer mapping_data,GstBuffer ** outbuf)72 mxf_jpeg2000_handle_essence_element (const MXFUL * key, GstBuffer * buffer,
73     GstCaps * caps,
74     MXFMetadataTimelineTrack * track,
75     gpointer mapping_data, GstBuffer ** outbuf)
76 {
77   *outbuf = buffer;
78 
79   /* SMPTE 422M 5.1 */
80   if (key->u[12] != 0x15 || (key->u[14] != 0x08 && key->u[14] != 0x09)) {
81     GST_ERROR ("Invalid JPEG2000 essence element");
82     return GST_FLOW_ERROR;
83   }
84 
85   return GST_FLOW_OK;
86 }
87 
88 static MXFEssenceWrapping
mxf_jpeg2000_get_track_wrapping(const MXFMetadataTimelineTrack * track)89 mxf_jpeg2000_get_track_wrapping (const MXFMetadataTimelineTrack * track)
90 {
91   guint i;
92 
93   g_return_val_if_fail (track != NULL, MXF_ESSENCE_WRAPPING_CUSTOM_WRAPPING);
94 
95   if (track->parent.descriptor == NULL) {
96     GST_ERROR ("No descriptor found for this track");
97     return MXF_ESSENCE_WRAPPING_CUSTOM_WRAPPING;
98   }
99 
100   for (i = 0; i < track->parent.n_descriptor; i++) {
101     if (!track->parent.descriptor[i])
102       continue;
103 
104     if (!MXF_IS_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (track->
105             parent.descriptor[i])
106         && !(MXF_IS_METADATA_FILE_DESCRIPTOR (track->parent.descriptor[i])
107             && !MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (track->
108                 parent.descriptor[i])))
109       continue;
110 
111     switch (track->parent.descriptor[i]->essence_container.u[14]) {
112       case 0x01:
113         return MXF_ESSENCE_WRAPPING_FRAME_WRAPPING;
114         break;
115       case 0x02:
116         return MXF_ESSENCE_WRAPPING_CLIP_WRAPPING;
117         break;
118       default:
119         return MXF_ESSENCE_WRAPPING_CUSTOM_WRAPPING;
120         break;
121     }
122   }
123 
124   return MXF_ESSENCE_WRAPPING_CUSTOM_WRAPPING;
125 }
126 
127 static GstCaps *
mxf_jpeg2000_create_caps(MXFMetadataTimelineTrack * track,GstTagList ** tags,gboolean * intra_only,MXFEssenceElementHandleFunc * handler,gpointer * mapping_data)128 mxf_jpeg2000_create_caps (MXFMetadataTimelineTrack * track, GstTagList ** tags,
129     gboolean * intra_only, MXFEssenceElementHandleFunc * handler,
130     gpointer * mapping_data)
131 {
132   MXFMetadataFileDescriptor *f = NULL;
133   MXFMetadataGenericPictureEssenceDescriptor *p = NULL;
134   guint i;
135   GstCaps *caps = NULL;
136   const gchar *colorspace;
137 
138   g_return_val_if_fail (track != NULL, NULL);
139 
140   if (track->parent.descriptor == NULL) {
141     GST_ERROR ("No descriptor found for this track");
142     return NULL;
143   }
144 
145   for (i = 0; i < track->parent.n_descriptor; i++) {
146     if (!track->parent.descriptor[i])
147       continue;
148 
149     if (MXF_IS_METADATA_GENERIC_PICTURE_ESSENCE_DESCRIPTOR (track->
150             parent.descriptor[i])) {
151       p = (MXFMetadataGenericPictureEssenceDescriptor *) track->parent.
152           descriptor[i];
153       f = track->parent.descriptor[i];
154       break;
155     } else if (MXF_IS_METADATA_FILE_DESCRIPTOR (track->parent.descriptor[i]) &&
156         !MXF_IS_METADATA_MULTIPLE_DESCRIPTOR (track->parent.descriptor[i])) {
157       f = track->parent.descriptor[i];
158     }
159   }
160 
161   if (!f) {
162     GST_ERROR ("No descriptor found for this track");
163     return NULL;
164   }
165 
166   colorspace = "sRGB";
167   if (p && MXF_IS_METADATA_CDCI_PICTURE_ESSENCE_DESCRIPTOR (p)) {
168     colorspace = "sYUV";
169   } else if (p && MXF_IS_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR (p)) {
170     MXFMetadataRGBAPictureEssenceDescriptor *r =
171         (MXFMetadataRGBAPictureEssenceDescriptor *) p;
172     gboolean rgb = TRUE;
173     gboolean xyz = TRUE;
174     gboolean yuv = TRUE;
175     guint i;
176 
177     if (r->pixel_layout) {
178       for (i = 0; i < r->n_pixel_layout; i++) {
179         guint8 c = r->pixel_layout[2 * i];
180 
181         switch (c) {
182           case 'R':
183           case 'r':
184           case 'G':
185           case 'g':
186           case 'B':
187           case 'b':
188             xyz = yuv = FALSE;
189             break;
190           case 'Y':
191           case 'y':
192             rgb = FALSE;
193             break;
194           case 'U':
195           case 'u':
196           case 'V':
197           case 'v':
198             xyz = rgb = FALSE;
199             break;
200           case 'X':
201           case 'x':
202           case 'Z':
203           case 'z':
204             rgb = yuv = FALSE;
205             break;
206           default:
207             break;
208         }
209       }
210       if (rgb) {
211         colorspace = "sRGB";
212       } else if (yuv) {
213         colorspace = "sYUV";
214       } else if (xyz) {
215         GST_ERROR ("JPEG2000 with XYZ colorspace not supported yet");
216         return NULL;
217       }
218     }
219   }
220 
221   *handler = mxf_jpeg2000_handle_essence_element;
222 
223   /* TODO: What about other field values? */
224   caps =
225       gst_caps_new_simple ("image/x-jpc", "colorspace",
226       G_TYPE_STRING, colorspace, NULL);
227   if (p) {
228     mxf_metadata_generic_picture_essence_descriptor_set_caps (p, caps);
229   } else {
230     GST_WARNING ("Only a generic file descriptor found");
231   }
232 
233   if (!*tags)
234     *tags = gst_tag_list_new_empty ();
235   gst_tag_list_add (*tags, GST_TAG_MERGE_APPEND, GST_TAG_VIDEO_CODEC,
236       "JPEG 2000", NULL);
237 
238   *intra_only = TRUE;
239 
240   return caps;
241 }
242 
243 static const MXFEssenceElementHandler mxf_jpeg2000_essence_element_handler = {
244   mxf_is_jpeg2000_essence_track,
245   mxf_jpeg2000_get_track_wrapping,
246   mxf_jpeg2000_create_caps
247 };
248 
249 static GstFlowReturn
mxf_jpeg2000_write_func(GstBuffer * buffer,gpointer mapping_data,GstAdapter * adapter,GstBuffer ** outbuf,gboolean flush)250 mxf_jpeg2000_write_func (GstBuffer * buffer,
251     gpointer mapping_data, GstAdapter * adapter, GstBuffer ** outbuf,
252     gboolean flush)
253 {
254   *outbuf = buffer;
255   return GST_FLOW_OK;
256 }
257 
258 static const guint8 jpeg2000_essence_container_ul[] = {
259   0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07,
260   0x0d, 0x01, 0x03, 0x01, 0x02, 0x0c, 0x01, 0x00
261 };
262 
263 static const guint jpeg2000_picture_essence_coding[] = {
264   0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x07,
265   0x04, 0x01, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00
266 };
267 
268 static MXFMetadataFileDescriptor *
mxf_jpeg2000_get_descriptor(GstPadTemplate * tmpl,GstCaps * caps,MXFEssenceElementWriteFunc * handler,gpointer * mapping_data)269 mxf_jpeg2000_get_descriptor (GstPadTemplate * tmpl, GstCaps * caps,
270     MXFEssenceElementWriteFunc * handler, gpointer * mapping_data)
271 {
272   MXFMetadataRGBAPictureEssenceDescriptor *ret;
273   GstStructure *s;
274   const gchar *colorspace;
275 
276   s = gst_caps_get_structure (caps, 0);
277   if (strcmp (gst_structure_get_name (s), "image/x-jpc") != 0 ||
278       !gst_structure_get_string (s, "colorspace")) {
279     GST_ERROR ("Invalid caps %" GST_PTR_FORMAT, caps);
280     return NULL;
281   }
282 
283   colorspace = gst_structure_get_string (s, "colorspace");
284 
285   ret = (MXFMetadataRGBAPictureEssenceDescriptor *)
286       g_object_new (MXF_TYPE_METADATA_RGBA_PICTURE_ESSENCE_DESCRIPTOR, NULL);
287 
288   memcpy (&ret->parent.parent.essence_container, &jpeg2000_essence_container_ul,
289       16);
290   memcpy (&ret->parent.picture_essence_coding, &jpeg2000_picture_essence_coding,
291       16);
292 
293   if (g_str_equal (colorspace, "sRGB")) {
294     ret->n_pixel_layout = 3;
295     ret->pixel_layout = g_new0 (guint8, 6);
296     ret->pixel_layout[0] = 'R';
297     ret->pixel_layout[1] = 8;
298     ret->pixel_layout[2] = 'G';
299     ret->pixel_layout[3] = 8;
300     ret->pixel_layout[4] = 'B';
301     ret->pixel_layout[5] = 8;
302   } else if (g_str_equal (colorspace, "sYUV")) {
303     ret->n_pixel_layout = 3;
304     ret->pixel_layout = g_new0 (guint8, 6);
305     ret->pixel_layout[0] = 'Y';
306     ret->pixel_layout[1] = 8;
307     ret->pixel_layout[2] = 'U';
308     ret->pixel_layout[3] = 8;
309     ret->pixel_layout[4] = 'V';
310     ret->pixel_layout[5] = 8;
311   } else {
312     g_assert_not_reached ();
313   }
314 
315   if (!mxf_metadata_generic_picture_essence_descriptor_from_caps (&ret->parent,
316           caps)) {
317     g_object_unref (ret);
318     return NULL;
319   }
320 
321   *handler = mxf_jpeg2000_write_func;
322 
323   return (MXFMetadataFileDescriptor *) ret;
324 }
325 
326 static void
mxf_jpeg2000_update_descriptor(MXFMetadataFileDescriptor * d,GstCaps * caps,gpointer mapping_data,GstBuffer * buf)327 mxf_jpeg2000_update_descriptor (MXFMetadataFileDescriptor * d, GstCaps * caps,
328     gpointer mapping_data, GstBuffer * buf)
329 {
330   return;
331 }
332 
333 static void
mxf_jpeg2000_get_edit_rate(MXFMetadataFileDescriptor * a,GstCaps * caps,gpointer mapping_data,GstBuffer * buf,MXFMetadataSourcePackage * package,MXFMetadataTimelineTrack * track,MXFFraction * edit_rate)334 mxf_jpeg2000_get_edit_rate (MXFMetadataFileDescriptor * a, GstCaps * caps,
335     gpointer mapping_data, GstBuffer * buf, MXFMetadataSourcePackage * package,
336     MXFMetadataTimelineTrack * track, MXFFraction * edit_rate)
337 {
338   edit_rate->n = a->sample_rate.n;
339   edit_rate->d = a->sample_rate.d;
340 }
341 
342 static guint32
mxf_jpeg2000_get_track_number_template(MXFMetadataFileDescriptor * a,GstCaps * caps,gpointer mapping_data)343 mxf_jpeg2000_get_track_number_template (MXFMetadataFileDescriptor * a,
344     GstCaps * caps, gpointer mapping_data)
345 {
346   return (0x15 << 24) | (0x08 << 8);
347 }
348 
349 static MXFEssenceElementWriter mxf_jpeg2000_essence_element_writer = {
350   mxf_jpeg2000_get_descriptor,
351   mxf_jpeg2000_update_descriptor,
352   mxf_jpeg2000_get_edit_rate,
353   mxf_jpeg2000_get_track_number_template,
354   NULL,
355   {{0,}}
356 };
357 
358 void
mxf_jpeg2000_init(void)359 mxf_jpeg2000_init (void)
360 {
361   mxf_essence_element_handler_register (&mxf_jpeg2000_essence_element_handler);
362 
363   mxf_jpeg2000_essence_element_writer.pad_template =
364       gst_pad_template_new ("jpeg2000_video_sink_%u", GST_PAD_SINK,
365       GST_PAD_REQUEST,
366       gst_caps_from_string ("image/x-jpc, width = "
367           GST_VIDEO_SIZE_RANGE ", height = " GST_VIDEO_SIZE_RANGE
368           ", framerate = " GST_VIDEO_FPS_RANGE
369           ", colorspace = (string) { \"sRGB\", \"sYUV\" }"));
370   memcpy (&mxf_jpeg2000_essence_element_writer.data_definition,
371       mxf_metadata_track_identifier_get (MXF_METADATA_TRACK_PICTURE_ESSENCE),
372       16);
373   mxf_essence_element_writer_register (&mxf_jpeg2000_essence_element_writer);
374 }
375