LCOV - code coverage report
Current view: top level - filters - out_file.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 257 319 80.6 %
Date: 2021-04-29 23:48:07 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2017-2020
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / generic FILE 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             : #include <gpac/xml.h>
      30             : 
      31             : enum
      32             : {
      33             :         FOUT_CAT_NONE = 0,
      34             :         FOUT_CAT_AUTO,
      35             :         FOUT_CAT_ALL
      36             : };
      37             : 
      38             : typedef struct
      39             : {
      40             :         //options
      41             :         Double start, speed;
      42             :         char *dst, *mime, *ext;
      43             :         Bool append, dynext, ow, redund;
      44             :         u32 cat;
      45             :         u32 mvbk;
      46             : 
      47             :         //only one input pid
      48             :         GF_FilterPid *pid;
      49             : 
      50             :         FILE *file;
      51             :         Bool is_std;
      52             :         u64 nb_write;
      53             :         Bool use_templates;
      54             :         GF_FilterCapability in_caps[2];
      55             :         char szExt[10];
      56             :         char szFileName[GF_MAX_PATH];
      57             : 
      58             :         Bool patch_blocks;
      59             :         Bool is_null;
      60             :         GF_Err is_error;
      61             :         u32 dash_mode;
      62             :         u64 offset_at_seg_start;
      63             :         const char *original_url;
      64             :         GF_FileIO *gfio_ref;
      65             : 
      66             :         FILE *hls_chunk;
      67             : } GF_FileOutCtx;
      68             : 
      69             : #ifdef WIN32
      70             : #include <io.h>
      71             : #include <fcntl.h>
      72             : #endif //WIN32
      73             : 
      74             : static void fileout_close_hls_chunk(GF_FileOutCtx *ctx, Bool final_flush)
      75             : {
      76        7199 :         if (!ctx->hls_chunk) return;
      77           4 :         gf_fclose(ctx->hls_chunk);
      78           4 :         ctx->hls_chunk = NULL;
      79             : }
      80             : 
      81       12537 : static GF_Err fileout_open_close(GF_FileOutCtx *ctx, const char *filename, const char *ext, u32 file_idx, Bool explicit_overwrite, char *file_suffix)
      82             : {
      83       12537 :         if (ctx->file && !ctx->is_std) {
      84        4976 :                 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileOut] closing output file %s\n", ctx->szFileName));
      85        4976 :                 gf_fclose(ctx->file);
      86             : 
      87             :                 fileout_close_hls_chunk(ctx, GF_FALSE);
      88             :         }
      89       12537 :         ctx->file = NULL;
      90             : 
      91       12537 :         if (!filename)
      92             :                 return GF_OK;
      93             : 
      94        4978 :         if (!strcmp(filename, "std")) ctx->is_std = GF_TRUE;
      95        4978 :         else if (!strcmp(filename, "stdout")) ctx->is_std = GF_TRUE;
      96        4976 :         else ctx->is_std = GF_FALSE;
      97             : 
      98        4978 :         if (ctx->is_std) {
      99           2 :                 ctx->file = stdout;
     100             : #ifdef WIN32
     101             :                 _setmode(_fileno(stdout), _O_BINARY);
     102             : #endif
     103             : 
     104             :         } else {
     105             :                 char szFinalName[GF_MAX_PATH];
     106        4976 :                 Bool append = ctx->append;
     107             :                 const char *url = filename;
     108             : 
     109        4976 :                 if (!strncmp(filename, "gfio://", 7))
     110           5 :                         url = gf_fileio_translate_url(filename);
     111             : 
     112        4976 :                 if (ctx->dynext) {
     113         272 :                         const char *has_ext = gf_file_ext_start(url);
     114             : 
     115             :                         strcpy(szFinalName, url);
     116         272 :                         if (!has_ext && ext) {
     117             :                                 strcat(szFinalName, ".");
     118             :                                 strcat(szFinalName, ext);
     119             :                         }
     120             :                 } else {
     121             :                         strcpy(szFinalName, url);
     122             :                 }
     123             : 
     124        4976 :                 if (ctx->use_templates) {
     125             :                         GF_Err e;
     126             :                         char szName[GF_MAX_PATH];
     127             :                         assert(ctx->dst);
     128         364 :                         if (!strcmp(filename, ctx->dst)) {
     129             :                                 strcpy(szName, szFinalName);
     130         364 :                                 e = gf_filter_pid_resolve_file_template(ctx->pid, szName, szFinalName, file_idx, file_suffix);
     131             :                         } else {
     132             :                                 char szFileName[GF_MAX_PATH];
     133             :                                 strcpy(szFileName, szFinalName);
     134             :                                 strcpy(szName, ctx->dst);
     135           0 :                                 e = gf_filter_pid_resolve_file_template_ex(ctx->pid, szName, szFinalName, file_idx, file_suffix, szFileName);
     136             :                         }
     137         364 :                         if (e) {
     138           0 :                                 return ctx->is_error = e;
     139             :                         }
     140             :                 }
     141             : 
     142        4976 :                 if (!gf_file_exists(szFinalName)) append = GF_FALSE;
     143             : 
     144        4976 :                 if (!strcmp(szFinalName, ctx->szFileName) && (ctx->cat==FOUT_CAT_AUTO))
     145             :                         append = GF_TRUE;
     146             : 
     147        4976 :                 if (!ctx->ow && gf_file_exists(szFinalName) && !append) {
     148             :                         char szRes[21];
     149             :                         s32 res;
     150             : 
     151           0 :                         fprintf(stderr, "File %s already exist - override (y/n/a) ?:", szFinalName);
     152           0 :                         res = scanf("%20s", szRes);
     153           0 :                         if (!res || (szRes[0] == 'n') || (szRes[0] == 'N')) {
     154           0 :                                 return ctx->is_error = GF_IO_ERR;
     155             :                         }
     156           0 :                         if ((szRes[0] == 'a') || (szRes[0] == 'A')) ctx->ow = GF_TRUE;
     157             :                 }
     158             : 
     159        4976 :                 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileOut] opening output file %s\n", szFinalName));
     160        4976 :                 ctx->file = gf_fopen_ex(szFinalName, ctx->original_url, append ? "a+b" : "w+b");
     161             : 
     162        4976 :                 if (!strcmp(szFinalName, ctx->szFileName) && !append && ctx->nb_write && !explicit_overwrite) {
     163          47 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[FileOut] re-opening in write mode output file %s, content overwrite (use `cat` option to enable append)\n", szFinalName));
     164             :                 }
     165             :                 strcpy(ctx->szFileName, szFinalName);
     166             :         }
     167        4978 :         ctx->nb_write = 0;
     168        4978 :         if (!ctx->file) {
     169           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] cannot open output file %s\n", ctx->szFileName));
     170           0 :                 return ctx->is_error = GF_IO_ERR;;
     171             :         }
     172             : 
     173             :         return GF_OK;
     174             : }
     175             : 
     176        1957 : static void fileout_setup_file(GF_FileOutCtx *ctx, Bool explicit_overwrite)
     177             : {
     178        1957 :         const char *dst = ctx->dst;
     179             :         const GF_PropertyValue *p, *ext;
     180        1957 :         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_OUTPATH);
     181        1957 :         ext = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_FILE_EXT);
     182             : 
     183        1957 :         if (p && p->value.string) {
     184          13 :                 fileout_open_close(ctx, p->value.string, (ext && ctx->dynext) ? ext->value.string : NULL, 0, explicit_overwrite, NULL);
     185          13 :                 return;
     186             :         }
     187        1944 :         if (!dst) {
     188           0 :                 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_FILEPATH);
     189           0 :                 if (p && p->value.string) {
     190             :                         dst = p->value.string;
     191           0 :                         char *sep = strstr(dst, "://");
     192           0 :                         if (sep) {
     193           0 :                                 dst = strchr(sep+3, '/');
     194           0 :                                 if (!dst) return;
     195             :                         } else {
     196           0 :                                 if (!strncmp(dst, "./", 2)) dst+= 2;
     197           0 :                                 else if (!strncmp(dst, ".\\", 2)) dst+= 2;
     198           0 :                                 else if (!strncmp(dst, "../", 3)) dst+= 3;
     199           0 :                                 else if (!strncmp(dst, "..\\", 3)) dst+= 3;
     200             :                         }
     201             :                 }
     202             :         }
     203        1944 :         if (ctx->dynext) {
     204          30 :                 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PCK_FILENUM);
     205          30 :                 if (!p) {
     206          30 :                         if (ext && ext->value.string) {
     207          30 :                                 fileout_open_close(ctx, dst, ext->value.string, 0, explicit_overwrite, NULL);
     208             :                         }
     209             :                 }
     210        1914 :         } else if (ctx->dst) {
     211        1914 :                 fileout_open_close(ctx, ctx->dst, NULL, 0, explicit_overwrite, NULL);
     212             :         } else {
     213           0 :                 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_FILEPATH);
     214           0 :                 if (!p) p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_URL);
     215           0 :                 if (p && p->value.string)
     216           0 :                         fileout_open_close(ctx, p->value.string, NULL, 0, explicit_overwrite, NULL);
     217             :         }
     218             : }
     219        2886 : static GF_Err fileout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     220             : {
     221             :         const GF_PropertyValue *p;
     222        2886 :         GF_FileOutCtx *ctx = (GF_FileOutCtx *) gf_filter_get_udta(filter);
     223        2886 :         if (is_remove) {
     224          10 :                 ctx->pid = NULL;
     225          10 :                 fileout_open_close(ctx, NULL, NULL, 0, GF_FALSE, NULL);
     226          10 :                 return GF_OK;
     227             :         }
     228        2876 :         gf_filter_pid_check_caps(pid);
     229             : 
     230        2876 :         if (!ctx->pid) {
     231             :                 GF_FilterEvent evt;
     232        1946 :                 gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "FileOut");
     233        1946 :                 gf_filter_pid_send_event(pid, &evt);
     234             :         }
     235        2876 :         ctx->pid = pid;
     236             : 
     237        2876 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DISABLE_PROGRESSIVE);
     238        2876 :         if (p && p->value.uint) ctx->patch_blocks = GF_TRUE;
     239             : 
     240        2876 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MODE);
     241        2876 :         if (p && p->value.uint) ctx->dash_mode = 1;
     242             :         return GF_OK;
     243             : }
     244             : 
     245        2223 : static GF_Err fileout_initialize(GF_Filter *filter)
     246             : {
     247             :         char *ext=NULL, *sep;
     248             :         const char *dst;
     249        2223 :         GF_FileOutCtx *ctx = (GF_FileOutCtx *) gf_filter_get_udta(filter);
     250             : 
     251        2223 :         if (!ctx || !ctx->dst) return GF_OK;
     252             : 
     253        2223 :         if (!ctx->mvbk)
     254           0 :                 ctx->mvbk = 1;
     255             : 
     256        2223 :         if (strnicmp(ctx->dst, "file:/", 6) && strnicmp(ctx->dst, "gfio:/", 6) && strstr(ctx->dst, "://"))  {
     257           0 :                 gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
     258           0 :                 return GF_NOT_SUPPORTED;
     259             :         }
     260        2223 :         if (!stricmp(ctx->dst, "null") ) {
     261           3 :                 ctx->is_null = GF_TRUE;
     262             :                 //null and no format specified, we accept any kind
     263           3 :                 if (!ctx->ext) {
     264           0 :                         ctx->in_caps[0].code = GF_PROP_PID_STREAM_TYPE;
     265           0 :                         ctx->in_caps[0].val = PROP_UINT(GF_STREAM_UNKNOWN);
     266           0 :                         ctx->in_caps[0].flags = GF_CAPS_INPUT_EXCLUDED;
     267           0 :                         gf_filter_override_caps(filter, ctx->in_caps, 1);
     268           0 :                         return GF_OK;
     269             :                 }
     270             :         }
     271        2223 :         if (!strncmp(ctx->dst, "gfio://", 7)) {
     272             :                 GF_Err e;
     273           3 :                 ctx->gfio_ref = gf_fileio_open_url(gf_fileio_from_url(ctx->dst), NULL, "ref", &e);
     274           3 :                 if (!ctx->gfio_ref) {
     275           0 :                         gf_filter_setup_failure(filter, e);
     276           0 :                         return e;
     277             :                 }
     278           3 :                 dst = gf_fileio_translate_url(ctx->dst);
     279           3 :                 ctx->original_url = ctx->dst;
     280             :         } else {
     281             :                 dst = ctx->dst;
     282             :         }
     283             : 
     284        2223 :         sep = dst ? strchr(dst, '$') : NULL;
     285        2223 :         if (sep) {
     286          80 :                 sep = strchr(sep+1, '$');
     287          80 :                 if (sep) ctx->use_templates = GF_TRUE;
     288             :         }
     289             : 
     290        2223 :         if (ctx->dynext) return GF_OK;
     291             : 
     292        2166 :         if (ctx->ext) ext = ctx->ext;
     293        2156 :         else if (dst) {
     294        2156 :                 ext = gf_file_ext_start(dst);
     295        2156 :                 if (!ext) ext = ".*";
     296        2156 :                 ext += 1;
     297             :         }
     298             : 
     299        2166 :         if (!ext && !ctx->mime) {
     300           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] No extension provided nor mime type for output file %s, cannot infer format\n", ctx->dst));
     301             :                 return GF_NOT_SUPPORTED;
     302             :         }
     303             :         //static cap, streamtype = file
     304        2166 :         ctx->in_caps[0].code = GF_PROP_PID_STREAM_TYPE;
     305        2166 :         ctx->in_caps[0].val = PROP_UINT(GF_STREAM_FILE);
     306        2166 :         ctx->in_caps[0].flags = GF_CAPS_INPUT_STATIC;
     307             : 
     308        2166 :         if (ctx->mime) {
     309         318 :                 ctx->in_caps[1].code = GF_PROP_PID_MIME;
     310         318 :                 ctx->in_caps[1].val = PROP_NAME( ctx->mime );
     311         318 :                 ctx->in_caps[1].flags = GF_CAPS_INPUT;
     312             :         } else {
     313        1848 :                 strncpy(ctx->szExt, ext, 9);
     314        1848 :                 ctx->szExt[9] = 0;
     315        1848 :                 strlwr(ctx->szExt);
     316        1848 :                 ctx->in_caps[1].code = GF_PROP_PID_FILE_EXT;
     317        1848 :                 ctx->in_caps[1].val = PROP_NAME( ctx->szExt );
     318        1848 :                 ctx->in_caps[1].flags = GF_CAPS_INPUT;
     319             :         }
     320        2166 :         gf_filter_override_caps(filter, ctx->in_caps, 2);
     321             : 
     322        2166 :         return GF_OK;
     323             : }
     324             : 
     325        2223 : static void fileout_finalize(GF_Filter *filter)
     326             : {
     327             :         GF_Err e;
     328        2223 :         GF_FileOutCtx *ctx = (GF_FileOutCtx *) gf_filter_get_udta(filter);
     329             : 
     330             :         fileout_close_hls_chunk(ctx, GF_TRUE);
     331             : 
     332        2223 :         fileout_open_close(ctx, NULL, NULL, 0, GF_FALSE, NULL);
     333        2223 :         if (ctx->gfio_ref)
     334           3 :                 gf_fileio_open_url((GF_FileIO *)ctx->gfio_ref, NULL, "unref", &e);
     335        2223 : }
     336             : 
     337      176699 : static GF_Err fileout_process(GF_Filter *filter)
     338             : {
     339             :         GF_FilterPacket *pck;
     340             :         const GF_PropertyValue *fname, *p;
     341             :         Bool start, end;
     342             :         const u8 *pck_data;
     343             :         u32 pck_size, nb_write;
     344      176699 :         GF_FileOutCtx *ctx = (GF_FileOutCtx *) gf_filter_get_udta(filter);
     345             : 
     346      176699 :         if (ctx->is_error) {
     347             :                 GF_Err e = ctx->is_error;
     348           0 :                 if (e != GF_EOS) {
     349           0 :                         gf_filter_pid_set_discard(ctx->pid, GF_TRUE);
     350           0 :                         ctx->is_error = GF_EOS;
     351             :                 }
     352             :                 return e;
     353             :         }
     354             : 
     355      176699 :         pck = gf_filter_pid_get_packet(ctx->pid);
     356      176699 :         if (!pck) {
     357       19774 :                 if (gf_filter_pid_is_eos(ctx->pid)) {
     358        1869 :                         if (gf_filter_reporting_enabled(filter)) {
     359             :                                 char szStatus[1024];
     360           0 :                                 snprintf(szStatus, 1024, "%s: done - wrote "LLU" bytes", gf_file_basename(ctx->szFileName), ctx->nb_write);
     361           0 :                                 gf_filter_update_status(filter, 10000, szStatus);
     362             :                         }
     363             : 
     364        1869 :                         if (ctx->dash_mode && ctx->file) {
     365             :                                 GF_FilterEvent evt;
     366           6 :                                 GF_FEVT_INIT(evt, GF_FEVT_SEGMENT_SIZE, ctx->pid);
     367             :                                 evt.seg_size.seg_url = NULL;
     368             : 
     369           6 :                                 if (ctx->dash_mode==1) {
     370           0 :                                         evt.seg_size.is_init = GF_TRUE;
     371           0 :                                         ctx->dash_mode = 2;
     372             :                                         evt.seg_size.media_range_start = 0;
     373             :                                         evt.seg_size.media_range_end = 0;
     374           0 :                                         gf_filter_pid_send_event(ctx->pid, &evt);
     375             :                                 } else {
     376             :                                         evt.seg_size.is_init = GF_FALSE;
     377           6 :                                         evt.seg_size.media_range_start = ctx->offset_at_seg_start;
     378           6 :                                         evt.seg_size.media_range_end = gf_ftell(ctx->file)-1;
     379           6 :                                         gf_filter_pid_send_event(ctx->pid, &evt);
     380             :                                 }
     381             :                         }
     382        1869 :                         fileout_open_close(ctx, NULL, NULL, 0, GF_FALSE, NULL);
     383        1869 :                         return GF_EOS;
     384             :                 }
     385             :                 return GF_OK;
     386             :         }
     387             : 
     388      156925 :         gf_filter_pck_get_framing(pck, &start, &end);
     389      156925 :         if (!ctx->redund) {
     390      156925 :                 u32 dep_flags = gf_filter_pck_get_dependency_flags(pck);
     391             :                 //redundant packet, do not store
     392      156925 :                 if ((dep_flags & 0x3) == 1) {
     393        1894 :                         gf_filter_pid_drop_packet(ctx->pid);
     394        1894 :                         return GF_OK;
     395             :                 }
     396             :         }
     397             : 
     398      155031 :         if (ctx->is_null) {
     399         868 :                 if (start) {
     400             :                         u32 fnum=0;
     401             :                         const char *filename=NULL;
     402           3 :                         p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
     403           3 :                         if (p) fnum = p->value.uint;
     404           3 :                         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_URL);
     405           3 :                         if (!p) p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_OUTPATH);
     406           3 :                         if (!p) p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
     407           3 :                         if (p) filename = p->value.string;
     408           3 :                         if (filename) {
     409           3 :                                 strcpy(ctx->szFileName, filename);
     410             :                         } else {
     411           0 :                                 sprintf(ctx->szFileName, "%d", fnum);
     412             :                         }
     413           3 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileOut] null open (file name is %s)\n", ctx->szFileName));
     414             :                 }
     415         868 :                 if (end) {
     416           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileOut] null close (file name was %s)\n", ctx->szFileName));
     417             :                 }
     418         868 :                 gf_filter_pid_drop_packet(ctx->pid);
     419         868 :                 return fileout_process(filter);
     420             :         }
     421             : 
     422      154163 :         if (ctx->file && start && (ctx->cat==FOUT_CAT_ALL))
     423           0 :                 start = GF_FALSE;
     424             : 
     425      154163 :         if (ctx->dash_mode) {
     426        1645 :                 p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
     427        1645 :                 if (p) {
     428             :                         GF_FilterEvent evt;
     429             : 
     430          66 :                         GF_FEVT_INIT(evt, GF_FEVT_SEGMENT_SIZE, ctx->pid);
     431             :                         evt.seg_size.seg_url = NULL;
     432             : 
     433          66 :                         if (ctx->dash_mode==1) {
     434           6 :                                 evt.seg_size.is_init = GF_TRUE;
     435           6 :                                 ctx->dash_mode = 2;
     436             :                                 evt.seg_size.media_range_start = 0;
     437             :                                 evt.seg_size.media_range_end = 0;
     438           6 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
     439             :                         } else {
     440             :                                 evt.seg_size.is_init = GF_FALSE;
     441          60 :                                 evt.seg_size.media_range_start = ctx->offset_at_seg_start;
     442          60 :                                 evt.seg_size.media_range_end = gf_ftell(ctx->file)-1;
     443          60 :                                 ctx->offset_at_seg_start = evt.seg_size.media_range_end+1;
     444          60 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
     445             :                         }
     446          66 :                         if ( gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME))
     447          22 :                                 start = GF_TRUE;
     448             :                 }
     449             :         }
     450             : 
     451      154163 :         if (start) {
     452             :                 const GF_PropertyValue *ext, *fnum, *fsuf;
     453             :                 Bool explicit_overwrite = GF_FALSE;
     454             :                 const char *name = NULL;
     455             :                 fname = ext = NULL;
     456             :                 //file num increased per packet, open new file
     457        4979 :                 fnum = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
     458        4979 :                 if (fnum) {
     459        2883 :                         fname = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_OUTPATH);
     460        2883 :                         ext = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_FILE_EXT);
     461        2883 :                         if (!fname) name = ctx->dst;
     462             :                 }
     463             :                 //filename change at packet start, open new file
     464        4979 :                 if (!fname) fname = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
     465        4979 :                 if (!fname) fname = gf_filter_pck_get_property(pck, GF_PROP_PID_OUTPATH);
     466        4979 :                 if (!ext) ext = gf_filter_pck_get_property(pck, GF_PROP_PID_FILE_EXT);
     467        4979 :                 if (fname) name = fname->value.string;
     468             : 
     469        4979 :                 fsuf = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
     470             : 
     471        4979 :                 if (end && gf_filter_pck_get_seek_flag(pck))
     472             :                         explicit_overwrite = GF_TRUE;
     473             : 
     474        4979 :                 if (name) {
     475        3021 :                         fileout_open_close(ctx, name, ext ? ext->value.string : NULL, fnum ? fnum->value.uint : 0, explicit_overwrite, fsuf ? fsuf->value.string : NULL);
     476        1958 :                 } else if (!ctx->file) {
     477        1957 :                         fileout_setup_file(ctx, explicit_overwrite);
     478             :                 }
     479             :         }
     480             : 
     481      154163 :         p = gf_filter_pck_get_property(pck, GF_PROP_PCK_HLS_FRAG_NUM);
     482      154163 :         if (p) {
     483             :                 char szHLSChunk[GF_MAX_PATH+21];
     484          40 :                 snprintf(szHLSChunk, GF_MAX_PATH+20, "%s.%d", ctx->szFileName, p->value.uint);
     485          40 :                 if (ctx->hls_chunk) gf_fclose(ctx->hls_chunk);
     486          40 :                 ctx->hls_chunk = gf_fopen_ex(szHLSChunk, ctx->original_url, "w+b");
     487             :         }
     488             : 
     489      154163 :         pck_data = gf_filter_pck_get_data(pck, &pck_size);
     490      154163 :         if (ctx->file) {
     491      154163 :                 GF_FilterFrameInterface *hwf = gf_filter_pck_get_frame_interface(pck);
     492      154163 :                 if (pck_data) {
     493      146883 :                         if (ctx->patch_blocks && gf_filter_pck_get_seek_flag(pck)) {
     494           4 :                                 u64 bo = gf_filter_pck_get_byte_offset(pck);
     495           4 :                                 if (ctx->is_std) {
     496           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Cannot patch file, output is stdout\n"));
     497           4 :                                 } else if (bo==GF_FILTER_NO_BO) {
     498           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Cannot patch file, wrong byte offset\n"));
     499             :                                 } else {
     500           4 :                                         u32 ilaced = gf_filter_pck_get_interlaced(pck);
     501           4 :                                         u64 pos = ctx->nb_write;
     502           4 :                                         if (ctx->file) {
     503             :                                                 assert( ctx->nb_write == gf_ftell(ctx->file) );
     504             :                                         }
     505             : 
     506             :                                         //we are inserting a block: write dummy bytes at end and move bytes
     507           4 :                                         if (ilaced) {
     508             :                                                 u8 *block;
     509             :                                                 u64 cur_r, cur_w;
     510           1 :                                                 nb_write = (u32) gf_fwrite(pck_data, pck_size, ctx->file);
     511             : 
     512           1 :                                                 if (nb_write!=pck_size) {
     513           0 :                                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, pck_size));
     514             :                                                 }
     515           1 :                                                 cur_w = gf_ftell(ctx->file);
     516             : 
     517           1 :                                                 gf_fseek(ctx->file, pos, SEEK_SET);
     518             : 
     519             :                                                 cur_r = pos;
     520             :                                                 pos = cur_w;
     521           1 :                                                 block = gf_malloc(ctx->mvbk);
     522           1 :                                                 if (!block) {
     523           0 :                                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] unable to allocate block of %d bytes\n", ctx->mvbk));
     524             :                                                 } else {
     525          17 :                                                         while (cur_r > bo) {
     526          16 :                                                                 u32 move_bytes = ctx->mvbk;
     527          16 :                                                                 if (cur_r - bo < move_bytes)
     528           1 :                                                                         move_bytes = (u32) (cur_r - bo);
     529             : 
     530          16 :                                                                 gf_fseek(ctx->file, cur_r - move_bytes, SEEK_SET);
     531          16 :                                                                 nb_write = (u32) gf_fread(block, (size_t) move_bytes, ctx->file);
     532             : 
     533          16 :                                                                 if (nb_write!=move_bytes) {
     534           0 :                                                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Read error, got %d bytes but had %d to read\n", nb_write, move_bytes));
     535             :                                                                 }
     536             : 
     537          16 :                                                                 gf_fseek(ctx->file, cur_w - move_bytes, SEEK_SET);
     538          16 :                                                                 nb_write = (u32) gf_fwrite(block, (size_t) move_bytes, ctx->file);
     539             : 
     540          16 :                                                                 if (nb_write!=move_bytes) {
     541           0 :                                                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, move_bytes));
     542             :                                                                 }
     543             :                                                                 cur_r -= move_bytes;
     544             :                                                                 cur_w -= move_bytes;
     545             :                                                         }
     546           1 :                                                         gf_free(block);
     547             :                                                 }
     548             :                                         }
     549             : 
     550           4 :                                         gf_fseek(ctx->file, bo, SEEK_SET);
     551           4 :                                         nb_write = (u32) gf_fwrite(pck_data, pck_size, ctx->file);
     552           4 :                                         gf_fseek(ctx->file, pos, SEEK_SET);
     553             : 
     554           4 :                                         if (nb_write!=pck_size) {
     555           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, pck_size));
     556             :                                         }
     557             :                                 }
     558             :                         } else {
     559      146879 :                                 nb_write = (u32) gf_fwrite(pck_data, pck_size, ctx->file);
     560      146879 :                                 if (nb_write!=pck_size) {
     561           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, pck_size));
     562             :                                 }
     563      146879 :                                 ctx->nb_write += nb_write;
     564             : 
     565      146879 :                                 if (ctx->hls_chunk) {
     566          80 :                                         nb_write = (u32) gf_fwrite(pck_data, pck_size, ctx->hls_chunk);
     567          80 :                                         if (nb_write!=pck_size) {
     568           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, pck_size));
     569             :                                         }
     570             :                                 }
     571             :                         }
     572        7280 :                 } else if (hwf) {
     573             :                         u32 w, h, stride, stride_uv, pf;
     574             :                         u32 nb_planes, uv_height;
     575        7280 :                         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_WIDTH);
     576        7280 :                         w = p ? p->value.uint : 0;
     577        7280 :                         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_HEIGHT);
     578        7280 :                         h = p ? p->value.uint : 0;
     579        7280 :                         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_PIXFMT);
     580        7280 :                         pf = p ? p->value.uint : 0;
     581             : 
     582        7280 :                         stride = stride_uv = 0;
     583             : 
     584        7280 :                         if (gf_pixel_get_size_info(pf, w, h, NULL, &stride, &stride_uv, &nb_planes, &uv_height) == GF_TRUE) {
     585             :                                 u32 i;
     586       22596 :                                 for (i=0; i<nb_planes; i++) {
     587             :                                         u32 j, write_h, lsize;
     588             :                                         const u8 *out_ptr;
     589        7658 :                                         u32 out_stride = i ? stride_uv : stride;
     590        7658 :                                         GF_Err e = hwf->get_plane(hwf, i, &out_ptr, &out_stride);
     591        7658 :                                         if (e) {
     592           0 :                                                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Failed to fetch plane data from hardware frame, cannot write\n"));
     593           0 :                                                 break;
     594             :                                         }
     595        7658 :                                         if (i) {
     596         378 :                                                 write_h = uv_height;
     597         378 :                                                 lsize = stride_uv;
     598             :                                         } else {
     599             :                                                 write_h = h;
     600        7280 :                                                 lsize = stride;
     601             :                                         }
     602     1353578 :                                         for (j=0; j<write_h; j++) {
     603     1345920 :                                                 nb_write = (u32) gf_fwrite(out_ptr, lsize, ctx->file);
     604     1345920 :                                                 if (nb_write!=lsize) {
     605           0 :                                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] Write error, wrote %d bytes but had %d to write\n", nb_write, lsize));
     606             :                                                 }
     607     1345920 :                                                 ctx->nb_write += nb_write;
     608     1345920 :                                                 out_ptr += out_stride;
     609             :                                         }
     610             :                                 }
     611             :                         }
     612             :                 } else {
     613           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[FileOut] No data associated with packet, cannot write\n"));
     614             :                 }
     615             :         } else {
     616           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileOut] output file handle is not opened, discarding %d bytes\n", pck_size));
     617             :         }
     618      154163 :         gf_filter_pid_drop_packet(ctx->pid);
     619      154163 :         if (end && !ctx->cat) {
     620        3457 :                 fileout_open_close(ctx, NULL, NULL, 0, GF_FALSE, NULL);
     621             :         }
     622      154163 :         if (gf_filter_reporting_enabled(filter)) {
     623             :                 char szStatus[1024];
     624           0 :                 snprintf(szStatus, 1024, "%s: wrote % 16"LLD_SUF" bytes", gf_file_basename(ctx->szFileName), (s64) ctx->nb_write);
     625           0 :                 gf_filter_update_status(filter, -1, szStatus);
     626             :         }
     627             :         return GF_OK;
     628             : }
     629             : 
     630       60135 : static Bool fileout_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
     631             : {
     632       60135 :         if (evt->base.type==GF_FEVT_FILE_DELETE) {
     633           1 :                 GF_FileOutCtx *ctx = (GF_FileOutCtx *) gf_filter_get_udta(filter);
     634           1 :                 if (ctx->is_null) {
     635           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileOut] null delete (file name was %s)\n", evt->file_del.url));
     636             :                 } else {
     637           1 :                         gf_file_delete(evt->file_del.url);
     638             :                 }
     639             :                 return GF_TRUE;
     640             :         }
     641             :         return GF_FALSE;
     642             : }
     643        2793 : static GF_FilterProbeScore fileout_probe_url(const char *url, const char *mime)
     644             : {
     645        2793 :         if (strstr(url, "://")) {
     646             : 
     647          84 :                 if (!strnicmp(url, "file://", 7)) return GF_FPROBE_MAYBE_SUPPORTED;
     648          84 :                 if (!strnicmp(url, "gfio://", 7)) {
     649           6 :                         if (!gf_fileio_write_mode(gf_fileio_from_url(url)))
     650             :                                 return GF_FPROBE_NOT_SUPPORTED;
     651           6 :                         return GF_FPROBE_MAYBE_SUPPORTED;
     652             :                 }
     653             :                 return GF_FPROBE_NOT_SUPPORTED;
     654             :         }
     655             :         return GF_FPROBE_MAYBE_SUPPORTED;
     656             : }
     657             : 
     658             : 
     659             : #define OFFS(_n)        #_n, offsetof(GF_FileOutCtx, _n)
     660             : 
     661             : static const GF_FilterArgs FileOutArgs[] =
     662             : {
     663             :         { OFFS(dst), "location of destination file - see filter help ", GF_PROP_NAME, NULL, NULL, 0},
     664             :         { OFFS(append), "open in append mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     665             :         { OFFS(dynext), "indicate the file extension is set by filter chain, not dst", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     666             :         { 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},
     667             :         { OFFS(speed), "set playback speed when vsync is on. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, 0},
     668             :         { OFFS(ext), "set extension for graph resolution, regardless of file extension", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
     669             :         { OFFS(mime), "set mime type for graph resolution", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
     670             :         { OFFS(cat), "cat each file of input pid rather than creating one file per filename\n"
     671             :                         "- none: never cat files\n"
     672             :                         "- auto: only cat if files have same names\n"
     673             :                         "- all: always cat regardless of file names"
     674             :         , GF_PROP_UINT, "none", "none|auto|all", GF_FS_ARG_HINT_ADVANCED},
     675             :         { OFFS(ow), "overwrite output if existing", GF_PROP_BOOL, "true", NULL, 0},
     676             :         { OFFS(mvbk), "block size used when moving parts of the file around in patch mode", GF_PROP_UINT, "8192", NULL, 0},
     677             :         { OFFS(redund), "keep redundant packet in output file", GF_PROP_BOOL, "false", NULL, 0},
     678             : 
     679             :         {0}
     680             : };
     681             : 
     682             : static const GF_FilterCapability FileOutCaps[] =
     683             : {
     684             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     685             :         CAP_STRING(GF_CAPS_INPUT,GF_PROP_PID_FILE_EXT, "*"),
     686             : };
     687             : 
     688             : 
     689             : GF_FilterRegister FileOutRegister = {
     690             :         .name = "fout",
     691             :         GF_FS_SET_DESCRIPTION("File output")
     692             :         GF_FS_SET_HELP("The file output filter is used to write output to disk, and does not produce any output PID.\n"
     693             :                 "It can work as a null sink when its destination is `null`, dropping all input packets. In this case it accepts ANY type of input pid, not just file ones.\n"
     694             :                 "In regular mode, the filter only accept pid of type file. It will dump to file incomming packets (stream type file), starting a new file for each packet having a __frame_start__ flag set, unless operating in [-cat]() mode.\n"
     695             :                 "If the output file name is `std` or `stdout`, writes to stdout.\n"
     696             :                 "The ouput file name can use gpac templating mechanism, see `gpac -h doc`."
     697             :                 "The filter watches the property `FileNumber` on incoming packets to create new files.\n"
     698             :         )
     699             :         .private_size = sizeof(GF_FileOutCtx),
     700             :         .args = FileOutArgs,
     701             :         SETCAPS(FileOutCaps),
     702             :         .probe_url = fileout_probe_url,
     703             :         .initialize = fileout_initialize,
     704             :         .finalize = fileout_finalize,
     705             :         .configure_pid = fileout_configure_pid,
     706             :         .process = fileout_process,
     707             :         .process_event = fileout_process_event
     708             : };
     709             : 
     710             : 
     711        2877 : const GF_FilterRegister *fileout_register(GF_FilterSession *session)
     712             : {
     713        2877 :         return &FileOutRegister;
     714             : }
     715             : 

Generated by: LCOV version 1.13