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