LCOV - code coverage report
Current view: top level - filters - mux_avi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 187 269 69.5 %
Date: 2021-04-29 23:48:07 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.13