video: fix some GIR annotations
[platform/upstream/gstreamer.git] / gst-libs / gst / video / video-anc.c
1 /* GStreamer
2  * Copyright (C) 2018 Edward Hervey <edward@centricular.com>
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 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <string.h>
25 #include <gst/base/gstbytereader.h>
26 #include "video-anc.h"
27
28 /**
29  * SECTION:gstvideoanc
30  * @title: GstVideo Ancillary
31  * @short_description: Utilities for Ancillary data, VBI and Closed Caption
32  *
33  * A collection of objects and methods to assist with handling Ancillary Data
34  * present in Vertical Blanking Interval as well as Closed Caption.
35  */
36
37
38 #ifndef GST_DISABLE_GST_DEBUG
39 #define GST_CAT_DEFAULT ensure_debug_category()
40 static GstDebugCategory *
41 ensure_debug_category (void)
42 {
43   static gsize cat_gonce = 0;
44
45   if (g_once_init_enter (&cat_gonce)) {
46     gsize cat_done;
47
48     cat_done = (gsize) _gst_debug_category_new ("video-anc", 0,
49         "Ancillary data, VBI and CC utilities");
50
51     g_once_init_leave (&cat_gonce, cat_done);
52   }
53
54   return (GstDebugCategory *) cat_gonce;
55 }
56 #else
57 #define ensure_debug_category() /* NOOP */
58 #endif /* GST_DISABLE_GST_DEBUG */
59
60 struct _GstVideoVBIParser
61 {
62   GstVideoInfo info;            /* format of the lines provided */
63   guint8 *work_data;            /* Converted line in planar 16bit format */
64   guint32 work_data_size;       /* Size in bytes of work_data */
65   guint offset;                 /* Current offset (in bytes) in work_data */
66   gboolean bit16;               /* Data is stored as 16bit if TRUE. Else 8bit(without parity) */
67 };
68
69 G_DEFINE_BOXED_TYPE (GstVideoVBIParser, gst_video_vbi_parser,
70     (GBoxedCopyFunc) gst_video_vbi_parser_copy,
71     (GBoxedFreeFunc) gst_video_vbi_parser_free);
72
73 GstVideoVBIParser *
74 gst_video_vbi_parser_copy (const GstVideoVBIParser * parser)
75 {
76   GstVideoVBIParser *res;
77
78   res = gst_video_vbi_parser_new (GST_VIDEO_INFO_FORMAT (&parser->info),
79       parser->info.width);
80   if (res) {
81     memcpy (res->work_data, parser->work_data, parser->work_data_size);
82   }
83   return res;
84 }
85
86 /* Smallest ANC size (which would have a size Data Count of 0 though) */
87 #define SMALLEST_ANC_SIZE 7
88
89 static GstVideoVBIParserResult
90 get_ancillary_16 (GstVideoVBIParser * parser, GstVideoAncillary * anc)
91 {
92   gboolean found = FALSE;
93   guint16 *data = (guint16 *) parser->work_data;
94
95   g_return_val_if_fail (parser != NULL, GST_VIDEO_VBI_PARSER_RESULT_ERROR);
96   g_return_val_if_fail (anc != NULL, GST_VIDEO_VBI_PARSER_RESULT_ERROR);
97
98   while (parser->offset < parser->work_data_size + SMALLEST_ANC_SIZE) {
99     guint8 DID, SDID, DC;
100     guint i;
101
102     /* Look for ADF
103      * FIXME : This assumes 10bit data with parity ! */
104     if (data[parser->offset] != 0x000 ||
105         data[parser->offset + 1] != 0x3ff ||
106         data[parser->offset + 2] != 0x3ff) {
107       parser->offset += 1;
108       continue;
109     }
110
111     /* FIXME : Add parity and checksum checks at some point if using
112      * 10bit data */
113
114     /* We have a valid ADF */
115     DID = data[parser->offset + 3] & 0xff;
116     SDID = data[parser->offset + 4] & 0xff;
117     DC = data[parser->offset + 5] & 0xff;
118     /* Check if we have enough room to get the User Data */
119     if (parser->offset >= parser->work_data_size + SMALLEST_ANC_SIZE + DC)
120       goto not_enough_data;
121
122     /* We found a valid ANC \o/ */
123     anc->DID = DID;
124     anc->SDID_block_number = SDID;
125     anc->data_count = DC;
126     memset (anc->data, 0, 256);
127     for (i = 0; i < anc->data_count; i++)
128       anc->data[i] = data[parser->offset + 6 + i] & 0xff;
129     found = TRUE;
130     parser->offset += SMALLEST_ANC_SIZE + DC;
131     break;
132   }
133
134   if (found)
135     return GST_VIDEO_VBI_PARSER_RESULT_OK;
136
137   return GST_VIDEO_VBI_PARSER_RESULT_DONE;
138
139   /* ERRORS */
140 not_enough_data:
141   {
142     GST_WARNING ("ANC requires more User Data that available line size");
143     /* Avoid further calls to go in the same error */
144     parser->offset = parser->work_data_size;
145     return GST_VIDEO_VBI_PARSER_RESULT_ERROR;
146   }
147 }
148
149 static GstVideoVBIParserResult
150 get_ancillary_8 (GstVideoVBIParser * parser, GstVideoAncillary * anc)
151 {
152   gboolean found = FALSE;
153   guint8 *data = parser->work_data;
154
155   g_return_val_if_fail (parser != NULL, GST_VIDEO_VBI_PARSER_RESULT_ERROR);
156   g_return_val_if_fail (anc != NULL, GST_VIDEO_VBI_PARSER_RESULT_ERROR);
157
158   while (parser->offset < parser->work_data_size + SMALLEST_ANC_SIZE) {
159     guint8 DID, SDID, DC;
160     guint i;
161
162     /* Look for 8bit ADF (0x00 0xff 0xff) */
163     if (data[parser->offset] != 0x00 ||
164         data[parser->offset + 1] != 0xff || data[parser->offset + 2] != 0xff) {
165       parser->offset += 1;
166       continue;
167     }
168
169     /* We have a valid ADF */
170     DID = data[parser->offset + 3];
171     SDID = data[parser->offset + 4];
172     DC = data[parser->offset + 5];
173     /* Check if we have enough room to get the User Data */
174     if (parser->offset >= parser->work_data_size + SMALLEST_ANC_SIZE + DC)
175       goto not_enough_data;
176
177     /* We found a valid ANC \o/ */
178     anc->DID = DID;
179     anc->SDID_block_number = SDID;
180     anc->data_count = DC;
181     memset (anc->data, 0, 256);
182     for (i = 0; i < anc->data_count; i++)
183       anc->data[i] = data[parser->offset + 6 + i];
184     found = TRUE;
185     parser->offset += SMALLEST_ANC_SIZE + DC;
186     break;
187   }
188
189   if (found)
190     return GST_VIDEO_VBI_PARSER_RESULT_OK;
191
192   return GST_VIDEO_VBI_PARSER_RESULT_DONE;
193
194   /* ERRORS */
195 not_enough_data:
196   {
197     GST_WARNING ("ANC requires more User Data that available line size");
198     /* Avoid further calls to go in the same error */
199     parser->offset = parser->work_data_size;
200     return GST_VIDEO_VBI_PARSER_RESULT_ERROR;
201   }
202 }
203
204 /**
205  * gst_video_vbi_parser_get_ancillary:
206  * @parser: a #GstVideoVBIParser
207  * @anc: (out caller-allocates): a #GstVideoAncillary to start the eventual ancillary data
208  *
209  * Parse the line provided previously by gst_video_vbi_parser_add_line().
210  *
211  * Since: 1.16
212  *
213  * Returns: %GST_VIDEO_VBI_PARSER_RESULT_OK if ancillary data was found and
214  * @anc was filled. %GST_VIDEO_VBI_PARSER_RESULT_DONE if there wasn't any
215  * data.
216  */
217
218 GstVideoVBIParserResult
219 gst_video_vbi_parser_get_ancillary (GstVideoVBIParser * parser,
220     GstVideoAncillary * anc)
221 {
222   if (parser->bit16)
223     return get_ancillary_16 (parser, anc);
224   return get_ancillary_8 (parser, anc);
225 }
226
227 /**
228  * gst_video_vbi_parser_new:
229  * @format: a #GstVideoFormat
230  * @pixel_width: The width in pixel to use
231  *
232  * Create a new #GstVideoVBIParser for the specified @format and @pixel_width.
233  *
234  * Since: 1.16
235  *
236  * Returns: The new #GstVideoVBIParser or %NULL if the @format and/or @pixel_width
237  * is not supported.
238  */
239 GstVideoVBIParser *
240 gst_video_vbi_parser_new (GstVideoFormat format, guint32 pixel_width)
241 {
242   GstVideoVBIParser *parser;
243
244   switch (format) {
245     case GST_VIDEO_FORMAT_v210:
246       parser = g_new0 (GstVideoVBIParser, 1);
247       parser->bit16 = TRUE;
248       break;
249     case GST_VIDEO_FORMAT_UYVY:
250       parser = g_new0 (GstVideoVBIParser, 1);
251       parser->bit16 = FALSE;
252       break;
253     default:
254       GST_WARNING ("Format not supported by GstVideoVBIParser");
255       return NULL;
256   }
257
258   gst_video_info_init (&parser->info);
259   if (!gst_video_info_set_format (&parser->info, format, pixel_width, 1)) {
260     GST_ERROR ("Could not create GstVideoInfo");
261     g_free (parser);
262     return NULL;
263   }
264
265   /* Allocate the workspace which is going to be 2 * pixel_width big
266    *  2 : number of pixels per "component" (we only deal with 4:2:2)
267    * We use 1 or 2 bytes per pixel depending on whether we are internally
268    * working in 8 or 16bit */
269   parser->work_data_size = 2 * pixel_width;
270   if (parser->bit16)
271     parser->work_data = g_malloc0 (parser->work_data_size * 2);
272   else
273     parser->work_data = g_malloc0 (parser->work_data_size);
274   parser->offset = 0;
275
276   return parser;
277 }
278
279 /**
280  * gst_video_vbi_parser_free:
281  * @parser: a #GstVideoVBIParser
282  *
283  * Frees the @parser.
284  *
285  * Since: 1.16
286  */
287 void
288 gst_video_vbi_parser_free (GstVideoVBIParser * parser)
289 {
290   g_free (parser->work_data);
291   g_free (parser);
292 }
293
294
295 static void
296 convert_line_uyvy (GstVideoVBIParser * parser, const guint8 * data)
297 {
298   guint i;
299   guint8 *y = parser->work_data;
300   guint8 *uv = y + parser->info.width;
301
302   for (i = 0; i < parser->info.width - 3; i += 4) {
303     *uv++ = data[(i / 4) * 4 + 0];
304     *y++ = data[(i / 4) * 4 + 1];
305     *uv++ = data[(i / 4) * 4 + 2];
306     *y++ = data[(i / 4) * 4 + 3];
307   }
308   GST_MEMDUMP ("Converted line", parser->work_data, 128);
309 }
310
311 static void
312 gst_info_dump_mem16_line (gchar * linebuf, gsize linebuf_size,
313     const guint16 * mem, gsize mem_offset, gsize mem_size)
314 {
315   gchar hexstr[50], digitstr[6];
316
317   if (mem_size > 8)
318     mem_size = 8;
319
320   hexstr[0] = '\0';
321
322   if (mem != NULL) {
323     guint i = 0;
324
325     mem += mem_offset;
326     while (i < mem_size) {
327       g_snprintf (digitstr, sizeof (digitstr), "%04x ", mem[i]);
328       g_strlcat (hexstr, digitstr, sizeof (hexstr));
329       ++i;
330     }
331   }
332
333   g_snprintf (linebuf, linebuf_size, "%08x: %-48.48s",
334       (guint) mem_offset, hexstr);
335 }
336
337 static void
338 convert_line_v210 (GstVideoVBIParser * parser, const guint8 * data)
339 {
340   guint i;
341   guint16 *y = (guint16 *) parser->work_data;
342   guint16 *uv = y + parser->info.width;
343   guint32 a, b, c, d;
344
345   /* Convert the line */
346   for (i = 0; i < parser->info.width - 5; i += 6) {
347     a = GST_READ_UINT32_LE (data + (i / 6) * 16 + 0);
348     b = GST_READ_UINT32_LE (data + (i / 6) * 16 + 4);
349     c = GST_READ_UINT32_LE (data + (i / 6) * 16 + 8);
350     d = GST_READ_UINT32_LE (data + (i / 6) * 16 + 12);
351
352     *uv++ = (a >> 0) & 0x3ff;
353     *y++ = (a >> 10) & 0x3ff;
354     *uv++ = (a >> 20) & 0x3ff;
355     *y++ = (b >> 0) & 0x3ff;
356
357     *uv++ = (b >> 10) & 0x3ff;
358     *y++ = (b >> 20) & 0x3ff;
359     *uv++ = (c >> 0) & 0x3ff;
360     *y++ = (c >> 10) & 0x3ff;
361
362     *uv++ = (c >> 20) & 0x3ff;
363     *y++ = (d >> 0) & 0x3ff;
364     *uv++ = (d >> 10) & 0x3ff;
365     *y++ = (d >> 20) & 0x3ff;
366   }
367
368   if (0) {
369     guint off = 0;
370     gsize length = parser->info.width * 2;
371
372     GST_TRACE ("--------"
373         "-------------------------------------------------------------------");
374
375     while (off < length) {
376       gchar buf[128];
377
378       /* gst_info_dump_mem_line will process 16 bytes (8 16bit chunks) at most */
379       gst_info_dump_mem16_line (buf, sizeof (buf),
380           (guint16 *) parser->work_data, off, length - off);
381       GST_TRACE ("%s", buf);
382       off += 8;
383     }
384     GST_TRACE ("--------"
385         "-------------------------------------------------------------------");
386   }
387 }
388
389 /**
390  * gst_video_vbi_parser_add_line:
391  * @parser: a #GstVideoVBIParser
392  * @data: (array) (transfer none): The line of data to parse
393  *
394  * Provide a new line of data to the @parser. Call gst_video_vbi_parser_get_ancillary()
395  * to get the Ancillary data that might be present on that line.
396  *
397  * Since: 1.16
398  */
399 void
400 gst_video_vbi_parser_add_line (GstVideoVBIParser * parser, const guint8 * data)
401 {
402   /* Reset offset */
403   parser->offset = 0;
404
405   switch (GST_VIDEO_INFO_FORMAT (&parser->info)) {
406     case GST_VIDEO_FORMAT_v210:
407       convert_line_v210 (parser, data);
408       break;
409     case GST_VIDEO_FORMAT_UYVY:
410       convert_line_uyvy (parser, data);
411       break;
412     default:
413       GST_ERROR ("UNSUPPORTED FORMAT !");
414       g_assert_not_reached ();
415       break;
416   }
417 }
418
419 /* Closed Caption Meta implementation *******************************************/
420
421 GType
422 gst_video_caption_meta_api_get_type (void)
423 {
424   static volatile GType type;
425
426   if (g_once_init_enter (&type)) {
427     static const gchar *tags[] = { NULL };
428     GType _type = gst_meta_api_type_register ("GstVideoCaptionMetaAPI", tags);
429     GST_INFO ("registering");
430     g_once_init_leave (&type, _type);
431   }
432   return type;
433 }
434
435
436 static gboolean
437 gst_video_caption_meta_transform (GstBuffer * dest, GstMeta * meta,
438     GstBuffer * buffer, GQuark type, gpointer data)
439 {
440   GstVideoCaptionMeta *dmeta, *smeta;
441
442   /* We always copy over the caption meta */
443   smeta = (GstVideoCaptionMeta *) meta;
444
445   GST_DEBUG ("copy caption metadata");
446   dmeta =
447       gst_buffer_add_video_caption_meta (dest, smeta->caption_type,
448       smeta->data, smeta->size);
449   if (!dmeta)
450     return FALSE;
451
452   return TRUE;
453 }
454
455 static gboolean
456 gst_video_caption_meta_init (GstMeta * meta, gpointer params,
457     GstBuffer * buffer)
458 {
459   GstVideoCaptionMeta *emeta = (GstVideoCaptionMeta *) meta;
460
461   emeta->caption_type = GST_VIDEO_CAPTION_TYPE_UNKNOWN;
462   emeta->data = NULL;
463   emeta->size = 0;
464
465   return TRUE;
466 }
467
468 static void
469 gst_video_caption_meta_free (GstMeta * meta, GstBuffer * buffer)
470 {
471   GstVideoCaptionMeta *emeta = (GstVideoCaptionMeta *) meta;
472
473   g_free (emeta->data);
474 }
475
476 const GstMetaInfo *
477 gst_video_caption_meta_get_info (void)
478 {
479   static const GstMetaInfo *meta_info = NULL;
480
481   if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
482     const GstMetaInfo *mi = gst_meta_register (GST_VIDEO_CAPTION_META_API_TYPE,
483         "GstVideoCaptionMeta",
484         sizeof (GstVideoCaptionMeta),
485         gst_video_caption_meta_init,
486         gst_video_caption_meta_free,
487         gst_video_caption_meta_transform);
488     g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi);
489   }
490   return meta_info;
491 }
492
493 /**
494  * gst_buffer_add_video_caption_meta:
495  * @buffer: a #GstBuffer
496  * @caption_type: The type of Closed Caption to add
497  * @data: (array length=size) (transfer none): The Closed Caption data
498  * @size: The size of @data in bytes
499  *
500  * Attaches #GstVideoCaptionMeta metadata to @buffer with the given
501  * parameters.
502  *
503  * Returns: (transfer none): the #GstVideoCaptionMeta on @buffer.
504  *
505  * Since: 1.16
506  */
507 GstVideoCaptionMeta *
508 gst_buffer_add_video_caption_meta (GstBuffer * buffer,
509     GstVideoCaptionType caption_type, const guint8 * data, gsize size)
510 {
511   GstVideoCaptionMeta *meta;
512
513   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
514   g_return_val_if_fail (data != NULL, NULL);
515   g_return_val_if_fail (size > 0, NULL);
516
517   switch (caption_type) {
518     case GST_VIDEO_CAPTION_TYPE_CEA608_RAW:
519     case GST_VIDEO_CAPTION_TYPE_CEA608_IN_CEA708_RAW:
520     case GST_VIDEO_CAPTION_TYPE_CEA708_RAW:
521     case GST_VIDEO_CAPTION_TYPE_CEA708_CDP:
522       break;
523     default:
524       GST_ERROR ("Unknown caption type !");
525       return NULL;
526   }
527   /* FIXME : Add checks for content ? */
528
529   meta = (GstVideoCaptionMeta *) gst_buffer_add_meta (buffer,
530       GST_VIDEO_CAPTION_META_INFO, NULL);
531   g_return_val_if_fail (meta != NULL, NULL);
532
533   meta->caption_type = caption_type;
534   meta->data = g_memdup (data, size);
535   meta->size = size;
536
537   return meta;
538 }