Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / XIPH Theora decoder filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 :
27 : #include <gpac/filters.h>
28 : #include <gpac/constants.h>
29 : #include <gpac/bitstream.h>
30 :
31 : #ifdef GPAC_HAS_THEORA
32 :
33 : #include <theora/theora.h>
34 :
35 : #if !defined(__GNUC__)
36 : # if defined(_WIN32_WCE) || defined (WIN32)
37 : # pragma comment(lib, "libtheora_static")
38 : # endif
39 : #endif
40 :
41 : typedef struct
42 : {
43 : GF_FilterPid *ipid, *opid;
44 : theora_info ti, the_ti;
45 : theora_state td;
46 : theora_comment tc;
47 : ogg_packet op;
48 :
49 : u32 cfg_crc;
50 :
51 : GF_List *src_packets;
52 : u64 next_cts;
53 : Bool has_reconfigured;
54 : } GF_TheoraDecCtx;
55 :
56 1 : static GF_Err theoradec_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
57 : {
58 : const GF_PropertyValue *p;
59 : ogg_packet oggpacket;
60 : GF_BitStream *bs;
61 1 : GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
62 :
63 :
64 1 : if (is_remove) {
65 0 : if (ctx->opid) {
66 0 : gf_filter_pid_remove(ctx->opid);
67 0 : ctx->opid = NULL;
68 : }
69 0 : ctx->ipid = NULL;
70 0 : return GF_OK;
71 : }
72 1 : if (! gf_filter_pid_check_caps(pid))
73 : return GF_NOT_SUPPORTED;
74 :
75 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
76 1 : if (p && p->value.data.ptr && p->value.data.size) {
77 : u32 ex_crc;
78 1 : if (strncmp(&p->value.data.ptr[3], "theora", 6)) return GF_NON_COMPLIANT_BITSTREAM;
79 1 : ex_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
80 1 : if (ctx->cfg_crc == ex_crc) return GF_OK;
81 1 : ctx->cfg_crc = ex_crc;
82 : } else {
83 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[Theora] Reconfiguring without DSI not yet supported\n"));
84 : return GF_NOT_SUPPORTED;
85 : }
86 1 : if (!ctx->opid) {
87 1 : ctx->opid = gf_filter_pid_new(filter);
88 : }
89 : //copy properties at init or reconfig
90 1 : gf_filter_pid_copy_properties(ctx->opid, pid);
91 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
92 :
93 1 : if (ctx->ipid) {
94 0 : theora_clear(&ctx->td);
95 0 : theora_info_clear(&ctx->ti);
96 0 : theora_comment_clear(&ctx->tc);
97 : }
98 1 : ctx->ipid = pid;
99 1 : gf_filter_pid_set_framing_mode(ctx->ipid, GF_TRUE);
100 :
101 1 : oggpacket.granulepos = -1;
102 1 : oggpacket.b_o_s = 1;
103 1 : oggpacket.e_o_s = 0;
104 1 : oggpacket.packetno = 0;
105 :
106 1 : theora_info_init(&ctx->ti);
107 1 : theora_comment_init(&ctx->tc);
108 :
109 1 : bs = gf_bs_new(p->value.data.ptr, p->value.data.size, GF_BITSTREAM_READ);
110 1 : while (gf_bs_available(bs)) {
111 : GF_Err e = GF_OK;
112 3 : oggpacket.bytes = gf_bs_read_u16(bs);
113 3 : oggpacket.packet = gf_malloc(sizeof(char) * oggpacket.bytes);
114 3 : gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes);
115 3 : if (theora_decode_header(&ctx->ti, &ctx->tc, &oggpacket) < 0 ) {
116 : e = GF_NON_COMPLIANT_BITSTREAM;
117 : }
118 3 : gf_free(oggpacket.packet);
119 3 : if (e) {
120 0 : gf_bs_del(bs);
121 0 : return e;
122 : }
123 : }
124 1 : theora_decode_init(&ctx->td, &ctx->ti);
125 1 : gf_bs_del(bs);
126 :
127 1 : return GF_OK;
128 : }
129 :
130 13 : static GF_Err theoradec_process(GF_Filter *filter)
131 : {
132 : ogg_packet op;
133 : yuv_buffer yuv;
134 : u32 i, count;
135 : u8 *buffer;
136 : u8 *pYO, *pUO, *pVO;
137 : u8 *pYD, *pUD, *pVD;
138 13 : GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
139 : GF_FilterPacket *pck, *dst_pck, *src_pck, *pck_ref;
140 : Bool is_seek;
141 :
142 13 : pck = gf_filter_pid_get_packet(ctx->ipid);
143 13 : if (!pck && !gf_filter_pid_is_eos(ctx->ipid))
144 : return GF_OK;
145 :
146 : memset(&yuv, 0, sizeof(yuv_buffer));
147 : memset(&op, 0, sizeof(ogg_packet));
148 13 : op.granulepos = -1;
149 :
150 13 : if (pck) {
151 12 : u64 cts = gf_filter_pck_get_cts(pck);
152 : u32 psize;
153 12 : op.packet = (char *) gf_filter_pck_get_data(pck, &psize);
154 12 : op.bytes = psize;
155 :
156 :
157 : //append in cts order since we get output frames in cts order
158 12 : pck_ref = pck;
159 12 : gf_filter_pck_ref_props(&pck_ref);
160 12 : count = gf_list_count(ctx->src_packets);
161 : src_pck = NULL;
162 12 : for (i=0; i<count; i++) {
163 : u64 acts;
164 0 : src_pck = gf_list_get(ctx->src_packets, i);
165 0 : acts = gf_filter_pck_get_cts(src_pck);
166 0 : if (acts==cts) {
167 0 : gf_filter_pck_unref(pck_ref);
168 0 : break;
169 : }
170 0 : if (acts>cts) {
171 0 : gf_list_insert(ctx->src_packets, pck_ref, i);
172 0 : break;
173 : }
174 : src_pck = NULL;
175 : }
176 12 : if (!src_pck)
177 12 : gf_list_add(ctx->src_packets, pck_ref);
178 :
179 : } else {
180 : //weird bug in theora dec: even though we feed empty packets, we still keep getting frames !
181 1 : if (!gf_list_count(ctx->src_packets)) {
182 1 : gf_filter_pid_set_eos(ctx->opid);
183 1 : return GF_EOS;
184 : }
185 0 : op.packet = NULL;
186 0 : op.bytes = 0;
187 : }
188 :
189 12 : if (theora_decode_packetin(&ctx->td, &op) != 0) {
190 0 : src_pck = gf_list_pop_front(ctx->src_packets);
191 0 : gf_filter_pck_unref(src_pck);
192 0 : if (pck) {
193 0 : gf_filter_pid_drop_packet(ctx->ipid);
194 0 : return GF_NON_COMPLIANT_BITSTREAM;
195 : }
196 0 : gf_filter_pid_set_eos(ctx->opid);
197 0 : return GF_EOS;
198 : }
199 : //no frame
200 12 : if (theora_decode_YUVout(&ctx->td, &yuv) != 0) {
201 0 : if (pck) {
202 0 : gf_filter_pid_drop_packet(ctx->ipid);
203 0 : return GF_OK;
204 : }
205 0 : gf_filter_pid_set_eos(ctx->opid);
206 0 : return GF_EOS;
207 : }
208 :
209 12 : if (memcmp(&ctx->ti, &ctx->the_ti, sizeof(theora_info))) {
210 : memcpy(&ctx->the_ti, &ctx->ti, sizeof(theora_info));
211 :
212 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(ctx->ti.width));
213 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(ctx->ti.height));
214 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, &PROP_UINT(ctx->ti.width));
215 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FPS, &PROP_FRAC_INT(ctx->ti.fps_numerator, ctx->ti.fps_denominator) );
216 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, &PROP_UINT(GF_PIXEL_YUV) );
217 : }
218 :
219 12 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->ti.width*ctx->ti.height * 3 / 2, &buffer);
220 12 : if (!dst_pck) return GF_OUT_OF_MEM;
221 :
222 12 : pYO = yuv.y;
223 12 : pUO = yuv.u;
224 12 : pVO = yuv.v;
225 12 : pYD = buffer;
226 12 : pUD = buffer + ctx->ti.width * ctx->ti.height;
227 12 : pVD = buffer + 5 * ctx->ti.width * ctx->ti.height / 4;
228 :
229 2892 : for (i=0; i<(u32)yuv.y_height; i++) {
230 2880 : memcpy(pYD, pYO, sizeof(char) * yuv.y_width);
231 2880 : pYD += ctx->ti.width;
232 2880 : pYO += yuv.y_stride;
233 2880 : if (i%2) continue;
234 :
235 1440 : memcpy(pUD, pUO, sizeof(char) * yuv.uv_width);
236 1440 : memcpy(pVD, pVO, sizeof(char) * yuv.uv_width);
237 1440 : pUD += ctx->ti.width/2;
238 1440 : pVD += ctx->ti.width/2;
239 1440 : pUO += yuv.uv_stride;
240 1440 : pVO += yuv.uv_stride;
241 : }
242 :
243 12 : src_pck = gf_list_pop_front(ctx->src_packets);
244 12 : if (src_pck) {
245 12 : gf_filter_pck_merge_properties(src_pck, dst_pck);
246 12 : is_seek = gf_filter_pck_get_seek_flag(src_pck);
247 12 : ctx->next_cts = gf_filter_pck_get_cts(src_pck);
248 12 : ctx->next_cts += gf_filter_pck_get_duration(src_pck);
249 12 : gf_filter_pck_unref(src_pck);
250 : } else {
251 : is_seek = 0;
252 0 : gf_filter_pck_set_cts(dst_pck, ctx->next_cts);
253 : }
254 :
255 12 : if (!pck || !is_seek )
256 12 : gf_filter_pck_send(dst_pck);
257 : else
258 0 : gf_filter_pck_discard(dst_pck);
259 :
260 12 : if (pck) gf_filter_pid_drop_packet(ctx->ipid);
261 : return GF_OK;
262 : }
263 :
264 1 : static GF_Err theoradec_initialize(GF_Filter *filter)
265 : {
266 1 : GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
267 1 : ctx->src_packets = gf_list_new();
268 1 : return GF_OK;
269 : }
270 1 : static void theoradec_finalize(GF_Filter *filter)
271 : {
272 1 : GF_TheoraDecCtx *ctx = gf_filter_get_udta(filter);
273 :
274 1 : theora_clear(&ctx->td);
275 1 : theora_info_clear(&ctx->ti);
276 1 : theora_comment_clear(&ctx->tc);
277 :
278 2 : while (gf_list_count(ctx->src_packets)) {
279 0 : GF_FilterPacket *pck = gf_list_pop_back(ctx->src_packets);
280 0 : gf_filter_pck_unref(pck);
281 : }
282 1 : gf_list_del(ctx->src_packets);
283 1 : }
284 :
285 : static const GF_FilterCapability TheoraDecCaps[] =
286 : {
287 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
288 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
289 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_THEORA),
290 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
291 : };
292 :
293 : GF_FilterRegister TheoraDecRegister = {
294 : .name = "theoradec",
295 : GF_FS_SET_DESCRIPTION("Theora decoder")
296 : GF_FS_SET_HELP("This filter decodes Theora streams through libtheora library.")
297 : .private_size = sizeof(GF_TheoraDecCtx),
298 : .priority = 1,
299 : SETCAPS(TheoraDecCaps),
300 : .initialize = theoradec_initialize,
301 : .finalize = theoradec_finalize,
302 : .configure_pid = theoradec_configure_pid,
303 : .process = theoradec_process,
304 : };
305 :
306 : #endif
307 :
308 2877 : const GF_FilterRegister *theoradec_register(GF_FilterSession *session)
309 : {
310 : #ifdef GPAC_HAS_THEORA
311 2877 : return &TheoraDecRegister;
312 : #else
313 : return NULL;
314 : #endif
315 : }
316 :
|