Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2019
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / AVI output 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 :
30 : #ifndef GPAC_DISABLE_AVILIB
31 :
32 : #include <gpac/internal/avilib.h>
33 :
34 : typedef struct
35 : {
36 : u32 width, height, pf, stride;
37 : GF_Fraction fps;
38 : u32 sr, bps, nb_ch, wfmt, br;
39 : u32 codec_id;
40 : u32 format;
41 : u32 timescale;
42 : Bool is_video, is_raw_vid;
43 : u32 is_open;
44 : u32 tk_idx;
45 : GF_FilterPid *pid;
46 : Bool suspended;
47 : } AVIStream;
48 :
49 : typedef struct
50 : {
51 : //options
52 : char *dst;
53 : GF_Fraction fps;
54 : Bool noraw;
55 : u64 opendml_size;
56 :
57 :
58 : avi_t *avi_out;
59 : u32 nb_write;
60 : GF_List *streams;
61 : Bool has_video;
62 : char comp_name[5];
63 :
64 : u64 last_video_time_ms;
65 : Bool video_is_eos;
66 : char *buf_tmp;
67 : u32 buf_alloc;
68 : Bool in_error;
69 :
70 : u32 cur_file_idx_plus_one;
71 : } GF_AVIMuxCtx;
72 :
73 9 : static GF_Err avimux_open_close(GF_AVIMuxCtx *ctx, const char *filename, const char *ext, u32 file_idx)
74 : {
75 : Bool had_file = GF_FALSE;
76 9 : if (ctx->avi_out) {
77 : had_file = GF_TRUE;
78 3 : AVI_close(ctx->avi_out);
79 : }
80 9 : ctx->avi_out = NULL;
81 9 : if (!filename) return GF_OK;
82 :
83 3 : if (!strcmp(filename, "std") || !strcmp(filename, "stdout")) {
84 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] Writing to stdout not yet implemented\n"));
85 : return GF_NOT_SUPPORTED;
86 : }
87 :
88 3 : ctx->avi_out = AVI_open_output_file((char *) filename, ctx->opendml_size);
89 :
90 3 : if (had_file && ctx->nb_write) {
91 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[AVIOut] re-opening in write mode output file %s, content overwrite\n", filename));
92 : }
93 3 : ctx->nb_write = 0;
94 3 : if (!ctx->avi_out) {
95 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] cannot open output file %s\n", filename));
96 0 : ctx->in_error = GF_TRUE;
97 : return GF_IO_ERR;
98 : }
99 : return GF_OK;
100 : }
101 :
102 1021 : static GF_Err avimux_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
103 : {
104 : AVIStream *stream;
105 : GF_FilterEvent evt;
106 : const GF_PropertyValue *p;
107 : u32 w, h, sr, stride, bps, nb_ch, pf, codec_id, type, br, timescale, wfmt;
108 : GF_Fraction fps;
109 1021 : GF_AVIMuxCtx *ctx = (GF_AVIMuxCtx *) gf_filter_get_udta(filter);
110 :
111 1021 : stream = gf_filter_pid_get_udta(pid);
112 :
113 1021 : if (is_remove) {
114 0 : if (!stream) return GF_SERVICE_ERROR;
115 0 : gf_list_del_item(ctx->streams, stream);
116 0 : gf_free(stream);
117 :
118 0 : if (!gf_list_count(ctx->streams))
119 0 : avimux_open_close(ctx, NULL, NULL, 0);
120 : return GF_OK;
121 : }
122 1021 : gf_filter_pid_check_caps(pid);
123 :
124 : w = h = sr = nb_ch = pf = codec_id = type = timescale = wfmt = stride = 0;
125 : bps = 16;
126 : br=128000;
127 : fps.den = fps.num = 0;
128 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
129 1021 : if (p) type = p->value.uint;
130 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
131 1021 : if (p) codec_id = p->value.uint;
132 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
133 1021 : if (p) timescale = p->value.uint;
134 :
135 1021 : if (!codec_id) return GF_NOT_SUPPORTED;
136 1021 : if ((type!=GF_STREAM_VISUAL) && (type!=GF_STREAM_AUDIO)) return GF_NOT_SUPPORTED;
137 :
138 1021 : if (!stream && (type==GF_STREAM_VISUAL) && ctx->has_video) {
139 : return GF_REQUIRES_NEW_INSTANCE;
140 : }
141 :
142 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
143 1021 : if (p) w = p->value.uint;
144 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
145 1021 : if (p) stride = p->value.uint;
146 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
147 1021 : if (p) h = p->value.uint;
148 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
149 1021 : if (p) fps = p->value.frac;
150 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
151 1021 : if (p) pf = p->value.uint;
152 :
153 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
154 1021 : if (p) sr = p->value.uint;
155 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
156 1021 : if (p) bps = p->value.uint;
157 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
158 1021 : if (p) nb_ch = p->value.uint;
159 1021 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_BITRATE);
160 1021 : if (p) br = p->value.uint;
161 :
162 1021 : switch (codec_id) {
163 2 : case GF_CODECID_MPEG4_PART2:
164 2 : strcpy(ctx->comp_name, "XVID");
165 : break;
166 0 : case GF_CODECID_AVC:
167 : case GF_CODECID_SVC:
168 : case GF_CODECID_MVC:
169 0 : strcpy(ctx->comp_name, "h264");
170 : break;
171 : // case GF_CODECID_HEVC:
172 : // case GF_CODECID_LHVC:
173 : // strcpy(szComp, "hevc");
174 : // break;
175 : case GF_CODECID_MPEG_AUDIO:
176 : case GF_CODECID_MPEG2_PART3:
177 : wfmt = 0x55;
178 : break;
179 0 : case GF_CODECID_AAC_MPEG4:
180 : case GF_CODECID_AAC_MPEG2_MP:
181 : case GF_CODECID_AAC_MPEG2_LCP:
182 : case GF_CODECID_AAC_MPEG2_SSRP:
183 : wfmt = 0x0000706d;
184 0 : break;
185 255 : case GF_CODECID_RAW:
186 :
187 255 : if (type==GF_STREAM_VISUAL) {
188 2 : if (pf != GF_PIXEL_BGR) {
189 1 : gf_filter_pid_negociate_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT(GF_PIXEL_BGR));
190 1 : return GF_OK;
191 : }
192 253 : } else if (type==GF_STREAM_AUDIO) {
193 253 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
194 253 : if (!p || (p->value.uint != GF_AUDIO_FMT_S16) ) {
195 0 : gf_filter_pid_negociate_property(pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S16));
196 0 : return GF_OK;
197 : }
198 : wfmt = WAVE_FORMAT_PCM;
199 : }
200 : break;
201 0 : default:
202 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] Unsupported codec %s for AVI error\n", gf_codecid_name(codec_id) ));
203 : return GF_NOT_SUPPORTED;
204 : }
205 :
206 1020 : if (!stream) {
207 6 : GF_SAFEALLOC(stream, AVIStream);
208 6 : if (!stream) return GF_OUT_OF_MEM;
209 6 : stream->pid = pid;
210 6 : gf_filter_pid_set_udta(pid, stream);
211 6 : gf_list_add(ctx->streams, stream);
212 6 : stream->codec_id = codec_id;
213 6 : stream->timescale = timescale;
214 6 : if (type==GF_STREAM_VISUAL) {
215 3 : ctx->has_video = GF_TRUE;
216 3 : stream->width = w;
217 3 : stream->height = h;
218 3 : stream->pf = pf;
219 3 : stream->fps = fps;
220 3 : stream->is_video = GF_TRUE;
221 : } else {
222 3 : stream->sr = sr;
223 3 : stream->bps = bps;
224 3 : stream->nb_ch = nb_ch;
225 3 : stream->wfmt = wfmt;
226 3 : stream->br = br;
227 : }
228 6 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
229 6 : gf_filter_pid_send_event(pid, &evt);
230 :
231 6 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
232 : } else {
233 1014 : if (!stream->is_open) {
234 0 : stream->codec_id = codec_id;
235 0 : stream->timescale = timescale;
236 0 : stream->width = w;
237 0 : stream->height = h;
238 0 : stream->pf = pf;
239 0 : stream->fps = fps;
240 0 : stream->sr = sr;
241 0 : stream->bps = bps;
242 0 : stream->nb_ch = nb_ch;
243 0 : stream->wfmt = wfmt;
244 0 : stream->br = br;
245 0 : stream->stride = stride;
246 0 : stream->is_raw_vid = (codec_id==GF_CODECID_RAW) ? GF_TRUE : GF_FALSE;
247 0 : goto check_mx;
248 : }
249 1014 : if ((type==GF_STREAM_VISUAL) && (stream->width==w) && (stream->height==h) && (stream->format==pf) && (stream->codec_id==codec_id) && (stream->timescale==timescale) && (stream->fps.num*fps.den == stream->fps.den*fps.num))
250 : {
251 : goto check_mx;
252 : }
253 1014 : else if ((type==GF_STREAM_AUDIO) && (stream->sr==sr) && (stream->nb_ch==nb_ch) && (stream->bps==bps) && (stream->codec_id==codec_id) && (stream->timescale==timescale) )
254 : {
255 : goto check_mx;
256 : }
257 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] Stream configuration changed for codec %s, not supported in AVI\n", gf_codecid_name(codec_id) ));
258 :
259 : return GF_NOT_SUPPORTED;
260 : }
261 :
262 1014 : check_mx:
263 1020 : if (!ctx->avi_out) {
264 : char szPath[GF_MAX_PATH];
265 : const char *cur_file_suffix=NULL;
266 1014 : GF_FilterPacket *pck = gf_filter_pid_get_packet(pid);
267 1014 : if (!pck) {
268 2028 : if (gf_filter_pid_is_eos(pid)) return GF_EOS;
269 0 : return GF_OK;
270 : }
271 0 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
272 0 : ctx->cur_file_idx_plus_one = p ? (p->value.uint + 1) : 1;
273 0 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
274 0 : if (p && p->value.string) cur_file_suffix = p->value.string;
275 :
276 0 : gf_filter_pid_resolve_file_template(pid, ctx->dst, szPath, ctx->cur_file_idx_plus_one-1, cur_file_suffix);
277 0 : avimux_open_close(ctx, szPath, NULL, 0);
278 : }
279 :
280 : return GF_OK;
281 : }
282 :
283 3 : static void avimux_finalize(GF_Filter *filter)
284 : {
285 3 : GF_AVIMuxCtx *ctx = (GF_AVIMuxCtx *) gf_filter_get_udta(filter);
286 3 : avimux_open_close(ctx, NULL, NULL, 0);
287 12 : while (gf_list_count(ctx->streams)) {
288 6 : AVIStream *st = gf_list_pop_back(ctx->streams);
289 6 : gf_free(st);
290 : }
291 3 : gf_list_del(ctx->streams);
292 3 : if (ctx->buf_tmp) gf_free(ctx->buf_tmp);
293 3 : }
294 :
295 1912 : static GF_Err avimux_process(GF_Filter *filter)
296 : {
297 : GF_FilterPacket *pck;
298 : s32 res=0;
299 : const GF_PropertyValue *p;
300 : u32 i, count, nb_eos, nb_suspended;
301 : AVIStream *video_st=NULL;
302 : const char *pck_data;
303 : u32 pck_size;
304 1912 : GF_AVIMuxCtx *ctx = (GF_AVIMuxCtx *) gf_filter_get_udta(filter);
305 :
306 1912 : if (ctx->in_error)
307 : return GF_IO_ERR;
308 :
309 1912 : count = gf_list_count(ctx->streams);
310 1912 : if (!ctx->avi_out) {
311 0 : for (i=0; i<count; i++) {
312 1014 : AVIStream *st = gf_list_get(ctx->streams, i);
313 1014 : GF_Err e = avimux_configure_pid(filter, st->pid, GF_FALSE);
314 1014 : if (e) return e;
315 : }
316 0 : if (!ctx->avi_out)
317 : return GF_OK;
318 : }
319 :
320 : //no video in mux, force writing 100ms of audio
321 : //hack in raw mode, avoids flushing frames after video TS for video dump
322 : //this should be fixed
323 898 : if (!ctx->has_video || (ctx->noraw && ctx->video_is_eos)) ctx->last_video_time_ms += 100;
324 :
325 :
326 : //flush all audio
327 : nb_eos = 0;
328 : nb_suspended = 0;
329 1796 : for (i=0; i<count; i++) {
330 : u64 cts;
331 1796 : AVIStream *st = gf_list_get(ctx->streams, i);
332 :
333 1796 : if (st->is_video) {
334 : video_st=st;
335 898 : continue;
336 : }
337 898 : if (!ctx->last_video_time_ms) continue;
338 886 : if (st->suspended) {
339 0 : nb_suspended++;
340 0 : continue;
341 : }
342 :
343 : while (1) {
344 3196 : pck = gf_filter_pid_get_packet(st->pid);
345 2041 : if (!pck) {
346 397 : if (gf_filter_pid_is_eos(st->pid)) {
347 3 : nb_eos++;
348 3 : if (st->suspended) {
349 0 : nb_suspended++;
350 : }
351 : }
352 : break;
353 : }
354 :
355 1644 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
356 1644 : if (p) {
357 0 : if (ctx->cur_file_idx_plus_one == p->value.uint+1) {
358 0 : } else if (!st->suspended) {
359 0 : st->suspended = GF_TRUE;
360 0 : nb_suspended++;
361 0 : break;
362 : }
363 : }
364 :
365 1644 : cts = gf_filter_pck_get_cts(pck);
366 1644 : if (cts==GF_FILTER_NO_TS) {
367 :
368 : } else {
369 1644 : cts *= 1000;
370 1644 : cts /= st->timescale;
371 1644 : if (!ctx->video_is_eos && (cts > ctx->last_video_time_ms))
372 : break;
373 : }
374 :
375 :
376 1155 : pck_data = gf_filter_pck_get_data(pck, &pck_size);
377 1155 : if (!st->is_open) {
378 3 : st->is_open = 1;
379 3 : AVI_set_audio(ctx->avi_out, st->nb_ch, st->sr, 8*st->bps, st->wfmt, st->br);
380 3 : st->tk_idx = AVI_get_audio_track(ctx->avi_out);
381 : } else {
382 1152 : AVI_set_audio_track(ctx->avi_out, st->tk_idx);
383 : }
384 1155 : res = AVI_write_audio(ctx->avi_out, (char *) pck_data, pck_size);
385 1155 : gf_filter_pid_drop_packet(st->pid);
386 1155 : if (res<0) {
387 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] Audio write error %d\n", res));
388 : }
389 1155 : ctx->nb_write++;
390 : }
391 : }
392 : //write video
393 898 : if (!video_st) return GF_OK;
394 :
395 898 : pck = gf_filter_pid_get_packet(video_st->pid);
396 :
397 898 : if (!video_st->is_open) {
398 : Double fps;
399 12 : if (!pck) return GF_OK;
400 :
401 3 : fps = ctx->fps.num;
402 3 : fps /= ctx->fps.den;
403 :
404 3 : if (video_st->fps.num && video_st->fps.den) {
405 3 : fps = video_st->fps.num;
406 3 : fps /= video_st->fps.den;
407 : } else {
408 0 : u32 duration = gf_filter_pck_get_duration(pck);
409 0 : if (duration) {
410 0 : fps = video_st->timescale;
411 0 : fps /= duration;
412 : }
413 : }
414 3 : AVI_set_video(ctx->avi_out, video_st->width, video_st->height, fps, ctx->comp_name);
415 3 : video_st->is_open = GF_TRUE;
416 : }
417 :
418 889 : if (!pck) {
419 139 : if (gf_filter_pid_is_eos(video_st->pid)) {
420 139 : nb_eos++;
421 139 : ctx->video_is_eos=GF_TRUE;
422 : }
423 : } else {
424 750 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
425 750 : if (p) {
426 0 : if (ctx->cur_file_idx_plus_one == p->value.uint+1) {
427 0 : } else if (!video_st->suspended) {
428 0 : video_st->suspended = GF_TRUE;
429 0 : nb_suspended++;
430 : pck = NULL;
431 : }
432 : }
433 : }
434 :
435 889 : if (pck) {
436 750 : u32 out_stride = video_st->stride;
437 750 : u64 cts = gf_filter_pck_get_cts(pck);
438 750 : int is_rap = gf_filter_pck_get_sap(pck) ? 1 : 0;
439 750 : pck_data = gf_filter_pck_get_data(pck, &pck_size);
440 750 : if (!pck_data) {
441 0 : GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
442 0 : if (frame_ifce && frame_ifce->get_plane) {
443 0 : GF_Err e = frame_ifce->get_plane(frame_ifce, 0, (const u8 **) &pck_data, &out_stride);
444 0 : if (e!=GF_OK) {
445 0 : pck_data = NULL;
446 : } else {
447 0 : gf_pixel_get_size_info(video_st->pf, video_st->width, video_st->height, &pck_size, &out_stride, NULL, NULL, NULL);
448 : }
449 : }
450 : }
451 750 : if (pck_data) {
452 : //raw RGB, flip
453 750 : if (video_st->is_raw_vid) {
454 0 : if (ctx->buf_alloc<pck_size) {
455 0 : ctx->buf_alloc = pck_size;
456 0 : ctx->buf_tmp = gf_realloc(ctx->buf_tmp, pck_size);
457 : }
458 0 : for (i=0; i<video_st->height; i++) {
459 0 : memcpy(ctx->buf_tmp + i*out_stride, pck_data + (video_st->height-i-1)*out_stride, out_stride);
460 : }
461 0 : res = AVI_write_frame(ctx->avi_out, (char *) ctx->buf_tmp, pck_size, is_rap);
462 : } else {
463 750 : res = AVI_write_frame(ctx->avi_out, (char *) pck_data, pck_size, is_rap);
464 : }
465 : }
466 :
467 750 : if (cts!=GF_FILTER_NO_TS) {
468 750 : u32 dur = gf_filter_pck_get_duration(pck);
469 750 : cts += dur;
470 750 : cts *= 1000;
471 750 : cts /= video_st->timescale;
472 750 : ctx->last_video_time_ms = cts + 1;
473 : } else {
474 0 : ctx->last_video_time_ms ++;
475 : }
476 750 : gf_filter_pid_drop_packet(video_st->pid);
477 750 : if (res<0) {
478 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AVIOut] Video write error %d\n", res));
479 : }
480 750 : ctx->nb_write++;
481 : }
482 :
483 889 : if (nb_suspended && (nb_suspended==count)) {
484 0 : avimux_open_close(ctx, NULL, NULL, 0);
485 0 : for (i=0; i<count; i++) {
486 0 : AVIStream *st = gf_list_get(ctx->streams, i);
487 0 : st->is_open = GF_FALSE;
488 0 : st->suspended = GF_FALSE;
489 : }
490 0 : ctx->avi_out = NULL;
491 0 : return GF_OK;
492 : }
493 :
494 889 : if (nb_eos==count) {
495 3 : if (ctx->avi_out)
496 3 : avimux_open_close(ctx, NULL, NULL, 0);
497 : return GF_EOS;
498 : }
499 :
500 : return GF_OK;
501 : }
502 :
503 2222 : static GF_FilterProbeScore avimux_probe_url(const char *url, const char *mime)
504 : {
505 2222 : char *fext = gf_file_ext_start(url);
506 2222 : if (fext && !stricmp(fext, ".avi")) return GF_FPROBE_FORCE;
507 2216 : if (mime) {
508 343 : if (!stricmp(mime, "video/avi")) return GF_FPROBE_FORCE;
509 343 : if (!stricmp(mime, "video/x-avi")) return GF_FPROBE_FORCE;
510 : }
511 2216 : return GF_FPROBE_NOT_SUPPORTED;
512 : }
513 :
514 : static GF_Err avimux_initialize(GF_Filter *filter);
515 :
516 :
517 : #define OFFS(_n) #_n, offsetof(GF_AVIMuxCtx, _n)
518 :
519 : static const GF_FilterArgs AVIMuxArgs[] =
520 : {
521 : { OFFS(dst), "location of destination file", GF_PROP_NAME, NULL, NULL, 0},
522 : { OFFS(fps), "default framerate if none indicated in stream", GF_PROP_FRACTION, "25/1", NULL, 0},
523 : { OFFS(noraw), "disable raw output in AVI, only compressed ones allowed", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
524 : { OFFS(opendml_size), "force opendml format when chunks are larger than this amount (0 means 1.9Gb max size in each riff chunk)", GF_PROP_LUINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
525 : {0}
526 : };
527 :
528 : static const GF_FilterCapability AVIMuxCaps[] =
529 : {
530 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
531 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
532 : {0},
533 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
534 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW),
535 : {0},
536 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
537 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AVC),
538 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG4_PART2),
539 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
540 : {0},
541 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
542 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG4),
543 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_MP),
544 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_LCP),
545 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_SSRP),
546 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
547 : {0},
548 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
549 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
550 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
551 : };
552 :
553 : static const GF_FilterCapability AVIMuxCapsNoRAW[] =
554 : {
555 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
556 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AVC),
557 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG4_PART2),
558 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
559 : {0},
560 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
561 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG4),
562 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_MP),
563 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_LCP),
564 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_AAC_MPEG2_SSRP),
565 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
566 : {0},
567 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
568 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG_AUDIO),
569 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_MPEG2_PART3),
570 : };
571 :
572 :
573 : GF_FilterRegister AVIMuxRegister = {
574 : .name = "avimx",
575 : GF_FS_SET_DESCRIPTION("AVI muxer")
576 : GF_FS_SET_HELP("This filter multiplexes raw or compressed audio and video to produce an AVI output.\n"
577 : "\n"
578 : "Unlike other multiplexing filters in GPAC, this filter is a sink filter and does not produce any PID to be redirected in the graph.\n"
579 : "The filter can however use template names for its output, using the first input PID to resolve the final name.\n"
580 : "The filter watches the property `FileNumber` on incoming packets to create new files.\n"
581 : )
582 : .private_size = sizeof(GF_AVIMuxCtx),
583 : .max_extra_pids = -1,
584 : .args = AVIMuxArgs,
585 : SETCAPS(AVIMuxCaps),
586 : .probe_url = avimux_probe_url,
587 : .initialize = avimux_initialize,
588 : .finalize = avimux_finalize,
589 : .configure_pid = avimux_configure_pid,
590 : .process = avimux_process
591 : };
592 :
593 3 : static GF_Err avimux_initialize(GF_Filter *filter)
594 : {
595 : const char *sep;
596 3 : GF_AVIMuxCtx *ctx = (GF_AVIMuxCtx *) gf_filter_get_udta(filter);
597 :
598 3 : if (!ctx || !ctx->dst) return GF_OK;
599 3 : ctx->streams = gf_list_new();
600 3 : if (strnicmp(ctx->dst, "file:/", 6) && strstr(ctx->dst, "://")) {
601 0 : gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
602 0 : return GF_NOT_SUPPORTED;
603 : }
604 3 : if (ctx->noraw) {
605 0 : gf_filter_override_caps(filter, AVIMuxCapsNoRAW, sizeof(AVIMuxCapsNoRAW)/sizeof(GF_FilterCapability) );
606 : }
607 3 : sep = strchr(ctx->dst, '$');
608 3 : if (sep && strchr(sep+1, '$')) {
609 : //using templates, cannot solve final name
610 : } else {
611 3 : avimux_open_close(ctx, ctx->dst, NULL, 0);
612 : }
613 : return GF_OK;
614 : }
615 :
616 : #endif
617 :
618 2877 : const GF_FilterRegister *avimux_register(GF_FilterSession *session)
619 : {
620 : #ifndef GPAC_DISABLE_AVILIB
621 2877 : return &AVIMuxRegister;
622 : #else
623 : return NULL;
624 : #endif
625 :
626 : }
627 :
|