• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2008 Thijs Vermeir <thijsvermeir@gmail.com>
3  * Copyright (C) 2011 David Schleef <ds@schleef.org>
4  * Copyright (C) 2021 Jan Schmidt <jan@centricular.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "qtdemux-webvtt.h"
27 #include <gst/base/gstbytereader.h>
28 
29 #include "fourcc.h"
30 #include "qtdemux.h"
31 #include "qtatomparser.h"
32 
33 #include <stdlib.h>
34 #include <string.h>
35 
36 GST_DEBUG_CATEGORY_EXTERN (qtdemux_debug);
37 #define GST_CAT_DEFAULT qtdemux_debug
38 
39 gboolean
qtdemux_webvtt_is_empty(GstQTDemux * demux,guint8 * data,gsize size)40 qtdemux_webvtt_is_empty (GstQTDemux * demux, guint8 * data, gsize size)
41 {
42   GstByteReader br;
43   guint32 atom_size;
44   guint32 atom_type;
45 
46   gst_byte_reader_init (&br, data, size);
47   if (gst_byte_reader_get_remaining (&br) < 8)
48     return FALSE;
49 
50   if (!gst_byte_reader_get_uint32_be (&br, &atom_size) ||
51       !qt_atom_parser_get_fourcc (&br, &atom_type))
52     return FALSE;
53 
54   if (atom_type == FOURCC_vtte)
55     return TRUE;
56 
57   return FALSE;
58 }
59 
60 struct WebvttCue
61 {
62   const guint8 *cue_id;
63   guint32 cue_id_len;
64 
65   const guint8 *cue_time;
66   guint32 cue_time_len;
67 
68   const guint8 *settings;
69   guint32 settings_len;
70 
71   const guint8 *cue_text;
72   guint32 cue_text_len;
73 };
74 
75 static void
webvtt_append_timestamp_to_string(GstClockTime timestamp,GString * str)76 webvtt_append_timestamp_to_string (GstClockTime timestamp, GString * str)
77 {
78   guint h, m, s, ms;
79 
80   h = timestamp / (3600 * GST_SECOND);
81 
82   timestamp -= h * 3600 * GST_SECOND;
83   m = timestamp / (60 * GST_SECOND);
84 
85   timestamp -= m * 60 * GST_SECOND;
86   s = timestamp / GST_SECOND;
87 
88   timestamp -= s * GST_SECOND;
89   ms = timestamp / GST_MSECOND;
90 
91   g_string_append_printf (str, "%02d:%02d:%02d.%03d", h, m, s, ms);
92 }
93 
94 static gboolean
webvtt_decode_vttc(GstQTDemux * qtdemux,GstByteReader * br,GstClockTime start,GstClockTime duration,GString * s)95 webvtt_decode_vttc (GstQTDemux * qtdemux, GstByteReader * br,
96     GstClockTime start, GstClockTime duration, GString * s)
97 {
98   struct WebvttCue cue = { 0, };
99   gboolean have_data = FALSE;
100 
101   while (gst_byte_reader_get_remaining (br) >= 8) {
102     guint32 atom_size;
103     guint32 atom_type;
104     guint next_pos;
105 
106     if (!gst_byte_reader_get_uint32_be (br, &atom_size) ||
107         !qt_atom_parser_get_fourcc (br, &atom_type))
108       break;
109 
110     if (gst_byte_reader_get_remaining (br) < atom_size - 8)
111       break;
112     next_pos = gst_byte_reader_get_pos (br) - 8 + atom_size;
113 
114     GST_LOG_OBJECT (qtdemux, "WebVTT cue atom %" GST_FOURCC_FORMAT " len %u",
115         GST_FOURCC_ARGS (atom_type), atom_size);
116 
117     switch (atom_type) {
118       case FOURCC_ctim:
119         if (!gst_byte_reader_get_data (br, atom_size - 8, &cue.cue_time))
120           return FALSE;
121         cue.cue_time_len = atom_size - 8;
122         break;
123       case FOURCC_iden:
124         if (!gst_byte_reader_get_data (br, atom_size - 8, &cue.cue_id))
125           return FALSE;
126         cue.cue_id_len = atom_size - 8;
127         break;
128       case FOURCC_sttg:
129         if (!gst_byte_reader_get_data (br, atom_size - 8, &cue.settings))
130           return FALSE;
131         cue.settings_len = atom_size - 8;
132         break;
133       case FOURCC_payl:
134         if (!gst_byte_reader_get_data (br, atom_size - 8, &cue.cue_text))
135           return FALSE;
136         cue.cue_text_len = atom_size - 8;
137         have_data = TRUE;
138         break;
139     }
140 
141     if (!gst_byte_reader_set_pos (br, next_pos))
142       break;
143   }
144 
145   if (have_data) {
146     if (cue.cue_id)
147       g_string_append_printf (s, "%.*s\n", cue.cue_id_len, cue.cue_id);
148 
149     /* Write the cue time and optional settings */
150     webvtt_append_timestamp_to_string (start, s);
151     g_string_append_printf (s, " --> ");
152     webvtt_append_timestamp_to_string (start + duration, s);
153 
154     if (cue.settings)
155       g_string_append_printf (s, " %.*s\n", cue.settings_len, cue.settings);
156     else
157       g_string_append (s, "\n");
158 
159     g_string_append_printf (s, "%.*s\n\n", cue.cue_text_len, cue.cue_text);
160   }
161 
162   return have_data;
163 }
164 
165 GstBuffer *
qtdemux_webvtt_decode(GstQTDemux * qtdemux,GstClockTime start,GstClockTime duration,guint8 * data,gsize size)166 qtdemux_webvtt_decode (GstQTDemux * qtdemux, GstClockTime start,
167     GstClockTime duration, guint8 * data, gsize size)
168 {
169   GstByteReader br;
170   GString *str = NULL;
171   GstBuffer *buf = NULL;
172 
173   gst_byte_reader_init (&br, data, size);
174   while (gst_byte_reader_get_remaining (&br) >= 8) {
175     guint32 atom_size;
176     guint32 atom_type;
177     guint next_pos;
178 
179     if (!gst_byte_reader_get_uint32_be (&br, &atom_size) ||
180         !qt_atom_parser_get_fourcc (&br, &atom_type))
181       break;
182 
183     if (gst_byte_reader_get_remaining (&br) < atom_size - 8)
184       break;
185     next_pos = gst_byte_reader_get_pos (&br) - 8 + atom_size;
186 
187     switch (atom_type) {
188       case FOURCC_vttc:
189         GST_LOG_OBJECT (qtdemux,
190             "WebVTT cue atom %" GST_FOURCC_FORMAT " len %u",
191             GST_FOURCC_ARGS (atom_type), atom_size);
192         if (str == NULL)
193           str = g_string_new (NULL);
194         if (!webvtt_decode_vttc (qtdemux, &br, start, duration, str))
195           break;
196         break;
197       case FOURCC_vtte:
198         /* The empty segment case should be handled separately using qtdemux_webvtt_is_empty().
199          * Ignore it during decode */
200         break;
201       case FOURCC_vtta:
202         /* extra attributes */
203         break;
204       default:
205         GST_DEBUG_OBJECT (qtdemux,
206             "Unknown WebVTT sample atom %" GST_FOURCC_FORMAT,
207             GST_FOURCC_ARGS (atom_type));
208         break;
209     }
210     if (!gst_byte_reader_set_pos (&br, next_pos))
211       break;
212   }
213 
214   if (str) {
215     gsize webvtt_len = str->len;
216     gchar *webvtt_chunk = g_string_free (str, FALSE);
217     buf = gst_buffer_new_wrapped (webvtt_chunk, webvtt_len);
218   }
219 
220   return buf;
221 }
222