LCOV - code coverage report
Current view: top level - filters - ff_mx.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 345 498 69.3 %
Date: 2021-04-29 23:48:07 Functions: 14 14 100.0 %

          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             : 

Generated by: LCOV version 1.13