Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom Paris 2019-2020
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / ffmpeg muxer 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 : #include <gpac/setup.h>
27 :
28 : #ifdef GPAC_HAS_FFMPEG
29 :
30 : #include "ff_common.h"
31 : #include <gpac/network.h>
32 :
33 : #if (LIBAVFORMAT_VERSION_MAJOR <= 56)
34 : #undef GPAC_HAS_FFMPEG
35 : #endif
36 :
37 : #endif
38 :
39 : #ifdef GPAC_HAS_FFMPEG
40 :
41 : typedef struct
42 : {
43 : AVStream *stream;
44 : Bool ts_rescale;
45 : AVRational in_scale;
46 : Bool in_seg_flush;
47 : u32 cts_shift;
48 :
49 : Bool suspended;
50 : } GF_FFMuxStream;
51 :
52 : enum{
53 : FFMX_STATE_ALLOC=0,
54 : FFMX_STATE_AVIO_OPEN,
55 : FFMX_STATE_HDR_DONE,
56 : FFMX_STATE_TRAILER_DONE,
57 : FFMX_STATE_EOS,
58 : FFMX_STATE_ERROR
59 : };
60 :
61 : typedef struct
62 : {
63 : //options
64 : char *dst, *mime, *ffmt;
65 : Double start, speed;
66 : u32 block_size;
67 : Bool interleave, nodisc, ffiles, noinit;
68 :
69 : AVFormatContext *muxer;
70 : //decode options
71 : AVDictionary *options;
72 :
73 : GF_List *streams;
74 :
75 : //0 not created, 1 created, header written, 2 finalized, trailer written, 3: end of stream
76 : u32 status;
77 :
78 : Bool dash_mode, init_done;
79 : u32 nb_pck_in_seg;
80 : u32 dash_seg_num;
81 : u64 offset_at_seg_start;
82 :
83 : u8 *avio_ctx_buffer;
84 : AVIOContext *avio_ctx;
85 : FILE *gfio;
86 :
87 : u32 cur_file_idx_plus_one;
88 : } GF_FFMuxCtx;
89 :
90 : static GF_Err ffmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove);
91 :
92 7 : static GF_Err ffmx_init_mux(GF_Filter *filter, GF_FFMuxCtx *ctx)
93 : {
94 : u32 i, nb_pids;
95 : int res;
96 :
97 : assert(ctx->status==FFMX_STATE_AVIO_OPEN);
98 :
99 7 : ctx->status = FFMX_STATE_HDR_DONE;
100 7 : res = avformat_init_output(ctx->muxer, &ctx->options);
101 :
102 7 : if (res<0) {
103 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to open %s - error %s\n", ctx->dst, av_err2str(res) ));
104 0 : ctx->status = FFMX_STATE_ERROR;
105 0 : return GF_NOT_SUPPORTED;
106 : }
107 :
108 7 : nb_pids = gf_filter_get_ipid_count(filter);
109 15 : for (i=0; i<nb_pids; i++) {
110 8 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
111 8 : GF_FFMuxStream *st = gf_filter_pid_get_udta(ipid);
112 8 : if (!st) continue;
113 :
114 8 : if ((st->in_scale.den == st->stream->time_base.den) && (st->in_scale.num == st->stream->time_base.num)) {
115 1 : st->ts_rescale = GF_FALSE;
116 : } else {
117 7 : st->ts_rescale = GF_TRUE;
118 : }
119 : }
120 :
121 7 : res = avformat_write_header(ctx->muxer, &ctx->options);
122 7 : if (res<0) {
123 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to write header for %s - error %s\n", ctx->dst, av_err2str(res) ));
124 0 : ctx->status = FFMX_STATE_ERROR;
125 0 : return GF_SERVICE_ERROR;
126 : }
127 :
128 7 : ffmpeg_report_unused_options(filter, ctx->options);
129 :
130 7 : return GF_OK;
131 : }
132 :
133 :
134 29 : static int ffavio_write_packet(void *opaque, uint8_t *buf, int buf_size)
135 : {
136 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *)opaque;
137 29 : return (int) gf_fwrite(buf, buf_size, ctx->gfio);
138 : }
139 :
140 20 : static int64_t ffavio_seek(void *opaque, int64_t offset, int whence)
141 : {
142 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *)opaque;
143 20 : if (whence==AVSEEK_SIZE) {
144 0 : u64 pos = gf_ftell(ctx->gfio);
145 0 : u64 size = gf_fsize(ctx->gfio);
146 0 : gf_fseek(ctx->gfio, pos, SEEK_SET);
147 0 : return size;
148 : }
149 20 : return (int64_t) gf_fseek(ctx->gfio, offset, whence);
150 : }
151 :
152 7 : static GF_Err ffmx_open_url(GF_FFMuxCtx *ctx, char *final_name)
153 : {
154 : const char *dst;
155 : Bool use_gfio=GF_FALSE;
156 7 : AVOutputFormat *ofmt = ctx->muxer->oformat;
157 :
158 7 : dst = final_name ? final_name : ctx->dst;
159 7 : if (!strncmp(dst, "gfio://", 7)) {
160 : use_gfio = GF_TRUE;
161 : }
162 :
163 7 : if (ofmt) {
164 : //handle local urls some_dir/some_other_dir/file.ext, ffmpeg doesn't create the dirs for us
165 7 : if (! (ofmt->flags & AVFMT_NOFILE) && gf_url_is_local(dst)) {
166 6 : FILE *f = gf_fopen(dst, "wb");
167 6 : gf_fclose(f);
168 : }
169 : }
170 :
171 7 : if (use_gfio) {
172 1 : ctx->gfio = gf_fopen(dst, "wb");
173 1 : if (!ctx->gfio) return GF_URL_ERROR;
174 1 : ctx->avio_ctx_buffer = av_malloc(ctx->block_size);
175 1 : if (!ctx->avio_ctx_buffer) return GF_OUT_OF_MEM;
176 :
177 1 : ctx->avio_ctx = avio_alloc_context(ctx->avio_ctx_buffer, ctx->block_size,
178 : 1, ctx, NULL, &ffavio_write_packet, &ffavio_seek);
179 :
180 1 : if (!ctx->avio_ctx) {
181 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Failed to create AVIO context for %s\n", dst));
182 : return GF_OUT_OF_MEM;
183 : }
184 1 : ctx->muxer->pb = ctx->avio_ctx;
185 6 : } else if (!ofmt || !(ofmt->flags & AVFMT_NOFILE) ) {
186 6 : int res = avio_open2(&ctx->muxer->pb, dst, AVIO_FLAG_WRITE, NULL, &ctx->options);
187 6 : if (res<0) {
188 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to open AVIO context for %s - error %s\n", dst, av_err2str(res) ));
189 : return GF_FILTER_NOT_SUPPORTED;
190 : }
191 : }
192 7 : ctx->status = FFMX_STATE_AVIO_OPEN;
193 7 : return GF_OK;
194 : }
195 7 : static GF_Err ffmx_initialize_ex(GF_Filter *filter, Bool use_templates)
196 : {
197 : const char *url, *sep;
198 : AVOutputFormat *ofmt;
199 7 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *) gf_filter_get_udta(filter);
200 :
201 7 : ffmpeg_setup_logs(GF_LOG_CONTAINER);
202 :
203 7 : ctx->muxer = avformat_alloc_context();
204 7 : if (!ctx->muxer) return GF_OUT_OF_MEM;
205 :
206 7 : if (!ctx->streams)
207 7 : ctx->streams = gf_list_new();
208 :
209 7 : url = ctx->dst;
210 :
211 7 : if (!strncmp(ctx->dst, "gfio://", 7)) {
212 1 : url = gf_fileio_translate_url(ctx->dst);
213 : }
214 7 : sep = strchr(url, '$');
215 7 : if (sep && strchr(sep+1, '$'))
216 : use_templates = GF_TRUE;
217 :
218 7 : ofmt = av_guess_format(ctx->ffmt, url, ctx->mime);
219 : //if protocol is present, we may fail at guessing the format
220 7 : if (!ofmt && !ctx->ffmt) {
221 : u32 len;
222 : char szProto[20];
223 0 : char *proto = strstr(url, "://");
224 0 : if (!proto)
225 0 : return GF_FILTER_NOT_SUPPORTED;
226 0 : szProto[19] = 0;
227 0 : len = (u32) (proto - url);
228 0 : if (len>19) len=19;
229 0 : strncpy(szProto, url, len);
230 0 : szProto[len] = 0;
231 0 : ofmt = av_guess_format(szProto, url, ctx->mime);
232 : }
233 7 : if (!ofmt) {
234 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Failed to guess output format for %s, cannot run\n", ctx->dst));
235 0 : if (!ctx->ffmt) {
236 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Try specifying the format through :ffmt option\n"));
237 : }
238 : return GF_SERVICE_ERROR;
239 : }
240 7 : ctx->muxer->oformat = ofmt;
241 :
242 7 : ctx->status = FFMX_STATE_ALLOC;
243 : //templates are used, we need to postpone opening the url until we have a PID and a first packet
244 7 : if (use_templates)
245 : return GF_OK;
246 :
247 6 : return ffmx_open_url(ctx, NULL);
248 : }
249 :
250 7 : static GF_Err ffmx_initialize(GF_Filter *filter)
251 : {
252 7 : return ffmx_initialize_ex(filter, GF_FALSE);
253 : }
254 11 : static GF_Err ffmx_start_seg(GF_Filter *filter, GF_FFMuxCtx *ctx, const char *seg_name)
255 : {
256 : int i;
257 : int res;
258 :
259 : /*create a new output context, clone all streams from current one and swap output context*/
260 11 : if (ctx->ffiles) {
261 : AVFormatContext *segmux;
262 :
263 0 : res = avformat_alloc_output_context2(&segmux, ctx->muxer->oformat, NULL, NULL);
264 0 : if (res<0) {
265 0 : return GF_IO_ERR;
266 : }
267 0 : segmux->interrupt_callback = ctx->muxer->interrupt_callback;
268 0 : segmux->max_delay = ctx->muxer->max_delay;
269 0 : av_dict_copy(&segmux->metadata, ctx->muxer->metadata, 0);
270 0 : segmux->opaque = ctx->muxer->opaque;
271 0 : segmux->io_close = ctx->muxer->io_close;
272 0 : segmux->io_open = ctx->muxer->io_open;
273 0 : segmux->flags = ctx->muxer->flags;
274 :
275 0 : for (i=0; i< (s32) ctx->muxer->nb_streams; i++) {
276 : u32 j;
277 : AVStream *st;
278 : AVCodecParameters *ipar, *opar;
279 :
280 0 : if (!(st = avformat_new_stream(segmux, NULL)))
281 : return GF_OUT_OF_MEM;
282 0 : ipar = ctx->muxer->streams[i]->codecpar;
283 0 : opar = st->codecpar;
284 0 : avcodec_parameters_copy(opar, ipar);
285 0 : if (!segmux->oformat->codec_tag ||
286 0 : av_codec_get_id (segmux->oformat->codec_tag, ipar->codec_tag) == opar->codec_id ||
287 0 : av_codec_get_tag(segmux->oformat->codec_tag, ipar->codec_id) <= 0) {
288 0 : opar->codec_tag = ipar->codec_tag;
289 : } else {
290 0 : opar->codec_tag = 0;
291 : }
292 0 : st->sample_aspect_ratio = ctx->muxer->streams[i]->sample_aspect_ratio;
293 0 : st->time_base = ctx->muxer->streams[i]->time_base;
294 0 : st->avg_frame_rate = ctx->muxer->streams[i]->avg_frame_rate;
295 :
296 0 : if (ctx->muxer->streams[i]->codecpar->codec_tag == MKTAG('t','m','c','d'))
297 0 : st->codec->time_base = ctx->muxer->streams[i]->codec->time_base;
298 :
299 0 : av_dict_copy(&st->metadata, ctx->muxer->streams[i]->metadata, 0);
300 :
301 : /*reassign stream*/
302 0 : for (j=0; j<gf_list_count(ctx->streams); j++) {
303 0 : GF_FFMuxStream *ast = gf_list_get(ctx->streams, j);
304 0 : if (ast->stream == ctx->muxer->streams[i]) {
305 0 : ast->stream = st;
306 : break;
307 : }
308 : }
309 : }
310 : //swap context
311 0 : avformat_free_context(ctx->muxer);
312 0 : ctx->muxer = segmux;
313 : } else {
314 11 : ctx->muxer->io_close(ctx->muxer, ctx->muxer->pb);
315 11 : ctx->muxer->pb = NULL;
316 : }
317 :
318 : // av_freep(&ctx->muxer->url);
319 : // ctx->muxer->url = av_strdup(seg_name);
320 11 : strncpy(ctx->muxer->filename, seg_name, 1023);
321 11 : ctx->muxer->filename[1023]=0;
322 11 : ctx->offset_at_seg_start = 0;
323 :
324 11 : if (!(ctx->muxer->oformat->flags & AVFMT_NOFILE)) {
325 : //handle local urls some_dir/some_other_dir/file.ext, ffmpeg doesn't create the dirs for us
326 11 : if (gf_url_is_local(seg_name)) {
327 11 : FILE *f = gf_fopen(seg_name, "wb");
328 11 : gf_fclose(f);
329 : }
330 :
331 11 : res = ctx->muxer->io_open(ctx->muxer, &ctx->muxer->pb, /*ctx->muxer->url*/seg_name, AVIO_FLAG_WRITE, NULL);
332 11 : if (res < 0) {
333 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to open new segment %s - error %s\n", seg_name, av_err2str(res) ));
334 : return GF_IO_ERR;
335 : }
336 : }
337 11 : ctx->muxer->pb->seekable = 0;
338 :
339 11 : if (ctx->muxer->oformat->priv_class && ctx->muxer->priv_data)
340 11 : av_opt_set(ctx->muxer->priv_data, "mpegts_flags", "+resend_headers", 0);
341 :
342 11 : if (ctx->ffiles) {
343 0 : AVDictionary *options = NULL;
344 0 : av_dict_copy(&options, ctx->options, 0);
345 0 : av_dict_set(&options, "fflags", "-autobsf", 0);
346 0 : res = avformat_write_header(ctx->muxer, &options);
347 0 : av_dict_free(&options);
348 0 : if (res < 0) {
349 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to configure segment %s - error %s\n", seg_name, av_err2str(res) ));
350 0 : return GF_IO_ERR;
351 : }
352 : }
353 11 : ctx->status = FFMX_STATE_HDR_DONE;
354 : return GF_OK;
355 : }
356 :
357 :
358 24 : static GF_Err ffmx_close_seg(GF_Filter *filter, GF_FFMuxCtx *ctx, Bool send_evt_only)
359 : {
360 : GF_FilterEvent evt;
361 : GF_FilterPid *pid;
362 :
363 24 : av_write_frame(ctx->muxer, NULL);
364 :
365 24 : if (!send_evt_only) {
366 : int res=0;
367 :
368 13 : if (ctx->ffiles) {
369 0 : if (ctx->status==FFMX_STATE_HDR_DONE) {
370 0 : res = av_write_trailer(ctx->muxer);
371 : } else {
372 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Invalid state %d for segment %s close\n", ctx->status, /*ctx->muxer->url*/ctx->muxer->filename));
373 : return GF_SERVICE_ERROR;
374 : }
375 : }
376 :
377 0 : if (res<0) {
378 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to flush segment %s - error %s\n", /*ctx->muxer->url*/ctx->muxer->filename, av_err2str(res) ));
379 : return GF_SERVICE_ERROR;
380 : }
381 13 : ctx->status = FFMX_STATE_TRAILER_DONE;
382 : }
383 24 : ctx->nb_pck_in_seg = 0;
384 :
385 24 : pid = gf_filter_get_ipid(filter, 0);
386 24 : GF_FEVT_INIT(evt, GF_FEVT_SEGMENT_SIZE, pid);
387 : evt.seg_size.seg_url = NULL;
388 :
389 : //init seg
390 24 : if (!ctx->init_done) {
391 2 : evt.seg_size.is_init = GF_TRUE;
392 2 : ctx->init_done = GF_TRUE;
393 : } else {
394 : evt.seg_size.is_init = GF_FALSE;
395 : }
396 24 : evt.seg_size.media_range_start = ctx->offset_at_seg_start;
397 24 : evt.seg_size.media_range_end = ctx->muxer->pb ? ctx->muxer->pb->written-1 : 0;
398 24 : ctx->offset_at_seg_start = evt.seg_size.media_range_end;
399 :
400 24 : gf_filter_pid_send_event(pid, &evt);
401 24 : return GF_OK;
402 : }
403 :
404 4987 : static GF_Err ffmx_process(GF_Filter *filter)
405 : {
406 : GF_Err e = GF_OK;
407 4987 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *) gf_filter_get_udta(filter);
408 4987 : u32 nb_done, nb_segs_done, nb_suspended, i, nb_pids = gf_filter_get_ipid_count(filter);
409 :
410 4987 : if (ctx->status<FFMX_STATE_HDR_DONE) {
411 : Bool all_ready = GF_TRUE;
412 : Bool needs_reinit = (ctx->status==FFMX_STATE_ALLOC) ? GF_TRUE : GF_FALSE;
413 :
414 10 : for (i=0; i<nb_pids; i++) {
415 10 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
416 10 : GF_FFMuxStream *st = gf_filter_pid_get_udta(ipid);
417 10 : GF_FilterPacket *ipck = gf_filter_pid_get_packet(ipid);
418 :
419 10 : if (needs_reinit) {
420 1 : e = ffmx_configure_pid(filter, ipid, GF_FALSE);
421 1 : if (e) return e;
422 : }
423 10 : if (!ipck) {
424 : all_ready = GF_FALSE;
425 1 : continue;
426 : }
427 :
428 9 : if (st->stream->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
429 7 : u8 ilced = gf_filter_pck_get_interlaced(ipck);
430 7 : if (ilced==2) st->stream->codecpar->field_order = AV_FIELD_BB;
431 7 : else if (ilced==1) st->stream->codecpar->field_order = AV_FIELD_TT;
432 7 : else st->stream->codecpar->field_order = AV_FIELD_PROGRESSIVE;
433 : }
434 2 : else if (st->stream->codecpar->codec_type==AVMEDIA_TYPE_AUDIO) {
435 2 : st->stream->codecpar->seek_preroll = gf_filter_pck_get_roll_info(ipck);
436 : }
437 : }
438 8 : if (!all_ready || (ctx->status==FFMX_STATE_ALLOC)) return GF_OK;
439 :
440 : //good to go, finalize mux
441 7 : ffmx_init_mux(filter, ctx);
442 : }
443 4986 : if (ctx->status==FFMX_STATE_EOS) return GF_EOS;
444 1612 : else if (ctx->status==FFMX_STATE_ERROR) return GF_SERVICE_ERROR;
445 :
446 : nb_segs_done = 0;
447 : nb_done = 0;
448 : nb_suspended = 0;
449 2204 : for (i=0; i<nb_pids; i++) {
450 2204 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
451 2204 : GF_FFMuxStream *st = gf_filter_pid_get_udta(ipid);
452 2204 : if (!st) continue;
453 2204 : if (st->in_seg_flush) {
454 0 : nb_segs_done ++;
455 0 : continue;
456 : }
457 2204 : if (st->suspended) {
458 0 : nb_suspended++;
459 0 : continue;
460 : }
461 :
462 : while (1) {
463 : u64 cts;
464 : int res;
465 : const GF_PropertyValue *p;
466 : u32 sap;
467 : AVPacket ffpck;
468 7166 : GF_FilterPacket *ipck = gf_filter_pid_get_packet(ipid);
469 7166 : if (!ipck) {
470 2184 : if (gf_filter_pid_is_eos(ipid)) {
471 8 : nb_done++;
472 : }
473 2204 : break;
474 : }
475 4982 : cts = gf_filter_pck_get_cts(ipck);
476 :
477 4982 : if (cts == GF_FILTER_NO_TS) {
478 0 : p = gf_filter_pck_get_property(ipck, GF_PROP_PCK_EODS);
479 0 : if (p && p->value.boolean) {
480 0 : st->in_seg_flush = GF_TRUE;
481 0 : nb_segs_done ++;
482 0 : nb_done++;
483 0 : gf_filter_pid_drop_packet(ipid);
484 0 : break;
485 : }
486 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Packet with no CTS assigned, cannot store to track, ignoring\n"));
487 0 : gf_filter_pid_drop_packet(ipid);
488 0 : continue;
489 : }
490 :
491 4982 : p = gf_filter_pck_get_property(ipck, GF_PROP_PCK_FILENUM);
492 4982 : if (ctx->dash_mode) {
493 524 : if (p) {
494 : //flush segment
495 42 : if (ctx->dash_seg_num && (p->value.uint != ctx->dash_seg_num)) {
496 20 : st->in_seg_flush = GF_TRUE;
497 20 : nb_segs_done++;
498 20 : break;
499 : }
500 22 : ctx->dash_seg_num = p->value.uint;
501 : //segment done, check if we switch file
502 22 : p = gf_filter_pck_get_property(ipck, GF_PROP_PCK_FILENAME);
503 22 : if (p && p->value.string) {
504 11 : ffmx_close_seg(filter, ctx, GF_FALSE);
505 11 : ffmx_start_seg(filter, ctx, p->value.string);
506 : }
507 : //otherwise close segment for event processing only
508 : else
509 11 : ffmx_close_seg(filter, ctx, GF_TRUE);
510 : }
511 4458 : } else if (p) {
512 0 : if (ctx->cur_file_idx_plus_one == p->value.uint+1) {
513 0 : } else if (!st->suspended) {
514 0 : st->suspended = GF_TRUE;
515 0 : nb_suspended++;
516 0 : break;
517 : }
518 : }
519 :
520 4962 : av_init_packet(&ffpck);
521 4962 : ffpck.stream_index = st->stream->index;
522 4962 : ffpck.dts = gf_filter_pck_get_dts(ipck);
523 4962 : ffpck.pts = gf_filter_pck_get_cts(ipck);
524 4962 : if (st->cts_shift) ffpck.pts += st->cts_shift;
525 :
526 4962 : if (ffpck.dts > ffpck.pts) {
527 3 : st->cts_shift = (u32) (ffpck.dts - ffpck.pts);
528 3 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[FFMux] Negative CTS offset -%d found, adjusting offset\n", st->cts_shift));
529 3 : ffpck.pts = ffpck.dts;
530 : }
531 4962 : ffpck.duration = gf_filter_pck_get_duration(ipck);
532 4962 : sap = gf_filter_pck_get_sap(ipck);
533 4962 : if (sap==GF_FILTER_SAP_1) ffpck.flags = AV_PKT_FLAG_KEY;
534 4962 : ffpck.data = (u8 *) gf_filter_pck_get_data(ipck, &ffpck.size);
535 :
536 4962 : if (st->ts_rescale) {
537 2176 : av_packet_rescale_ts(&ffpck, st->in_scale, st->stream->time_base);
538 : }
539 :
540 4962 : if (ctx->interleave) {
541 4962 : res = av_interleaved_write_frame(ctx->muxer, &ffpck);
542 : } else {
543 0 : res = av_write_frame(ctx->muxer, &ffpck);
544 : }
545 4962 : if (res<0) {
546 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Fail to write packet to %s - error %s\n", /*ctx->muxer->url*/ctx->muxer->filename, av_err2str(res) ));
547 : e = GF_IO_ERR;
548 : }
549 :
550 4962 : gf_filter_pid_drop_packet(ipid);
551 4962 : ctx->nb_pck_in_seg++;
552 : }
553 : }
554 :
555 : //done writing file
556 1612 : if (nb_suspended && (nb_suspended==nb_pids)) {
557 0 : av_write_trailer(ctx->muxer);
558 0 : if (ctx->muxer) avformat_free_context(ctx->muxer);
559 0 : ctx->muxer = NULL;
560 0 : ctx->status = FFMX_STATE_ALLOC;
561 0 : ffmx_initialize_ex(filter, GF_TRUE);
562 0 : for (i=0; i<nb_pids; i++) {
563 0 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
564 0 : GF_FFMuxStream *st = gf_filter_pid_get_udta(ipid);
565 0 : if (!st) continue;
566 0 : st->suspended = GF_FALSE;
567 0 : st->stream = 0;
568 0 : ffmx_configure_pid(filter, ipid, GF_FALSE);
569 : }
570 : nb_done = 0;
571 : }
572 :
573 : //done writing segment
574 1612 : if (nb_segs_done==nb_pids) {
575 20 : ctx->dash_seg_num = 0;
576 40 : for (i=0; i<nb_pids; i++) {
577 20 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
578 20 : GF_FFMuxStream *st = gf_filter_pid_get_udta(ipid);
579 20 : if (!st) continue;
580 20 : st->in_seg_flush = GF_FALSE;
581 : }
582 : }
583 :
584 1612 : if (e) return e;
585 :
586 1612 : if (nb_done==nb_pids) {
587 7 : if (ctx->status==FFMX_STATE_HDR_DONE) {
588 7 : if (ctx->dash_mode) {
589 2 : ffmx_close_seg(filter, ctx, GF_FALSE);
590 : } else {
591 5 : av_write_trailer(ctx->muxer);
592 : }
593 7 : ctx->status = FFMX_STATE_EOS;
594 : }
595 : return GF_EOS;
596 : }
597 : return GF_OK;
598 : }
599 :
600 9 : static GF_Err ffmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
601 : {
602 : int res;
603 : AVStream *avst;
604 : Bool check_disc = GF_FALSE;
605 : u32 streamtype, codec_id;
606 : u32 ff_codec_id, ff_st, ff_codec_tag;
607 : const GF_PropertyValue *p, *dsi;
608 : GF_FFMuxStream *st;
609 9 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *) gf_filter_get_udta(filter);
610 :
611 : //disconnect of src pid (not yet supported)
612 9 : if (is_remove) {
613 : //one in one out, this is simple
614 : return GF_OK;
615 : }
616 9 : st = gf_filter_pid_get_udta(pid);
617 9 : if (st) {
618 1 : if (ctx->status>FFMX_STATE_HDR_DONE)
619 : check_disc = GF_TRUE;
620 : } else {
621 8 : if (ctx->status>FFMX_STATE_HDR_DONE) {
622 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Cannot dynamically add new stream to %s, not supported\n", ctx->dst ));
623 : return GF_NOT_SUPPORTED;
624 : }
625 : }
626 9 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
627 9 : if (!p) return GF_NOT_SUPPORTED;
628 9 : streamtype = p->value.uint;
629 :
630 : //not supported yet
631 9 : if (streamtype==GF_STREAM_ENCRYPTED) {
632 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Cannot import protected stream, not supported\n"));
633 : return GF_NOT_SUPPORTED;
634 : }
635 :
636 9 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
637 9 : if (!p) return GF_NOT_SUPPORTED;
638 9 : codec_id = p->value.uint;
639 :
640 9 : ff_st = ffmpeg_stream_type_from_gpac(streamtype);
641 9 : if (codec_id==GF_CODECID_RAW) {
642 0 : ff_codec_tag = 0;
643 0 : if (streamtype==GF_STREAM_VISUAL) {
644 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
645 0 : if (!p) return GF_NOT_SUPPORTED;
646 : switch (p->value.uint) {
647 : default:
648 : ff_codec_id = AV_CODEC_ID_RAWVIDEO;
649 : break;
650 : }
651 : } else {
652 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
653 0 : if (!p) return GF_NOT_SUPPORTED;
654 :
655 0 : switch (p->value.uint) {
656 : case GF_AUDIO_FMT_U8:
657 : case GF_AUDIO_FMT_U8P:
658 : ff_codec_id = AV_CODEC_ID_PCM_U8;
659 : break;
660 0 : case GF_AUDIO_FMT_S16:
661 : case GF_AUDIO_FMT_S16P:
662 : ff_codec_id = AV_CODEC_ID_PCM_S16LE;
663 0 : break;
664 0 : case GF_AUDIO_FMT_S24:
665 : case GF_AUDIO_FMT_S24P:
666 : ff_codec_id = AV_CODEC_ID_PCM_S24LE;
667 0 : break;
668 0 : case GF_AUDIO_FMT_S32:
669 : case GF_AUDIO_FMT_S32P:
670 : ff_codec_id = AV_CODEC_ID_PCM_S32LE;
671 0 : break;
672 0 : case GF_AUDIO_FMT_FLT:
673 : case GF_AUDIO_FMT_FLTP:
674 : ff_codec_id = AV_CODEC_ID_PCM_F32LE;
675 0 : break;
676 0 : default:
677 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Unmapped raw audio format %s to FFMPEG, patch welcome\n", gf_audio_fmt_name(p->value.uint) ));
678 : return GF_NOT_SUPPORTED;
679 : }
680 : }
681 : } else {
682 9 : ff_codec_id = ffmpeg_codecid_from_gpac(codec_id, &ff_codec_tag);
683 : }
684 :
685 9 : if (ctx->muxer->oformat && ctx->muxer->oformat->query_codec) {
686 6 : res = ctx->muxer->oformat->query_codec(ff_codec_id, 1);
687 6 : if (!res) {
688 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Codec %s not supported in container %s\n", gf_codecid_name(codec_id), ctx->muxer->oformat->name));
689 : return GF_NOT_SUPPORTED;
690 : }
691 : }
692 9 : const AVCodec *c = avcodec_find_decoder(ff_codec_id);
693 9 : if (!c) return GF_NOT_SUPPORTED;
694 :
695 9 : if (!st) {
696 : GF_FilterEvent evt;
697 :
698 8 : GF_SAFEALLOC(st, GF_FFMuxStream);
699 8 : if (!st) return GF_OUT_OF_MEM;
700 8 : gf_filter_pid_set_udta(pid, st);
701 8 : gf_list_add(ctx->streams, st);
702 :
703 8 : gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "FFMux");
704 8 : gf_filter_pid_send_event(pid, &evt);
705 : }
706 :
707 9 : if (ctx->status==FFMX_STATE_ALLOC) {
708 : char szPath[GF_MAX_PATH];
709 : const GF_PropertyValue *p;
710 : const char *file_suffix = NULL;
711 2 : GF_FilterPacket *pck = gf_filter_pid_get_packet(pid);
712 2 : if (!pck) {
713 1 : if (gf_filter_pid_is_eos(pid))
714 1 : return GF_SERVICE_ERROR;
715 1 : return GF_OK;
716 : }
717 1 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
718 1 : ctx->cur_file_idx_plus_one = p ? (p->value.uint + 1) : 1;
719 :
720 1 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
721 1 : if (p && p->value.string) file_suffix = p->value.string;
722 :
723 1 : gf_filter_pid_resolve_file_template(pid, ctx->dst, szPath, ctx->cur_file_idx_plus_one-1, file_suffix);
724 1 : ffmx_open_url(ctx, szPath);
725 : }
726 :
727 :
728 8 : if (!st->stream) {
729 8 : st->stream = avformat_new_stream(ctx->muxer, c);
730 8 : if (!st->stream) return GF_NOT_SUPPORTED;
731 : }
732 8 : avst = st->stream;
733 :
734 8 : dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
735 :
736 8 : if (check_disc) {
737 : u32 dsi_crc, old_dsi_crc;
738 : Bool is_ok = GF_TRUE;
739 0 : if (avst->codecpar->codec_id != ff_codec_id) is_ok = GF_FALSE;
740 0 : else if (avst->codecpar->codec_type != ff_st) is_ok = GF_FALSE;
741 :
742 : dsi_crc = old_dsi_crc = 0;
743 0 : if (avst->codecpar->extradata) old_dsi_crc = gf_crc_32(avst->codecpar->extradata, avst->codecpar->extradata_size);
744 0 : if (dsi) dsi_crc = gf_crc_32(dsi->value.data.ptr, dsi->value.data.size);
745 :
746 0 : if (dsi_crc != old_dsi_crc) is_ok = GF_FALSE;
747 0 : if (!is_ok) {
748 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[FFMux] Dynamic stream update in mux %s not supported\n", ctx->dst));
749 : return GF_NOT_SUPPORTED;
750 : }
751 : }
752 :
753 8 : avst->codecpar->codec_id = ff_codec_id;
754 8 : avst->codecpar->codec_type = ff_st;
755 8 : avst->codecpar->codec_tag = ff_codec_tag;
756 :
757 8 : if (dsi && dsi->value.data.ptr) {
758 8 : if (avst->codecpar->extradata) av_free(avst->codecpar->extradata);
759 8 : avst->codecpar->extradata_size = dsi->value.data.size;
760 8 : avst->codecpar->extradata = av_malloc(avst->codecpar->extradata_size+ AV_INPUT_BUFFER_PADDING_SIZE);
761 8 : if (avst->codecpar->extradata) {
762 8 : memcpy(avst->codecpar->extradata, dsi->value.data.ptr, avst->codecpar->extradata_size);
763 8 : memset(avst->codecpar->extradata + dsi->value.data.size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
764 : }
765 : }
766 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
767 8 : if (p) avst->id = p->value.uint;
768 :
769 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
770 8 : if (p) {
771 8 : avst->time_base.den = p->value.uint;
772 8 : avst->time_base.num = 1;
773 : }
774 8 : st->in_scale = avst->time_base;
775 :
776 8 : avst->start_time = 0;
777 8 : avst->duration = 0;
778 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
779 8 : if (p && p->value.lfrac.den) {
780 7 : avst->duration = p->value.lfrac.num;
781 7 : avst->duration *= avst->time_base.den;
782 7 : avst->duration /= p->value.lfrac.den;
783 : }
784 :
785 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_BITRATE);
786 8 : if (p) avst->codecpar->bit_rate = p->value.uint;
787 :
788 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CTS_SHIFT);
789 8 : if (p) st->cts_shift = p->value.uint;
790 :
791 8 : if (streamtype==GF_STREAM_VISUAL) {
792 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
793 7 : if (p) avst->codecpar->width = p->value.uint;
794 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
795 7 : if (p) avst->codecpar->height = p->value.uint;
796 :
797 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
798 7 : if (p) {
799 7 : avst->r_frame_rate.num = p->value.frac.num;
800 7 : avst->r_frame_rate.den = p->value.frac.den;
801 7 : avst->avg_frame_rate = avst->r_frame_rate;
802 : }
803 7 : if (codec_id==GF_CODECID_RAW) {
804 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
805 0 : if (p) {
806 0 : avst->codecpar->format = ffmpeg_pixfmt_from_gpac(p->value.uint);
807 0 : avst->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag(avst->codecpar->format);
808 : }
809 : }
810 :
811 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
812 7 : if (p) {
813 2 : avst->codecpar->sample_aspect_ratio.num = p->value.frac.num;
814 2 : avst->codecpar->sample_aspect_ratio.den = p->value.frac.den;
815 : }
816 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_PRIMARIES);
817 7 : if (p) avst->codecpar->color_primaries = p->value.uint;
818 :
819 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_RANGE);
820 7 : if (p) avst->codecpar->color_range = (p->value.uint==1) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
821 :
822 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_TRANSFER);
823 7 : if (p) avst->codecpar->color_trc = p->value.uint;
824 :
825 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_MX);
826 7 : if (p) avst->codecpar->color_space = p->value.uint;
827 :
828 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_CHROMALOC);
829 7 : if (p) avst->codecpar->chroma_location = p->value.uint;
830 :
831 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
832 7 : if (p && avst->r_frame_rate.num && avst->r_frame_rate.den) {
833 0 : s64 delay = p->value.longsint;
834 0 : delay *= avst->r_frame_rate.num;
835 0 : delay /= avst->r_frame_rate.den;
836 0 : avst->codecpar->video_delay = (s32) delay;
837 : }
838 :
839 : }
840 1 : else if (streamtype==GF_STREAM_AUDIO) {
841 : u64 ch_layout;
842 : u32 samplerate=0;
843 :
844 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
845 1 : if (p) avst->codecpar->sample_rate = samplerate = p->value.uint;
846 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
847 1 : if (p) avst->codecpar->channels = p->value.uint;
848 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLES_PER_FRAME);
849 1 : if (p) avst->codecpar->frame_size = p->value.uint;
850 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_BPS);
851 1 : if (p) avst->codecpar->bits_per_raw_sample = p->value.uint;
852 1 : if (codec_id==GF_CODECID_RAW) {
853 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
854 0 : if (p) avst->codecpar->format = ffmpeg_audio_fmt_from_gpac(p->value.uint);
855 : }
856 :
857 : ch_layout = AV_CH_LAYOUT_MONO;
858 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT);
859 1 : if (p)
860 0 : ch_layout = p->value.longuint;
861 1 : else if (avst->codecpar->channels==2)
862 : ch_layout = AV_CH_LAYOUT_STEREO;
863 1 : avst->codecpar->channel_layout = ffmpeg_channel_layout_from_gpac(ch_layout);
864 :
865 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
866 1 : if (p && (p->value.sint<0) && samplerate) {
867 0 : s64 pad = p->value.longsint;
868 0 : if (st->in_scale.den != samplerate) {
869 0 : pad *= samplerate;
870 0 : pad /= st->in_scale.den;
871 : }
872 0 : avst->codecpar->initial_padding = (s32) pad;
873 : }
874 : /*
875 : //not mapped in gpac
876 : int trailing_padding;
877 : */
878 : }
879 :
880 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MODE);
881 8 : if (p && (p->value.uint==1)) {
882 2 : ctx->dash_mode = GF_TRUE;
883 : }
884 :
885 :
886 8 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
887 8 : return GF_OK;
888 : }
889 :
890 7 : static void ffmx_finalize(GF_Filter *filter)
891 : {
892 7 : GF_FFMuxCtx *ctx = (GF_FFMuxCtx *) gf_filter_get_udta(filter);
893 :
894 7 : if (ctx->status==FFMX_STATE_HDR_DONE) {
895 0 : if (ctx->dash_mode) {
896 0 : ffmx_close_seg(filter, ctx, GF_FALSE);
897 : } else {
898 0 : av_write_trailer(ctx->muxer);
899 : }
900 0 : ctx->status = FFMX_STATE_TRAILER_DONE;
901 : }
902 7 : if (!ctx->gfio && ctx->muxer->pb) {
903 6 : ctx->muxer->io_close(ctx->muxer, ctx->muxer->pb);
904 : }
905 :
906 7 : if (ctx->options) av_dict_free(&ctx->options);
907 7 : if (ctx->muxer) avformat_free_context(ctx->muxer);
908 15 : while (gf_list_count(ctx->streams)) {
909 8 : GF_FFMuxStream *st = gf_list_pop_back(ctx->streams);
910 8 : gf_free(st);
911 : }
912 7 : gf_list_del(ctx->streams);
913 7 : if (ctx->avio_ctx) {
914 1 : if (ctx->avio_ctx->buffer) av_freep(&ctx->avio_ctx->buffer);
915 1 : av_freep(&ctx->avio_ctx);
916 : }
917 7 : if (ctx->gfio) gf_fclose(ctx->gfio);
918 7 : return;
919 : }
920 :
921 18 : static GF_Err ffmx_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *arg_val)
922 : {
923 : s32 res;
924 18 : GF_FFMuxCtx *ctx = gf_filter_get_udta(filter);
925 :
926 : //initial parsing of arguments
927 18 : if (!ctx->muxer) {
928 18 : switch (arg_val->type) {
929 18 : case GF_PROP_STRING:
930 18 : res = av_dict_set(&ctx->options, arg_name, arg_val->value.string, 0);
931 18 : if (res<0) {
932 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FFDec] Failed to set option %s:%s\n", arg_name, arg_val ));
933 : }
934 : break;
935 0 : default:
936 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[FFDec] Failed to set option %s:%s, unrecognized type %d\n", arg_name, arg_val, arg_val->type ));
937 : return GF_NOT_SUPPORTED;
938 : }
939 : return GF_OK;
940 : }
941 : //updates of arguments, not supported for ffmpeg decoders
942 : return GF_NOT_SUPPORTED;
943 : }
944 :
945 2188 : static GF_FilterProbeScore ffmx_probe_url(const char *url, const char *mime)
946 : {
947 : const char *proto;
948 2188 : if (url && !strncmp(url, "gfio://", 7)) {
949 4 : url = gf_fileio_translate_url(url);
950 : }
951 2188 : if (!url)
952 : return GF_FPROBE_NOT_SUPPORTED;
953 :
954 :
955 2188 : AVOutputFormat *ofmt = av_guess_format(NULL, url, mime);
956 2188 : if (!ofmt && mime) ofmt = av_guess_format(NULL, NULL, mime);
957 2188 : if (!ofmt && url) ofmt = av_guess_format(NULL, url, NULL);
958 :
959 2188 : if (ofmt) return GF_FPROBE_SUPPORTED;
960 :
961 922 : proto = strstr(url, "://");
962 922 : if (!proto)
963 : return GF_FPROBE_NOT_SUPPORTED;
964 :
965 35 : proto = avio_find_protocol_name(url);
966 35 : if (proto)
967 : return GF_FPROBE_MAYBE_SUPPORTED;
968 9 : return GF_FPROBE_NOT_SUPPORTED;
969 : }
970 :
971 : static const GF_FilterCapability FFMuxCaps[] =
972 : {
973 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
974 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
975 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
976 :
977 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
978 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
979 :
980 : #ifdef FF_SUB_SUPPORT
981 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
982 : #endif
983 : };
984 :
985 : GF_FilterRegister FFMuxRegister = {
986 : .name = "ffmx",
987 : .version = LIBAVFORMAT_IDENT,
988 : GF_FS_SET_DESCRIPTION("FFMPEG muxer")
989 :
990 : GF_FS_SET_HELP("Mulitiplexes files and open output protocols using FFMPEG.\n"
991 : "See FFMPEG documentation (https://ffmpeg.org/documentation.html) for more details.\n"
992 : "To list all supported muxers for your GPAC build, use `gpac -h ffmx:*`."
993 : "This will list both supported output formats and protocols.\n"
994 : "Output protocols are listed with `Description: Output protocol`, and the subclass name identitfes the protocol scheme.\n"
995 : "For example, if `ffmx:rtmp` is listed as output protocol, this means `rtmp://` destination URLs are supported.\n"
996 : "\n"
997 : "Some URL formats may not be sufficient to derive the multiplexing format, you must then use [-ffmt]() to specify the desired format.\n"
998 : "\n"
999 : "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"
1000 : "The filter can however use template names for its output, using the first input PID to resolve the final name.\n"
1001 : "The filter watches the property `FileNumber` on incoming packets to create new files.\n"
1002 : )
1003 : .private_size = sizeof(GF_FFMuxCtx),
1004 : SETCAPS(FFMuxCaps),
1005 : .initialize = ffmx_initialize,
1006 : .finalize = ffmx_finalize,
1007 : .configure_pid = ffmx_configure_pid,
1008 : .process = ffmx_process,
1009 : .update_arg = ffmx_update_arg,
1010 : .probe_url = ffmx_probe_url,
1011 : .flags = GF_FS_REG_META,
1012 : .max_extra_pids = (u32) -1,
1013 :
1014 :
1015 : //use lowest priorty, so that we still use our default built-in muxers
1016 : .priority = 255
1017 : };
1018 :
1019 : #define OFFS(_n) #_n, offsetof(GF_FFMuxCtx, _n)
1020 :
1021 :
1022 : static const GF_FilterArgs FFMuxArgs[] =
1023 : {
1024 : { OFFS(dst), "location of destination file or remote URL", GF_PROP_NAME, NULL, NULL, 0},
1025 : { OFFS(start), "set playback start offset. Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, 0},
1026 : { OFFS(speed), "set playback speed. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, 0},
1027 : { OFFS(interleave), "write frame in interleave mode", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
1028 : { OFFS(nodisc), "ignore stream configuration changes while muxing, may result in broken streams", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1029 : { OFFS(mime), "set mime type for graph resolution", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
1030 : { OFFS(ffiles), "force complete files to be created for each segment in DASH modes", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1031 : { OFFS(ffmt), "force ffmpeg output format for the given URL", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1032 : { OFFS(block_size), "block size used to read file when using avio context", GF_PROP_UINT, "4096", NULL, GF_FS_ARG_HINT_EXPERT},
1033 : { "*", -1, "any possible options defined for AVFormatContext and sub-classes. See `gpac -hx ffmx` and `gpac -hx ffmx:*`", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_META},
1034 : {0}
1035 : };
1036 :
1037 : const int FFMX_STATIC_ARGS = (sizeof (FFMuxArgs) / sizeof (GF_FilterArgs)) - 1;
1038 :
1039 2877 : const GF_FilterRegister *ffmx_register(GF_FilterSession *session)
1040 : {
1041 2877 : ffmpeg_build_register(session, &FFMuxRegister, FFMuxArgs, FFMX_STATIC_ARGS, FF_REG_TYPE_MUX);
1042 2877 : return &FFMuxRegister;
1043 : }
1044 :
1045 : #else
1046 : #include <gpac/filters.h>
1047 : const GF_FilterRegister *ffmx_register(GF_FilterSession *session)
1048 : {
1049 : return NULL;
1050 : }
1051 : #endif
1052 :
|