1 /* GStreamer
2  *
3  * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <gst/gst.h>
25 #include <gst/check/gstcheck.h>
26 #include <gst/check/gstharness.h>
27 
28 #include <string.h>
29 
GST_START_TEST(cdp_requires_framerate)30 GST_START_TEST (cdp_requires_framerate)
31 {
32   GstHarness *h;
33   GstBuffer *buffer;
34   GstMapInfo map;
35 
36   h = gst_harness_new ("ccconverter");
37 
38   /* Enforce conversion to CDP */
39   gst_harness_set_sink_caps_str (h,
40       "closedcaption/x-cea-708,format=(string)cdp");
41 
42   /* Try without a framerate first, this has to fail */
43   gst_harness_set_src_caps_str (h,
44       "closedcaption/x-cea-708,format=(string)cc_data");
45 
46   buffer = gst_buffer_new_and_alloc (3);
47   gst_buffer_map (buffer, &map, GST_MAP_WRITE);
48   map.data[0] = 0xfc;
49   map.data[1] = 0x80;
50   map.data[2] = 0x80;
51   gst_buffer_unmap (buffer, &map);
52   fail_unless_equals_int (gst_harness_push (h, gst_buffer_ref (buffer)),
53       GST_FLOW_NOT_NEGOTIATED);
54 
55   /* Now set a framerate only on the sink caps, this should still fail:
56    * We can't do framerate conversion!
57    */
58   gst_harness_set_sink_caps_str (h,
59       "closedcaption/x-cea-708,format=(string)cdp,framerate=(fraction)30/1");
60 
61   fail_unless_equals_int (gst_harness_push (h, gst_buffer_ref (buffer)),
62       GST_FLOW_NOT_NEGOTIATED);
63 
64   /* Then try with a framerate, this should work now */
65   gst_harness_set_sink_caps_str (h,
66       "closedcaption/x-cea-708,format=(string)cdp");
67   gst_harness_set_src_caps_str (h,
68       "closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)30/1");
69 
70   fail_unless_equals_int (gst_harness_push (h, buffer), GST_FLOW_OK);
71 
72   gst_harness_teardown (h);
73 }
74 
75 GST_END_TEST;
76 
GST_START_TEST(framerate_passthrough)77 GST_START_TEST (framerate_passthrough)
78 {
79   GstHarness *h;
80   GstBuffer *buffer;
81   GstMapInfo map;
82   GstCaps *caps, *expected_caps;
83 
84   h = gst_harness_new ("ccconverter");
85 
86   gst_harness_set_src_caps_str (h,
87       "closedcaption/x-cea-608,format=(string)s334-1a,framerate=(fraction)30/1");
88 
89   gst_harness_set_sink_caps_str (h,
90       "closedcaption/x-cea-708,format=(string)cc_data");
91 
92   buffer = gst_buffer_new_and_alloc (3);
93   gst_buffer_map (buffer, &map, GST_MAP_WRITE);
94   map.data[0] = 0x00;
95   map.data[1] = 0x80;
96   map.data[2] = 0x80;
97   gst_buffer_unmap (buffer, &map);
98 
99   fail_unless_equals_int (gst_harness_push (h, gst_buffer_ref (buffer)),
100       GST_FLOW_OK);
101 
102   caps = gst_pad_get_current_caps (h->sinkpad);
103   fail_unless (caps);
104   expected_caps =
105       gst_caps_from_string
106       ("closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)30/1");
107   gst_check_caps_equal (caps, expected_caps);
108   gst_caps_unref (caps);
109   gst_caps_unref (expected_caps);
110 
111   /* Now try between the same formats, should still pass through */
112   gst_harness_set_src_caps_str (h,
113       "closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)30/1");
114 
115   gst_harness_set_sink_caps_str (h,
116       "closedcaption/x-cea-708,format=(string)cc_data");
117 
118   fail_unless_equals_int (gst_harness_push (h, gst_buffer_ref (buffer)),
119       GST_FLOW_OK);
120 
121   caps = gst_pad_get_current_caps (h->sinkpad);
122   fail_unless (caps);
123   expected_caps =
124       gst_caps_from_string
125       ("closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)30/1");
126   gst_check_caps_equal (caps, expected_caps);
127   gst_caps_unref (caps);
128   gst_caps_unref (expected_caps);
129 
130   /* And another time with the same format but only framerate on the output
131    * side. This should fail as we can't just come up with a framerate! */
132   gst_harness_set_src_caps_str (h,
133       "closedcaption/x-cea-708,format=(string)cc_data");
134 
135   gst_harness_set_sink_caps_str (h,
136       "closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)30/1");
137 
138   fail_unless_equals_int (gst_harness_push (h, buffer),
139       GST_FLOW_NOT_NEGOTIATED);
140 
141   gst_harness_teardown (h);
142 }
143 
144 GST_END_TEST;
145 
146 static void
check_conversion(const guint8 * in,guint in_len,const guint8 * out,guint out_len,const gchar * in_caps,const gchar * out_caps)147 check_conversion (const guint8 * in, guint in_len, const guint8 * out,
148     guint out_len, const gchar * in_caps, const gchar * out_caps)
149 {
150   GstHarness *h;
151   GstBuffer *buffer;
152 
153   h = gst_harness_new ("ccconverter");
154 
155   gst_harness_set_src_caps_str (h, in_caps);
156   gst_harness_set_sink_caps_str (h, out_caps);
157 
158   buffer =
159       gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, (gpointer) in,
160       in_len, 0, in_len, NULL, NULL);
161 
162   buffer = gst_harness_push_and_pull (h, buffer);
163 
164   fail_unless (buffer != NULL);
165   gst_check_buffer_data (buffer, out, out_len);
166   gst_buffer_unref (buffer);
167 
168   gst_harness_teardown (h);
169 }
170 
GST_START_TEST(convert_cea608_raw_cea608_s334_1a)171 GST_START_TEST (convert_cea608_raw_cea608_s334_1a)
172 {
173   const guint8 in[] = { 0x80, 0x80 };
174   const guint8 out[] = { 0x80, 0x80, 0x80 };
175   check_conversion (in, sizeof (in), out, sizeof (out),
176       "closedcaption/x-cea-608,format=(string)raw",
177       "closedcaption/x-cea-608,format=(string)s334-1a");
178 }
179 
180 GST_END_TEST;
181 
GST_START_TEST(convert_cea608_raw_cea708_cc_data)182 GST_START_TEST (convert_cea608_raw_cea708_cc_data)
183 {
184   const guint8 in[] = { 0x80, 0x80 };
185   const guint8 out[] = { 0xfc, 0x80, 0x80 };
186   check_conversion (in, sizeof (in), out, sizeof (out),
187       "closedcaption/x-cea-608,format=(string)raw",
188       "closedcaption/x-cea-708,format=(string)cc_data");
189 }
190 
191 GST_END_TEST;
192 
GST_START_TEST(convert_cea608_raw_cea708_cdp)193 GST_START_TEST (convert_cea608_raw_cea708_cdp)
194 {
195   const guint8 in[] = { 0x80, 0x80 };
196   const guint8 out[] =
197       { 0x96, 0x69, 0x2b, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xea, 0xfc, 0x80, 0x80,
198     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200     0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x38
201   };
202   check_conversion (in, sizeof (in), out, sizeof (out),
203       "closedcaption/x-cea-608,format=(string)raw,framerate=(fraction)60/1",
204       "closedcaption/x-cea-708,format=(string)cdp");
205 }
206 
207 GST_END_TEST;
208 
GST_START_TEST(convert_cea608_s334_1a_cea608_raw)209 GST_START_TEST (convert_cea608_s334_1a_cea608_raw)
210 {
211   const guint8 in[] = { 0x80, 0x80, 0x80, 0x00, 0x80, 0x80 };
212   const guint8 out[] = { 0x80, 0x80 };
213   check_conversion (in, sizeof (in), out, sizeof (out),
214       "closedcaption/x-cea-608,format=(string)s334-1a",
215       "closedcaption/x-cea-608,format=(string)raw");
216 }
217 
218 GST_END_TEST;
219 
GST_START_TEST(convert_cea608_s334_1a_cea708_cc_data)220 GST_START_TEST (convert_cea608_s334_1a_cea708_cc_data)
221 {
222   const guint8 in[] = { 0x80, 0x80, 0x80, 0x00, 0x80, 0x80 };
223   const guint8 out[] = { 0xfc, 0x80, 0x80, 0xfd, 0x80, 0x80 };
224   check_conversion (in, sizeof (in), out, sizeof (out),
225       "closedcaption/x-cea-608,format=(string)s334-1a",
226       "closedcaption/x-cea-708,format=(string)cc_data");
227 }
228 
229 GST_END_TEST;
230 
GST_START_TEST(convert_cea608_s334_1a_cea708_cdp)231 GST_START_TEST (convert_cea608_s334_1a_cea708_cdp)
232 {
233   const guint8 in[] = { 0x80, 0x80, 0x80, 0x00, 0x80, 0x80 };
234   const guint8 out[] =
235       { 0x96, 0x69, 0x2b, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xea, 0xfc, 0x80, 0x80,
236     0xfd, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238     0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x3b
239   };
240   check_conversion (in, sizeof (in), out, sizeof (out),
241       "closedcaption/x-cea-608,format=(string)s334-1a,framerate=(fraction)60/1",
242       "closedcaption/x-cea-708,format=(string)cdp");
243 }
244 
245 GST_END_TEST;
246 
GST_START_TEST(convert_cea708_cc_data_cea608_raw)247 GST_START_TEST (convert_cea708_cc_data_cea608_raw)
248 {
249   const guint8 in[] = { 0xfc, 0x80, 0x80, 0xfe, 0x80, 0x80 };
250   const guint8 out[] = { 0x80, 0x80 };
251   check_conversion (in, sizeof (in), out, sizeof (out),
252       "closedcaption/x-cea-708,format=(string)cc_data",
253       "closedcaption/x-cea-608,format=(string)raw");
254 }
255 
256 GST_END_TEST;
257 
GST_START_TEST(convert_cea708_cc_data_cea608_s334_1a)258 GST_START_TEST (convert_cea708_cc_data_cea608_s334_1a)
259 {
260   const guint8 in[] = { 0xfc, 0x80, 0x80, 0xfe, 0x80, 0x80 };
261   const guint8 out[] = { 0x80, 0x80, 0x80 };
262   check_conversion (in, sizeof (in), out, sizeof (out),
263       "closedcaption/x-cea-708,format=(string)cc_data",
264       "closedcaption/x-cea-608,format=(string)s334-1a");
265 }
266 
267 GST_END_TEST;
268 
GST_START_TEST(convert_cea708_cc_data_cea708_cdp)269 GST_START_TEST (convert_cea708_cc_data_cea708_cdp)
270 {
271   const guint8 in[] = { 0xfc, 0x80, 0x80, 0xfe, 0x80, 0x80 };
272   const guint8 out[] =
273       { 0x96, 0x69, 0x2b, 0x8f, 0x43, 0x00, 0x00, 0x72, 0xea, 0xfc, 0x80, 0x80,
274     0xfe, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276     0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x3a
277   };
278   check_conversion (in, sizeof (in), out, sizeof (out),
279       "closedcaption/x-cea-708,format=(string)cc_data,framerate=(fraction)60/1",
280       "closedcaption/x-cea-708,format=(string)cdp");
281 }
282 
283 GST_END_TEST;
284 
GST_START_TEST(convert_cea708_cdp_cea608_raw)285 GST_START_TEST (convert_cea708_cdp_cea608_raw)
286 {
287   const guint8 in[] =
288       { 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80,
289     0xfe, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a
290   };
291   const guint8 out[] = { 0x80, 0x80 };
292   check_conversion (in, sizeof (in), out, sizeof (out),
293       "closedcaption/x-cea-708,format=(string)cdp",
294       "closedcaption/x-cea-608,format=(string)raw");
295 }
296 
297 GST_END_TEST;
298 
GST_START_TEST(convert_cea708_cdp_cea608_s334_1a)299 GST_START_TEST (convert_cea708_cdp_cea608_s334_1a)
300 {
301   const guint8 in[] =
302       { 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80,
303     0xfe, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a
304   };
305   const guint8 out[] = { 0x80, 0x80, 0x80 };
306   check_conversion (in, sizeof (in), out, sizeof (out),
307       "closedcaption/x-cea-708,format=(string)cdp",
308       "closedcaption/x-cea-608,format=(string)s334-1a");
309 }
310 
311 GST_END_TEST;
312 
GST_START_TEST(convert_cea708_cdp_cea708_cc_data)313 GST_START_TEST (convert_cea708_cdp_cea708_cc_data)
314 {
315   const guint8 in[] =
316       { 0x96, 0x69, 0x13, 0x5f, 0x43, 0x00, 0x00, 0x72, 0xe2, 0xfc, 0x80, 0x80,
317     0xfe, 0x80, 0x80, 0x74, 0x00, 0x00, 0x8a
318   };
319   const guint8 out[] = { 0xfc, 0x80, 0x80, 0xfe, 0x80, 0x80 };
320   check_conversion (in, sizeof (in), out, sizeof (out),
321       "closedcaption/x-cea-708,format=(string)cdp",
322       "closedcaption/x-cea-708,format=(string)cc_data");
323 }
324 
325 GST_END_TEST;
326 
327 static Suite *
ccextractor_suite(void)328 ccextractor_suite (void)
329 {
330   Suite *s = suite_create ("ccconverter");
331   TCase *tc = tcase_create ("general");
332 
333   suite_add_tcase (s, tc);
334 
335   tcase_add_test (tc, cdp_requires_framerate);
336   tcase_add_test (tc, framerate_passthrough);
337   tcase_add_test (tc, convert_cea608_raw_cea608_s334_1a);
338   tcase_add_test (tc, convert_cea608_raw_cea708_cc_data);
339   tcase_add_test (tc, convert_cea608_raw_cea708_cdp);
340   tcase_add_test (tc, convert_cea608_s334_1a_cea608_raw);
341   tcase_add_test (tc, convert_cea608_s334_1a_cea708_cc_data);
342   tcase_add_test (tc, convert_cea608_s334_1a_cea708_cdp);
343   tcase_add_test (tc, convert_cea708_cc_data_cea608_raw);
344   tcase_add_test (tc, convert_cea708_cc_data_cea608_s334_1a);
345   tcase_add_test (tc, convert_cea708_cc_data_cea708_cdp);
346   tcase_add_test (tc, convert_cea708_cdp_cea608_raw);
347   tcase_add_test (tc, convert_cea708_cdp_cea608_s334_1a);
348   tcase_add_test (tc, convert_cea708_cdp_cea708_cc_data);
349 
350   return s;
351 }
352 
353 GST_CHECK_MAIN (ccextractor);
354