LCOV - code coverage report
Current view: top level - media_tools - media_export.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 503 773 65.1 %
Date: 2021-04-29 23:48:07 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Media Tools sub-project
       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/internal/media_dev.h>
      28             : #include <gpac/internal/isomedia_dev.h>
      29             : #include <gpac/mpegts.h>
      30             : #include <gpac/constants.h>
      31             : #include <gpac/filters.h>
      32             : 
      33             : #ifndef GPAC_DISABLE_MEDIA_EXPORT
      34             : 
      35             : #ifndef GPAC_DISABLE_AVILIB
      36             : #include <gpac/internal/avilib.h>
      37             : #endif
      38             : 
      39             : #ifndef GPAC_DISABLE_OGG
      40             : #include <gpac/internal/ogg.h>
      41             : #endif
      42             : 
      43             : #ifndef GPAC_DISABLE_VOBSUB
      44             : #include <gpac/internal/vobsub.h>
      45             : #endif
      46             : 
      47             : #ifndef GPAC_DISABLE_ZLIB
      48             : #include <zlib.h>
      49             : #endif
      50             : 
      51           1 : static GF_Err gf_export_message(GF_MediaExporter *dumper, GF_Err e, char *format, ...)
      52             : {
      53           1 :         if (dumper->flags & GF_EXPORT_PROBE_ONLY) return e;
      54             : 
      55             : #ifndef GPAC_DISABLE_LOG
      56           1 :         if (gf_log_tool_level_on(GF_LOG_AUTHOR, e ? GF_LOG_ERROR : GF_LOG_WARNING)) {
      57             :                 va_list args;
      58             :                 char szMsg[1024];
      59           1 :                 va_start(args, format);
      60             :                 vsnprintf(szMsg, 1024, format, args);
      61           1 :                 va_end(args);
      62           1 :                 GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_AUTHOR, ("%s\n", szMsg) );
      63             :         }
      64             : #endif
      65             :         return e;
      66             : }
      67             : 
      68             : #ifndef GPAC_DISABLE_AV_PARSERS
      69             : /*that's very very crude, we only support vorbis & theora in MP4 - this will need cleanup as soon as possible*/
      70           2 : static GF_Err gf_dump_to_ogg(GF_MediaExporter *dumper, char *szName, u32 track)
      71             : {
      72             : #ifdef GPAC_DISABLE_OGG
      73             :         return GF_NOT_SUPPORTED;
      74             : #else
      75             :         FILE *out;
      76             :         ogg_stream_state os;
      77             :         ogg_packet op;
      78             :         ogg_page og;
      79             :         u32 count, i, di, theora_kgs, nb_i, nb_p;
      80             :         Bool flush_first = GF_TRUE;
      81             :         GF_BitStream *bs;
      82             :         GF_ISOSample *samp;
      83           2 :         GF_ESD *esd = gf_isom_get_esd(dumper->file, track, 1);
      84             : 
      85             : 
      86             :         memset(&os, 0, sizeof(ogg_stream_state));
      87             :         memset(&og, 0, sizeof(ogg_page));
      88             :         memset(&op, 0, sizeof(ogg_packet));
      89             : 
      90           2 :         if (gf_sys_is_test_mode()) {
      91           2 :                 ogg_stream_init(&os, 1);
      92             :         } else {
      93           0 :                 gf_rand_init(GF_TRUE);
      94           0 :                 ogg_stream_init(&os, gf_rand());
      95             :         }
      96             : 
      97           2 :         out = szName ? gf_fopen(szName, "wb") : stdout;
      98           2 :         if (!out) return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
      99             : 
     100             :         theora_kgs = 0;
     101           2 :         bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ);
     102           2 :         if (esd->decoderConfig->objectTypeIndication==GF_CODECID_OPUS) {
     103             :                 GF_BitStream *bs_out;
     104           1 :                 GF_OpusSpecificBox *dops = (GF_OpusSpecificBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DOPS);
     105           1 :                 dops->size = gf_bs_read_u32(bs);
     106           1 :                 gf_bs_read_u32(bs);
     107           1 :                 gf_isom_box_read((GF_Box *)dops, bs);
     108           1 :                 bs_out = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
     109           1 :                 gf_bs_write_data(bs_out, "OpusHead", 8);
     110           1 :                 gf_bs_write_u8(bs_out, 1);//version
     111           1 :                 gf_bs_write_u8(bs_out, dops->OutputChannelCount);
     112           1 :                 gf_bs_write_u16_le(bs_out, dops->PreSkip);
     113           1 :                 gf_bs_write_u32_le(bs_out, dops->InputSampleRate);
     114           1 :                 gf_bs_write_u16_le(bs_out, dops->OutputGain);
     115           1 :                 gf_bs_write_u8(bs_out, dops->ChannelMappingFamily);
     116           1 :                 if (dops->ChannelMappingFamily) {
     117           1 :                         gf_bs_write_u8(bs_out, dops->StreamCount);
     118           1 :                         gf_bs_write_u8(bs_out, dops->CoupledCount);
     119           1 :                         gf_bs_write_data(bs, (char *) dops->ChannelMapping, dops->OutputChannelCount);
     120             :                 }
     121           1 :                 gf_isom_box_del((GF_Box*)dops);
     122             : 
     123           1 :                 gf_bs_get_content(bs_out, &op.packet, &op.bytes);
     124           1 :                 gf_bs_del(bs_out);
     125           1 :                 ogg_stream_packetin(&os, &op);
     126           1 :                 gf_free(op.packet);
     127           1 :                 op.packetno ++;
     128             : 
     129             :         } else {
     130           4 :                 while (gf_bs_available(bs)) {
     131           3 :                         op.bytes = gf_bs_read_u16(bs);
     132           3 :                         op.packet = (unsigned char*)gf_malloc(sizeof(char) * op.bytes);
     133           3 :                         gf_bs_read_data(bs, (char*)op.packet, op.bytes);
     134           3 :                         ogg_stream_packetin(&os, &op);
     135             : 
     136           3 :                         if (flush_first) {
     137           1 :                                 ogg_stream_pageout(&os, &og);
     138           1 :                                 gf_fwrite(og.header, og.header_len, out);
     139           1 :                                 gf_fwrite(og.body, og.body_len, out);
     140             :                                 flush_first = 0;
     141             : 
     142           1 :                                 if (esd->decoderConfig->objectTypeIndication==GF_CODECID_THEORA) {
     143             :                                         u32 kff;
     144           0 :                                         GF_BitStream *vbs = gf_bs_new((char*)op.packet, op.bytes, GF_BITSTREAM_READ);
     145           0 :                                         gf_bs_skip_bytes(vbs, 40);
     146           0 :                                         gf_bs_read_int(vbs, 6); /* quality */
     147           0 :                                         kff = 1 << gf_bs_read_int(vbs, 5);
     148           0 :                                         gf_bs_del(vbs);
     149             : 
     150             :                                         theora_kgs = 0;
     151           0 :                                         kff--;
     152           0 :                                         while (kff) {
     153           0 :                                                 theora_kgs ++;
     154           0 :                                                 kff >>= 1;
     155             :                                         }
     156             :                                 }
     157             :                         }
     158           3 :                         gf_free(op.packet);
     159           3 :                         op.packetno ++;
     160             :                 }
     161             :         }
     162           2 :         gf_bs_del(bs);
     163           2 :         gf_odf_desc_del((GF_Descriptor *)esd);
     164             : 
     165           5 :         while (ogg_stream_pageout(&os, &og)>0) {
     166           1 :                 gf_fwrite(og.header, og.header_len, out);
     167           1 :                 gf_fwrite(og.body, og.body_len, out);
     168             :         }
     169             : 
     170           2 :         op.granulepos = -1;
     171             : 
     172           2 :         count = gf_isom_get_sample_count(dumper->file, track);
     173             : 
     174             :         nb_i = nb_p = 0;
     175           2 :         samp = gf_isom_get_sample(dumper->file, track, 1, &di);
     176        3225 :         for (i=0; i<count; i++) {
     177        3221 :                 GF_ISOSample *next_samp = gf_isom_get_sample(dumper->file, track, i+2, &di);
     178        3221 :                 if (!samp) break;
     179        3221 :                 op.bytes = samp->dataLength;
     180        3221 :                 op.packet = (unsigned char*)samp->data;
     181        3221 :                 op.packetno ++;
     182             : 
     183        3221 :                 if (theora_kgs) {
     184           0 :                         if (samp->IsRAP) {
     185           0 :                                 if (i) nb_i+=nb_p+1;
     186             :                                 nb_p = 0;
     187             :                         } else {
     188           0 :                                 nb_p++;
     189             :                         }
     190           0 :                         op.granulepos = nb_i;
     191           0 :                         op.granulepos <<= theora_kgs;
     192           0 :                         op.granulepos |= nb_p;
     193             :                 } else {
     194        3221 :                         if (next_samp) op.granulepos = next_samp->DTS;
     195             :                 }
     196        3221 :                 if (!next_samp) op.e_o_s = 1;
     197             : 
     198        3221 :                 ogg_stream_packetin(&os, &op);
     199             : 
     200        3221 :                 gf_isom_sample_del(&samp);
     201        3221 :                 samp = next_samp;
     202             :                 next_samp = NULL;
     203        3221 :                 gf_set_progress("OGG Export", i+1, count);
     204        3221 :                 if (dumper->flags & GF_EXPORT_DO_ABORT) break;
     205             : 
     206        3461 :                 while (ogg_stream_pageout(&os, &og)>0) {
     207         240 :                         gf_fwrite(og.header, og.header_len, out);
     208         240 :                         gf_fwrite(og.body, og.body_len, out);
     209             :                 }
     210             :         }
     211           2 :         if (samp) gf_isom_sample_del(&samp);
     212             : 
     213           2 :         while (ogg_stream_flush(&os, &og)>0) {
     214           0 :                 gf_fwrite(og.header, og.header_len, out);
     215           0 :                 gf_fwrite(og.body, og.body_len, out);
     216             :         }
     217           2 :         ogg_stream_clear(&os);
     218           2 :         if (szName) gf_fclose(out);
     219             :         return GF_OK;
     220             : #endif
     221             : }
     222             : #endif
     223             : 
     224             : 
     225             : #ifndef GPAC_DISABLE_AV_PARSERS
     226           1 : static GF_Err gf_dump_to_vobsub(GF_MediaExporter *dumper, char *szName, u32 track, char *dsi, u32 dsiSize)
     227             : {
     228             : #ifndef GPAC_DISABLE_VOBSUB
     229             :         FILE *fidx, *fsub;
     230             :         u32 width, height, i, count, di;
     231             :         GF_ISOSample *samp;
     232           1 :         char *lang = NULL;
     233             : 
     234           1 :         if (!szName) {
     235           0 :                 szName = gf_file_basename(gf_isom_get_filename(dumper->file));
     236           0 :                 if (!szName) return GF_BAD_PARAM;
     237             :         }
     238             :         /* Check decoder specific information (palette) size - should be 64 */
     239           1 :         if (!dsi || (dsiSize != 64)) {
     240           0 :                 return gf_export_message(dumper, GF_CORRUPTED_DATA, "Invalid decoder specific info size - must be 64 but is %d", dsiSize);
     241             :         }
     242             : 
     243             :         /* Create an idx file */
     244           1 :         if (!gf_file_ext_start(szName)) {
     245             :                 char szPath[GF_MAX_PATH];
     246             :                 strcpy(szPath, szName);
     247             :                 strcat(szPath, ".idx");
     248           1 :                 fidx = gf_fopen(szPath, "wb");
     249             :          } else {
     250           0 :                 fidx = gf_fopen(szName, "wb");
     251             :         }
     252           1 :         if (!fidx) {
     253           0 :                 return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
     254             :         }
     255             : 
     256             :         /* Create a sub file */
     257           1 :         char *ext = gf_file_ext_start(szName);
     258           1 :         if (ext && (!stricmp(ext, ".idx") || !stricmp(ext, ".sub")) ) {
     259           0 :                 ext[0] = 0;
     260             :         }
     261             :         szName = strcat(szName, ".sub");
     262           1 :         fsub = gf_fopen(szName, "wb");
     263           1 :         if (!fsub) {
     264           0 :                 gf_fclose(fidx);
     265           0 :                 return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
     266             :         }
     267             : 
     268             :         /* Retrieve original subpicture resolution */
     269           1 :         gf_isom_get_track_layout_info(dumper->file, track, &width, &height, NULL, NULL, NULL);
     270             : 
     271             :         /* Write header */
     272           1 :         gf_fputs("# VobSub index file, v7 (do not modify this line!)\n#\n", fidx);
     273             : 
     274             :         /* Write original subpicture resolution */
     275           1 :         gf_fprintf(fidx, "size: %ux%u\n", width, height);
     276             : 
     277             :         /* Write palette */
     278           1 :         gf_fputs("palette:", fidx);
     279          17 :         for (i = 0; i < 16; i++) {
     280             :                 s32 y, u, v, r, g, b;
     281             : 
     282          16 :                 y = (s32)(u8)dsi[(i<<2)+1] - 0x10;
     283          16 :                 u = (s32)(u8)dsi[(i<<2)+3] - 0x80;
     284          16 :                 v = (s32)(u8)dsi[(i<<2)+2] - 0x80;
     285          16 :                 r = (298 * y           + 409 * v + 128) >> 8;
     286          16 :                 g = (298 * y - 100 * u - 208 * v + 128) >> 8;
     287          16 :                 b = (298 * y + 516 * u           + 128) >> 8;
     288             : 
     289          16 :                 if (i) gf_fputc(',', fidx);
     290             : 
     291             : #define CLIP(x) (((x) >= 0) ? (((x) < 256) ? (x) : 255) : 0)
     292          16 :                 gf_fprintf(fidx, " %02x%02x%02x", CLIP(r), CLIP(g), CLIP(b));
     293             : #undef CLIP
     294             :         }
     295           1 :         gf_fputc('\n', fidx);
     296             : 
     297             :         /* Write some other useful values */
     298           1 :         gf_fputs("# ON: displays only forced subtitles, OFF: shows everything\n", fidx);
     299           1 :         gf_fputs("forced subs: OFF\n\n", fidx);
     300             : 
     301             :         /* Write current language index */
     302           1 :         gf_fputs("# Language index in use\nlangidx: 0\n", fidx);
     303             : 
     304             :         /* Write language header */
     305           1 :         gf_isom_get_media_language(dumper->file, track, &lang);
     306           1 :         gf_fprintf(fidx, "id: %s, index: 0\n", vobsub_lang_id(lang));
     307           1 :         gf_free(lang);
     308             : 
     309             :         /* Retrieve sample count */
     310           1 :         count = gf_isom_get_sample_count(dumper->file, track);
     311             : 
     312             :         /* Process samples (skip first - because it is special) */
     313          16 :         for (i = 2; i <= count; i++)
     314             :         {
     315             :                 u64 dts;
     316             :                 u32 hh, mm, ss, ms;
     317             : 
     318          14 :                 samp = gf_isom_get_sample(dumper->file, track, i, &di);
     319          14 :                 if (!samp) {
     320             :                         break;
     321             :                 }
     322             : 
     323          14 :                 dts = samp->DTS / 90;
     324          14 :                 ms  = (u32)(dts % 1000);
     325          14 :                 dts = dts / 1000;
     326          14 :                 ss  = (u32)(dts % 60);
     327          14 :                 dts = dts / 60;
     328          14 :                 mm  = (u32)(dts % 60);
     329          14 :                 hh  = (u32)(dts / 60);
     330          14 :                 gf_fprintf(fidx, "timestamp: %02u:%02u:%02u:%03u, filepos: %09"LLX_SUF"\n", hh, mm, ss, ms, gf_ftell(fsub));
     331          14 :                 if (vobsub_packetize_subpicture(fsub, samp->DTS, samp->data, samp->dataLength) != GF_OK) {
     332           0 :                         gf_isom_sample_del(&samp);
     333           0 :                         gf_fclose(fsub);
     334           0 :                         gf_fclose(fidx);
     335           0 :                         return gf_export_message(dumper, GF_IO_ERR, "Unable packetize subpicture into file %s\n", szName);
     336             :                 }
     337             : 
     338          14 :                 gf_isom_sample_del(&samp);
     339          14 :                 gf_set_progress("VobSub Export", i + 1, count);
     340             : 
     341          14 :                 if (dumper->flags & GF_EXPORT_DO_ABORT) {
     342             :                         break;
     343             :                 }
     344             :         }
     345             : 
     346             :         /* Delete sample if any */
     347           1 :         if (samp) {
     348           0 :                 gf_isom_sample_del(&samp);
     349             :         }
     350             : 
     351           1 :         gf_fclose(fsub);
     352           1 :         gf_fclose(fidx);
     353             : 
     354           1 :         return GF_OK;
     355             : #else
     356             :         return GF_NOT_SUPPORTED;
     357             : #endif
     358             : }
     359             : 
     360             : #endif // GPAC_DISABLE_AV_PARSERS
     361             : 
     362             : 
     363             : #ifndef GPAC_DISABLE_ISOM_WRITE
     364           1 : static GF_Err gf_export_isom_copy_track(GF_MediaExporter *dumper, GF_ISOFile *infile, u32 inTrackNum, GF_ISOFile *outfile, Bool ResetDependencies, Bool AddToIOD)
     365             : {
     366             :         GF_ESD *esd;
     367             :         GF_InitialObjectDescriptor *iod;
     368             :         GF_ISOTrackID TrackID;
     369             :         u32 newTk, descIndex, i, ts, rate, pos, di, count, msubtype;
     370             :         u64 dur;
     371             :         GF_ISOSample *samp;
     372             : 
     373           1 :         if (!inTrackNum) {
     374           0 :                 if (gf_isom_get_track_count(infile) != 1) return gf_export_message(dumper, GF_BAD_PARAM, "Please specify trackID to export");
     375             :                 inTrackNum = 1;
     376             :         }
     377             :         //check the ID is available
     378           1 :         TrackID = gf_isom_get_track_id(infile, inTrackNum);
     379           1 :         newTk = gf_isom_get_track_by_id(outfile, TrackID);
     380           1 :         if (newTk) TrackID = 0;
     381             : 
     382             :         //get the ESD and remove dependencies
     383             :         esd = NULL;
     384           1 :         msubtype = gf_isom_get_media_subtype(infile, inTrackNum, 1);
     385             : 
     386           1 :         if (msubtype == GF_ISOM_SUBTYPE_MPEG4) {
     387           0 :                 esd = gf_isom_get_esd(infile, inTrackNum, 1);
     388           0 :                 if (esd && ResetDependencies) {
     389           0 :                         esd->dependsOnESID = 0;
     390           0 :                         esd->OCRESID = 0;
     391             :                 }
     392             :         }
     393             : 
     394           1 :         newTk = gf_isom_new_track(outfile, TrackID, gf_isom_get_media_type(infile, inTrackNum), gf_isom_get_media_timescale(infile, inTrackNum));
     395           1 :         gf_isom_set_track_enabled(outfile, newTk, GF_TRUE);
     396             : 
     397           1 :         if (gf_isom_has_keep_utc_times(infile)) {
     398             :                 u64 cdate, mdate;
     399           0 :                 gf_isom_get_track_creation_time(infile, inTrackNum, &cdate, &mdate);
     400           0 :                 gf_isom_set_track_creation_time(outfile, newTk, cdate, mdate);
     401             :         }
     402             : 
     403           1 :         if (esd) {
     404           0 :                 gf_isom_new_mpeg4_description(outfile, newTk, esd, NULL, NULL, &descIndex);
     405           0 :                 if ((esd->decoderConfig->streamType == GF_STREAM_VISUAL) || (esd->decoderConfig->streamType == GF_STREAM_SCENE)) {
     406             :                         u32 w, h;
     407           0 :                         gf_isom_get_visual_info(infile, inTrackNum, 1, &w, &h);
     408             : #ifndef GPAC_DISABLE_AV_PARSERS
     409             :                         /*this is because so many files have reserved values of 320x240 from v1 ... */
     410           0 :                         if (esd->decoderConfig->objectTypeIndication == GF_CODECID_MPEG4_PART2) {
     411             :                                 GF_M4VDecSpecInfo dsi;
     412           0 :                                 gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
     413           0 :                                 w = dsi.width;
     414           0 :                                 h = dsi.height;
     415             :                         }
     416             : #endif
     417           0 :                         gf_isom_set_visual_info(outfile, newTk, 1, w, h);
     418             :                 }
     419           0 :                 else if ((esd->decoderConfig->streamType == GF_STREAM_TEXT) && (esd->decoderConfig->objectTypeIndication == GF_CODECID_SUBPIC)) {
     420             :                         u32 w, h;
     421             :                         s32 trans_x, trans_y;
     422             :                         s16 layer;
     423           0 :                         gf_isom_get_track_layout_info(infile, inTrackNum, &w, &h, &trans_x, &trans_y, &layer);
     424           0 :                         gf_isom_set_track_layout_info(outfile, newTk, w << 16, h << 16, trans_x, trans_y, layer);
     425             :                 }
     426           0 :                 esd->decoderConfig->avgBitrate = 0;
     427           0 :                 esd->decoderConfig->maxBitrate = 0;
     428             :         } else {
     429           1 :                 gf_isom_clone_sample_description(outfile, newTk, infile, inTrackNum, 1, NULL, NULL, &descIndex);
     430             :         }
     431             : 
     432             :         pos = 0;
     433             :         rate = 0;
     434           1 :         ts = gf_isom_get_media_timescale(infile, inTrackNum);
     435           1 :         count = gf_isom_get_sample_count(infile, inTrackNum);
     436         175 :         for (i=0; i<count; i++) {
     437         173 :                 samp = gf_isom_get_sample(infile, inTrackNum, i+1, &di);
     438         173 :                 gf_isom_add_sample(outfile, newTk, descIndex, samp);
     439         173 :                 if (esd) {
     440           0 :                         rate += samp->dataLength;
     441           0 :                         esd->decoderConfig->avgBitrate += samp->dataLength;
     442           0 :                         if (esd->decoderConfig->bufferSizeDB<samp->dataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength;
     443           0 :                         if (samp->DTS - pos > ts) {
     444           0 :                                 if (esd->decoderConfig->maxBitrate<rate) esd->decoderConfig->maxBitrate = rate;
     445             :                                 rate = 0;
     446             :                                 pos = 0;
     447             :                         }
     448             :                 }
     449         173 :                 gf_isom_sample_del(&samp);
     450         173 :                 gf_set_progress("ISO File Export", i, count);
     451             :         }
     452           1 :         gf_set_progress("ISO File Export", count, count);
     453             : 
     454           1 :         if (msubtype == GF_ISOM_SUBTYPE_MPEG4_CRYP) {
     455           0 :                 esd = gf_isom_get_esd(infile, inTrackNum, 1);
     456           1 :         } else if ((msubtype == GF_ISOM_SUBTYPE_AVC_H264)
     457             :                    || (msubtype == GF_ISOM_SUBTYPE_AVC2_H264)
     458             :                    || (msubtype == GF_ISOM_SUBTYPE_AVC3_H264)
     459           1 :                    || (msubtype == GF_ISOM_SUBTYPE_AVC4_H264)
     460             :                   ) {
     461           1 :                 return gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0x0F);
     462             :         }
     463             :         /*likely 3gp or any non-MPEG-4 isomedia file*/
     464           0 :         else if (!esd) return gf_isom_remove_root_od(outfile);
     465             : 
     466           0 :         dur = gf_isom_get_media_duration(outfile, newTk);
     467           0 :         if (!dur) dur = ts;
     468           0 :         esd->decoderConfig->maxBitrate *= 8;
     469           0 :         esd->decoderConfig->avgBitrate = (u32) (esd->decoderConfig->avgBitrate * 8 * ts / dur);
     470           0 :         gf_isom_change_mpeg4_description(outfile, newTk, 1, esd);
     471             : 
     472             : 
     473           0 :         iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(infile);
     474           0 :         switch (esd->decoderConfig->streamType) {
     475           0 :         case GF_STREAM_SCENE:
     476           0 :                 if (iod && (iod->tag==GF_ODF_IOD_TAG)) {
     477           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, iod->scene_profileAndLevel);
     478           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, iod->graphics_profileAndLevel);
     479           0 :                 } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_MPEG4_PART2) {
     480           0 :                         gf_export_message(dumper, GF_OK, "Warning: Scene PLs not found in original MP4 - defaulting to No Profile Specified");
     481           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, 0xFE);
     482           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, 0xFE);
     483             :                 }
     484             :                 break;
     485           0 :         case GF_STREAM_VISUAL:
     486           0 :                 if (iod && (iod->tag==GF_ODF_IOD_TAG)) {
     487           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, iod->visual_profileAndLevel);
     488             :                 }
     489             : #ifndef GPAC_DISABLE_AV_PARSERS
     490           0 :                 else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_MPEG4_PART2) {
     491             :                         GF_M4VDecSpecInfo dsi;
     492           0 :                         gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
     493           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, dsi.VideoPL);
     494             :                 }
     495             : #endif
     496             :                 else {
     497           0 :                         gf_export_message(dumper, GF_OK, "Warning: Visual PLs not found in original MP4 - defaulting to No Profile Specified");
     498           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0xFE);
     499             :                 }
     500             :                 break;
     501           0 :         case GF_STREAM_AUDIO:
     502           0 :                 if (iod && (iod->tag==GF_ODF_IOD_TAG)) {
     503           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, iod->audio_profileAndLevel);
     504             :                 }
     505             : #ifndef GPAC_DISABLE_AV_PARSERS
     506           0 :                 else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_AAC_MPEG4) {
     507             :                         GF_M4ADecSpecInfo cfg;
     508           0 :                         gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &cfg);
     509           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, cfg.audioPL);
     510             :                 }
     511             : #endif
     512             :                 else {
     513           0 :                         gf_export_message(dumper, GF_OK, "Warning: Audio PLs not found in original MP4 - defaulting to No Profile Specified");
     514           0 :                         gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, 0xFE);
     515             :                 }
     516             :         default:
     517             :                 break;
     518             :         }
     519           0 :         if (iod) gf_odf_desc_del((GF_Descriptor *) iod);
     520           0 :         gf_odf_desc_del((GF_Descriptor *)esd);
     521             : 
     522           0 :         if (AddToIOD) gf_isom_add_track_to_root_od(outfile, newTk);
     523             : 
     524             :         return GF_OK;
     525             : }
     526             : 
     527             : 
     528           1 : GF_Err gf_media_export_isom(GF_MediaExporter *dumper)
     529             : {
     530             :         GF_ISOFile *outfile;
     531             :         GF_Err e;
     532             :         Bool add_to_iod, is_stdout;
     533             :         char szName[1000];
     534             :         u32 track;
     535             :         GF_ISOOpenMode mode;
     536             : 
     537           1 :         if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) {
     538           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file)));
     539             :                 return GF_BAD_PARAM;
     540             :         }
     541           1 :         if (gf_isom_get_media_type(dumper->file, dumper->trackID)==GF_ISOM_MEDIA_OD) {
     542           0 :                 return gf_export_message(dumper, GF_BAD_PARAM, "Cannot extract OD track, result is  meaningless");
     543             :         }
     544             : 
     545           1 :         if (dumper->flags & GF_EXPORT_PROBE_ONLY) {
     546           0 :                 dumper->flags |= GF_EXPORT_MERGE;
     547           0 :                 return GF_OK;
     548             :         }
     549           1 :         if (dumper->out_name && gf_file_ext_start(dumper->out_name)) {
     550           1 :                 strcpy(szName, dumper->out_name);
     551             :         } else {
     552           0 :                 char *ext = (char *) gf_isom_get_filename(dumper->file);
     553           0 :                 if (ext) ext = gf_file_ext_start(ext);
     554           0 :                 sprintf(szName, "%s%s", dumper->out_name, ext ? ext : ".mp4");
     555             :         }
     556           1 :         is_stdout = (dumper->out_name && !strcmp(dumper->out_name, "std")) ? 1 : 0;
     557             :         add_to_iod = 1;
     558             :         mode = GF_ISOM_WRITE_EDIT;
     559           1 :         if (!is_stdout && (dumper->flags & GF_EXPORT_MERGE)) {
     560           1 :                 FILE *t = gf_fopen(szName, "rb");
     561           1 :                 if (t) {
     562             :                         add_to_iod = 0;
     563             :                         mode = GF_ISOM_OPEN_EDIT;
     564           0 :                         gf_fclose(t);
     565             :                 }
     566             :         }
     567           1 :         outfile = gf_isom_open(is_stdout ? "std" : szName, mode, NULL);
     568             : 
     569           1 :         if (mode == GF_ISOM_WRITE_EDIT) {
     570           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_AUDIO, 0xFF);
     571           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0xFF);
     572           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_GRAPHICS, 0xFF);
     573           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_SCENE, 0xFF);
     574           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_OD, 0xFF);
     575           1 :                 gf_isom_set_pl_indication(outfile, GF_ISOM_PL_MPEGJ, 0xFF);
     576             :         }
     577           1 :         if (gf_isom_has_keep_utc_times(dumper->file)) {
     578             :                 u64 cdate, mdate;
     579           0 :                 gf_isom_get_creation_time(dumper->file, &cdate, &mdate);
     580           0 :                 gf_isom_set_creation_time(outfile, cdate, mdate);
     581             :         }
     582             : 
     583           1 :         e = gf_export_isom_copy_track(dumper, dumper->file, track, outfile, 1, add_to_iod);
     584           1 :         if (!add_to_iod) {
     585             :                 u32 i;
     586           0 :                 for (i=0; i<gf_isom_get_track_count(outfile); i++) {
     587           0 :                         gf_isom_remove_track_from_root_od(outfile, i+1);
     588             :                 }
     589             :         }
     590             : 
     591           1 :         if (gf_isom_has_keep_utc_times(dumper->file))
     592           0 :                 gf_isom_keep_utc_times(outfile, GF_TRUE);
     593             : 
     594           1 :         if (e) gf_isom_delete(outfile);
     595           1 :         else gf_isom_close(outfile);
     596             : 
     597             :         return e;
     598             : }
     599             : #endif /*GPAC_DISABLE_ISOM_WRITE*/
     600             : 
     601             : 
     602             : /* Required for base64 encoding of DecoderSpecificInfo */
     603             : #include <gpac/base_coding.h>
     604             : 
     605             : #ifndef GPAC_DISABLE_VTT
     606             : 
     607             : /* Required for timestamp generation */
     608             : #include <gpac/webvtt.h>
     609             : 
     610           1 : GF_Err gf_media_export_webvtt_metadata(GF_MediaExporter *dumper)
     611             : {
     612             :         GF_ESD *esd;
     613             :         char szName[1000], szMedia[1000];
     614             :         FILE *med, *vtt;
     615             :         u32 w, h;
     616             :         u32 track, i, di, count, pos;
     617             :         u32 mtype, mstype;
     618             :         Bool isText;
     619             :         char *mime = NULL;
     620             :         Bool useBase64 = GF_FALSE;
     621           1 :         u32 headerLength = 0;
     622             : 
     623           1 :         if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) {
     624           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file)));
     625             :                 return GF_BAD_PARAM;
     626             :         }
     627             :         if (!track) return gf_export_message(dumper, GF_BAD_PARAM, "Invalid track ID %d", dumper->trackID);
     628             : 
     629           1 :         if (dumper->flags & GF_EXPORT_PROBE_ONLY) {
     630             :                 return GF_OK;
     631             :         }
     632           1 :         esd = gf_isom_get_esd(dumper->file, track, 1);
     633             :         med = NULL;
     634           1 :         if (dumper->flags & GF_EXPORT_WEBVTT_META_EMBEDDED) {
     635             :         } else {
     636           1 :                 sprintf(szMedia, "%s.media", dumper->out_name);
     637           1 :                 med = gf_fopen(szMedia, "wb");
     638           1 :                 if (!med) {
     639           0 :                         if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
     640           0 :                         return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szMedia);
     641             :                 }
     642             :         }
     643             : 
     644           1 :         sprintf(szName, "%s.vtt", dumper->out_name);
     645           1 :         vtt = gf_fopen(szName, "wt");
     646           1 :         if (!vtt) {
     647           0 :                 gf_fclose(med);
     648           0 :                 if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
     649           0 :                 return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
     650             :         }
     651             : 
     652           1 :         mtype = gf_isom_get_media_type(dumper->file, track);
     653           1 :         if (mtype==GF_ISOM_MEDIA_TEXT || mtype == GF_ISOM_MEDIA_MPEG_SUBT || mtype == GF_ISOM_MEDIA_SUBT) {
     654             :                 isText = GF_TRUE;
     655             :         } else {
     656             :                 isText = GF_FALSE;
     657             :         }
     658           1 :         mstype = gf_isom_get_media_subtype(dumper->file, track, 1);
     659             : 
     660             :         /*write header*/
     661           1 :         gf_fprintf(vtt, "WEBVTT Metadata track generated by GPAC MP4Box %s\n", gf_sys_is_test_mode() ? "" : gf_gpac_version());
     662             : 
     663           1 :         gf_fprintf(vtt, "kind:metadata\n");
     664             :         {
     665             :                 char *lang;
     666           1 :                 gf_isom_get_media_language(dumper->file, track, &lang);
     667           1 :                 gf_fprintf(vtt, "language:%s\n", lang);
     668           1 :                 gf_free(lang);
     669             :         }
     670             :         {
     671             :                 const char *handler;
     672           1 :                 gf_isom_get_handler_name(dumper->file, track, &handler);
     673           1 :                 gf_fprintf(vtt, "label: %s\n", handler);
     674             :         }
     675           1 :         if (gf_isom_is_track_in_root_od(dumper->file, track)) gf_fprintf(vtt, "inRootOD: yes\n");
     676           1 :         gf_fprintf(vtt, "trackID: %d\n", dumper->trackID);
     677           1 :         if (med) {
     678           1 :                 gf_fprintf(vtt, "baseMediaFile: %s\n", gf_file_basename(szMedia));
     679             :         }
     680           1 :         if (esd) {
     681             :                 /* TODO: export the MPEG-4 Stream type only if it is not a GPAC internal value */
     682           1 :                 gf_fprintf(vtt, "MPEG-4-streamType: %d\n", esd->decoderConfig->streamType);
     683             :                 /* TODO: export the MPEG-4 Object Type Indication only if it is not a GPAC internal value */
     684           1 :                 gf_fprintf(vtt, "MPEG-4-objectTypeIndication: %d\n", esd->decoderConfig->objectTypeIndication);
     685           1 :                 if (gf_isom_is_video_handler_type(mtype) ) {
     686           0 :                         gf_isom_get_visual_info(dumper->file, track, 1, &w, &h);
     687           0 :                         gf_fprintf(vtt, "width:%d\n", w);
     688           0 :                         gf_fprintf(vtt, "height:%d\n", h);
     689             :                 }
     690           1 :                 else if (mtype==GF_ISOM_MEDIA_AUDIO) {
     691             :                         u32 sr, nb_ch, bps;
     692           0 :                         gf_isom_get_audio_info(dumper->file, track, 1, &sr, &nb_ch, &bps);
     693           0 :                         gf_fprintf(vtt, "sampleRate: %d\n", sr);
     694           0 :                         gf_fprintf(vtt, "numChannels: %d\n", nb_ch);
     695           1 :                 } else if (isText) {
     696             :                         s32 tx, ty;
     697             :                         s16 layer;
     698           1 :                         gf_isom_get_track_layout_info(dumper->file, track, &w, &h, &tx, &ty, &layer);
     699           1 :                         gf_fprintf(vtt, "width:%d\n", w);
     700           1 :                         gf_fprintf(vtt, "height:%d\n", h);
     701           1 :                         if (tx || ty) gf_fprintf(vtt, "translation:%d,%d\n", tx, ty);
     702           1 :                         if (layer) gf_fprintf(vtt, "layer:%d\n", layer);
     703             :                 }
     704           1 :                 if (esd->decoderConfig->decoderSpecificInfo  && esd->decoderConfig->decoderSpecificInfo->data) {
     705           1 :                         if (isText) {
     706           1 :                                 if (mstype == GF_ISOM_SUBTYPE_WVTT) {
     707             :                                         /* Warning: Just use -raw export */
     708             :                                         mime = "text/vtt";
     709           0 :                                 } else if (mstype == GF_ISOM_SUBTYPE_STXT) {
     710             :                                         /* TODO: find the mime type from the ESD, assume SVG for now */
     711             :                                         mime = "image/svg+xml";
     712           0 :                                 } else if (mstype == GF_ISOM_SUBTYPE_STPP) {
     713             :                                         /* TODO: find the mime type from the ESD, assume TTML for now */
     714             :                                         mime = "application/ttml+xml";
     715             :                                 }
     716           1 :                                 if (dumper->flags & GF_EXPORT_WEBVTT_META_EMBEDDED) {
     717           0 :                                         if (mstype == GF_ISOM_SUBTYPE_STXT) {
     718           0 :                                                 if (esd->decoderConfig->decoderSpecificInfo->dataLength) {
     719           0 :                                                         gf_fprintf(vtt, "text-header: \n");
     720           0 :                                                         gf_webvtt_dump_header_boxed(vtt, esd->decoderConfig->decoderSpecificInfo->data+4, esd->decoderConfig->decoderSpecificInfo->dataLength, &headerLength);
     721             :                                                 }
     722             :                                         }
     723             :                                 } else {
     724           1 :                                         gf_webvtt_dump_header_boxed(med, esd->decoderConfig->decoderSpecificInfo->data+4, esd->decoderConfig->decoderSpecificInfo->dataLength, &headerLength);
     725           1 :                                         gf_fprintf(vtt, "text-header-length: %d\n", headerLength);
     726             :                                 }
     727             :                         } else {
     728             :                                 char b64[200];
     729           0 :                                 u32 size = gf_base64_encode(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, b64, 200);
     730             :                                 useBase64 = GF_TRUE;
     731           0 :                                 if (size != (u32)-1 && size != 0) {
     732           0 :                                         b64[size] = 0;
     733           0 :                                         gf_fprintf(vtt, "MPEG-4-DecoderSpecificInfo: %s\n", b64);
     734             :                                 }
     735             :                         }
     736             :                 }
     737           1 :                 gf_odf_desc_del((GF_Descriptor *) esd);
     738             :         } else {
     739           0 :                 GF_GenericSampleDescription *sdesc = gf_isom_get_generic_sample_description(dumper->file, track, 1);
     740           0 :                 gf_fprintf(vtt, "mediaType: %s\n", gf_4cc_to_str(mtype));
     741           0 :                 gf_fprintf(vtt, "mediaSubType: %s\n", gf_4cc_to_str(mstype ));
     742           0 :                 if (sdesc) {
     743           0 :                         if (gf_isom_is_video_handler_type(mtype) ) {
     744           0 :                                 gf_fprintf(vtt, "codecVendor: %s\n", gf_4cc_to_str(sdesc->vendor_code));
     745           0 :                                 gf_fprintf(vtt, "codecVersion: %d\n", sdesc->version);
     746           0 :                                 gf_fprintf(vtt, "codecRevision: %d\n", sdesc->revision);
     747           0 :                                 gf_fprintf(vtt, "width: %d\n", sdesc->width);
     748           0 :                                 gf_fprintf(vtt, "height: %d\n", sdesc->height);
     749           0 :                                 gf_fprintf(vtt, "compressorName: %s\n", sdesc->compressor_name);
     750           0 :                                 gf_fprintf(vtt, "temporalQuality: %d\n", sdesc->temporal_quality);
     751           0 :                                 gf_fprintf(vtt, "spatialQuality: %d\n", sdesc->spatial_quality);
     752           0 :                                 gf_fprintf(vtt, "horizontalResolution: %d\n", sdesc->h_res);
     753           0 :                                 gf_fprintf(vtt, "verticalResolution: %d\n", sdesc->v_res);
     754           0 :                                 gf_fprintf(vtt, "bitDepth: %d\n", sdesc->depth);
     755           0 :                         } else if (mtype==GF_ISOM_MEDIA_AUDIO) {
     756           0 :                                 gf_fprintf(vtt, "codecVendor: %s\n", gf_4cc_to_str(sdesc->vendor_code));
     757           0 :                                 gf_fprintf(vtt, "codecVersion: %d\n", sdesc->version);
     758           0 :                                 gf_fprintf(vtt, "codecRevision: %d\n", sdesc->revision);
     759           0 :                                 gf_fprintf(vtt, "sampleRate: %d\n", sdesc->samplerate);
     760           0 :                                 gf_fprintf(vtt, "numChannels: %d\n", sdesc->nb_channels);
     761           0 :                                 gf_fprintf(vtt, "bitsPerSample: %d\n", sdesc->bits_per_sample);
     762             :                         }
     763           0 :                         if (sdesc->extension_buf) {
     764             :                                 char b64[200];
     765           0 :                                 u32 size = gf_base64_encode(sdesc->extension_buf, sdesc->extension_buf_size, b64, 200);
     766             :                                 useBase64 = GF_TRUE;
     767           0 :                                 if (size != (u32)-1) {
     768           0 :                                         b64[size] = 0;
     769           0 :                                         gf_fprintf(vtt, "specificInfo: %s\n", b64);
     770           0 :                                         gf_free(sdesc->extension_buf);
     771             :                                 }
     772             :                         }
     773           0 :                         gf_free(sdesc);
     774             :                 }
     775             :         }
     776           1 :         gf_fprintf(vtt, "inBandMetadataTrackDispatchType: %s\n", (mime ? mime : (isText? "text/plain" : "application/octet-stream")));
     777           1 :         if (useBase64) gf_fprintf(vtt, "encoding: base64\n");
     778             : 
     779           1 :         gf_fprintf(vtt, "\n");
     780             : 
     781             :         pos = 0;
     782           1 :         count = gf_isom_get_sample_count(dumper->file, track);
     783           1 :         for (i=0; i<count; i++) {
     784          17 :                 GF_ISOSample *samp = gf_isom_get_sample(dumper->file, track, i+1, &di);
     785          17 :                 if (!samp) break;
     786             : 
     787             :                 {
     788             :                         GF_WebVTTTimestamp start, end;
     789          17 :                         u64 dur = gf_isom_get_sample_duration(dumper->file, track, i+1);
     790          17 :                         gf_webvtt_timestamp_set(&start, samp->DTS);
     791          17 :                         gf_webvtt_timestamp_set(&end, samp->DTS+dur);
     792          17 :                         gf_webvtt_timestamp_dump(&start, vtt, GF_TRUE);
     793          17 :                         gf_fprintf(vtt, " --> ");
     794          17 :                         gf_webvtt_timestamp_dump(&end, vtt, GF_TRUE);
     795          17 :                         gf_fprintf(vtt, " ");
     796          17 :                         if (med) {
     797          17 :                                 gf_fprintf(vtt, "mediaOffset:%d ", pos+headerLength);
     798          17 :                                 gf_fprintf(vtt, "dataLength:%d ", samp->dataLength);
     799             :                         }
     800          17 :                         if (samp->CTS_Offset) gf_fprintf(vtt, "CTS: "LLD"", samp->DTS+samp->CTS_Offset);
     801          17 :                         if (samp->IsRAP==RAP) gf_fprintf(vtt, "isRAP:true ");
     802           0 :                         else if (samp->IsRAP==RAP_REDUNDANT) gf_fprintf(vtt, "isSyncShadow: true ");
     803           0 :                         else gf_fprintf(vtt, "isRAP:false ");
     804          17 :                         gf_fprintf(vtt, "\n");
     805             :                 }
     806          17 :                 if (med) {
     807          17 :                         gf_fwrite(samp->data, samp->dataLength, med);
     808           0 :                 } else if (dumper->flags & GF_EXPORT_WEBVTT_META_EMBEDDED) {
     809           0 :                         if (isText) {
     810           0 :                                 samp->data = (char *)gf_realloc(samp->data, samp->dataLength+1);
     811           0 :                                 samp->data[samp->dataLength] = 0;
     812           0 :                                 gf_fprintf(vtt, "%s\n", samp->data);
     813             :                         } else {
     814             :                                 u32 b64_size;
     815             :                                 char *b64;
     816           0 :                                 b64 = (char *)gf_malloc(samp->dataLength*3);
     817           0 :                                 b64_size = gf_base64_encode(samp->data, samp->dataLength, b64, samp->dataLength*3);
     818           0 :                                 if (b64_size != (u32)-1) {
     819           0 :                                         b64[b64_size] = 0;
     820           0 :                                         gf_fprintf(vtt, "%s\n", b64);
     821             :                                 }
     822           0 :                                 gf_free(b64);
     823             :                         }
     824             :                 }
     825          17 :                 gf_fprintf(vtt, "\n");
     826             : 
     827          17 :                 pos += samp->dataLength;
     828          17 :                 gf_isom_sample_del(&samp);
     829          17 :                 gf_set_progress("WebVTT metadata Export", i+1, count);
     830          17 :                 if (dumper->flags & GF_EXPORT_DO_ABORT) break;
     831             :         }
     832           1 :         if (med) gf_fclose(med);
     833           1 :         gf_fclose(vtt);
     834           1 :         return GF_OK;
     835             : }
     836             : 
     837             : #endif /*GPAC_DISABLE_VTT*/
     838             : 
     839             : /* Experimental Streaming Instructions XML export */
     840           1 : GF_Err gf_media_export_six(GF_MediaExporter *dumper)
     841             : {
     842             :         GF_ESD *esd;
     843             :         char szName[1000], szMedia[1000];
     844             :         FILE *media, *six;
     845             :         u32 track, i, di, count, pos, header_size;
     846             :         //u32 mtype;
     847             : #if !defined(GPAC_DISABLE_TTXT) && !defined(GPAC_DISABLE_VTT)
     848             :         u32 mstype;
     849             : #endif
     850             :         const char *szRootName;
     851             :         //Bool isText;
     852             : 
     853           1 :         if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) {
     854           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file)));
     855             :                 return GF_BAD_PARAM;
     856             :         }
     857             :         if (!track) return gf_export_message(dumper, GF_BAD_PARAM, "Invalid track ID %d", dumper->trackID);
     858             : 
     859           1 :         if (dumper->flags & GF_EXPORT_PROBE_ONLY) {
     860           0 :                 dumper->flags |= GF_EXPORT_NHML_FULL;
     861           0 :                 return GF_OK;
     862             :         }
     863           1 :         esd = gf_isom_get_esd(dumper->file, track, 1);
     864             : 
     865           1 :         sprintf(szMedia, "%s.media", dumper->out_name);
     866           1 :         media = gf_fopen(szMedia, "wb");
     867           1 :         if (!media) {
     868           0 :                 if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
     869           0 :                 return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szMedia);
     870             :         }
     871             : 
     872           1 :         sprintf(szName, "%s.six", dumper->out_name);
     873             :         szRootName = "stream";
     874             : 
     875           1 :         six = gf_fopen(szName, "wt");
     876           1 :         if (!six) {
     877           0 :                 gf_fclose(media);
     878           0 :                 if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
     879           0 :                 return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
     880             :         }
     881             :         /*
     882             :                 mtype = gf_isom_get_media_type(dumper->file, track);
     883             :                 if (mtype==GF_ISOM_MEDIA_TEXT || mtype == GF_ISOM_MEDIA_SUBM || mtype == GF_ISOM_MEDIA_SUBT) {
     884             :                         isText = GF_TRUE;
     885             :                 } else {
     886             :                         isText = GF_FALSE;
     887             :                 }
     888             :         */
     889             : #if !defined(GPAC_DISABLE_TTXT) && !defined(GPAC_DISABLE_VTT)
     890           1 :         mstype = gf_isom_get_media_subtype(dumper->file, track, 1);
     891             : #endif
     892             : 
     893             :         /*write header*/
     894           1 :         gf_fprintf(six, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
     895           1 :         gf_fprintf(six, "<%s timescale=\"%d\" ", szRootName, gf_isom_get_media_timescale(dumper->file, track) );
     896           1 :         gf_fprintf(six, "file=\"%s\" ", szMedia);
     897           1 :         gf_fprintf(six, ">\n");
     898           1 :         header_size = 0;
     899           1 :         if (esd) {
     900           1 :                 if (esd->decoderConfig->decoderSpecificInfo  && esd->decoderConfig->decoderSpecificInfo->data) {
     901             : #if !defined(GPAC_DISABLE_TTXT) && !defined(GPAC_DISABLE_VTT)
     902           1 :                         if (mstype == GF_ISOM_SUBTYPE_WVTT || mstype == GF_ISOM_SUBTYPE_STXT) {
     903           2 :                                 gf_webvtt_dump_header_boxed(media,
     904           1 :                                                             esd->decoderConfig->decoderSpecificInfo->data+4,
     905             :                                                             esd->decoderConfig->decoderSpecificInfo->dataLength,
     906             :                                                             &header_size);
     907             :                         } else
     908             : #endif
     909             :                         {
     910           0 :                                 gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, media);
     911           0 :                                 header_size = esd->decoderConfig->decoderSpecificInfo->dataLength;
     912             :                         }
     913             :                 }
     914           1 :                 gf_odf_desc_del((GF_Descriptor *) esd);
     915             :         } else {
     916           0 :                 GF_GenericSampleDescription *sdesc = gf_isom_get_generic_sample_description(dumper->file, track, 1);
     917           0 :                 if (sdesc) {
     918           0 :                         header_size = sdesc->extension_buf_size;
     919           0 :                         gf_free(sdesc);
     920             :                 }
     921             :         }
     922           1 :         gf_fprintf(six, "<header range-begin=\"0\" range-end=\"%d\"/>\n", header_size-1);
     923             : 
     924           1 :         pos = header_size;
     925           1 :         count = gf_isom_get_sample_count(dumper->file, track);
     926           1 :         for (i=0; i<count; i++) {
     927          17 :                 GF_ISOSample *samp = gf_isom_get_sample(dumper->file, track, i+1, &di);
     928          17 :                 if (!samp) break;
     929             : 
     930             :                 if (media) {
     931          17 :                         gf_fwrite(samp->data, samp->dataLength, media);
     932             :                 }
     933             : 
     934          17 :                 gf_fprintf(six, "<unit time=\""LLU"\" ", samp->DTS);
     935          17 :                 if (samp->IsRAP==RAP) gf_fprintf(six, "rap=\"1\" ");
     936           0 :                 else if (samp->IsRAP==RAP_NO) gf_fprintf(six, "rap=\"0\" ");
     937          17 :                 gf_fprintf(six, "range-begin=\"%d\" ", pos);
     938          17 :                 gf_fprintf(six, "range-end=\"%d\" ", pos+samp->dataLength-1);
     939          17 :                 gf_fprintf(six, "/>\n");
     940             : 
     941          17 :                 pos += samp->dataLength;
     942          17 :                 gf_isom_sample_del(&samp);
     943          17 :                 gf_set_progress("SIX Export", i+1, count);
     944          17 :                 if (dumper->flags & GF_EXPORT_DO_ABORT) break;
     945             :         }
     946           1 :         gf_fprintf(six, "</%s>\n", szRootName);
     947           1 :         if (media) gf_fclose(media);
     948           1 :         gf_fclose(six);
     949           1 :         return GF_OK;
     950             : 
     951             : }
     952             : 
     953             : typedef struct
     954             : {
     955             :         u32 track_num, stream_id, last_sample, nb_samp;
     956             : } SAFInfo;
     957             : 
     958           1 : GF_Err gf_media_export_saf(GF_MediaExporter *dumper)
     959             : {
     960             : #ifndef GPAC_DISABLE_SAF
     961             :         u32 count, i, s_count, di, tot_samp, samp_done;
     962             :         char out_file[GF_MAX_PATH];
     963             :         GF_SAFMuxer *mux;
     964             :         u8 *data;
     965             :         u32 size;
     966             :         Bool is_stdout = 0;
     967             :         FILE *saf_f;
     968             :         SAFInfo safs[1024];
     969             : 
     970           1 :         if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK;
     971             : 
     972             :         s_count = tot_samp = 0;
     973             : 
     974           1 :         mux = gf_saf_mux_new();
     975           1 :         count = gf_isom_get_track_count(dumper->file);
     976           4 :         for (i=0; i<count; i++) {
     977             :                 u32 time_scale, mtype, stream_id;
     978             :                 GF_ESD *esd;
     979           2 :                 mtype = gf_isom_get_media_type(dumper->file, i+1);
     980           2 :                 if (mtype==GF_ISOM_MEDIA_OD) continue;
     981           1 :                 if (mtype==GF_ISOM_MEDIA_HINT) continue;
     982             : 
     983           1 :                 time_scale = gf_isom_get_media_timescale(dumper->file, i+1);
     984           1 :                 esd = gf_isom_get_esd(dumper->file, i+1, 1);
     985           1 :                 if (esd) {
     986           1 :                         stream_id = gf_isom_find_od_id_for_track(dumper->file, i+1);
     987           1 :                         if (!stream_id) stream_id = esd->ESID;
     988             : 
     989             :                         /*translate OD IDs to ESIDs !!*/
     990           1 :                         if (esd->decoderConfig->decoderSpecificInfo) {
     991           1 :                                 gf_saf_mux_stream_add(mux, stream_id, time_scale, esd->decoderConfig->bufferSizeDB, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, NULL, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->URLString);
     992             :                         } else {
     993           0 :                                 gf_saf_mux_stream_add(mux, stream_id, time_scale, esd->decoderConfig->bufferSizeDB, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication, NULL, NULL, 0, esd->URLString);
     994             :                         }
     995           1 :                         gf_odf_desc_del((GF_Descriptor *)esd);
     996             :                 } else {
     997             :                         char *mime = NULL;
     998           0 :                         switch (gf_isom_get_media_subtype(dumper->file, i+1, 1)) {
     999             :                         case GF_ISOM_SUBTYPE_3GP_H263:
    1000             :                                 mime = "video/h263";
    1001             :                                 break;
    1002           0 :                         case GF_ISOM_SUBTYPE_3GP_AMR:
    1003             :                                 mime = "audio/amr";
    1004           0 :                                 break;
    1005           0 :                         case GF_ISOM_SUBTYPE_3GP_AMR_WB:
    1006             :                                 mime = "audio/amr-wb";
    1007           0 :                                 break;
    1008           0 :                         case GF_ISOM_SUBTYPE_3GP_EVRC:
    1009             :                                 mime = "audio/evrc";
    1010           0 :                                 break;
    1011           0 :                         case GF_ISOM_SUBTYPE_3GP_QCELP:
    1012             :                                 mime = "audio/qcelp";
    1013           0 :                                 break;
    1014           0 :                         case GF_ISOM_SUBTYPE_3GP_SMV:
    1015             :                                 mime = "audio/smv";
    1016           0 :                                 break;
    1017             :                         }
    1018           0 :                         if (!mime) continue;
    1019           0 :                         stream_id = gf_isom_get_track_id(dumper->file, i+1);
    1020           0 :                         gf_saf_mux_stream_add(mux, stream_id, time_scale, 0, 0xFF, 0xFF, mime, NULL, 0, NULL);
    1021             :                 }
    1022             : 
    1023           1 :                 safs[s_count].track_num = i+1;
    1024           1 :                 safs[s_count].stream_id = stream_id;
    1025           1 :                 safs[s_count].nb_samp = gf_isom_get_sample_count(dumper->file, i+1);
    1026           1 :                 safs[s_count].last_sample = 0;
    1027             : 
    1028           1 :                 tot_samp += safs[s_count].nb_samp;
    1029             : 
    1030           1 :                 s_count++;
    1031             :         }
    1032             : 
    1033           1 :         if (!s_count) {
    1034           0 :                 gf_export_message(dumper, GF_OK, "No tracks available for SAF muxing");
    1035           0 :                 gf_saf_mux_del(mux);
    1036           0 :                 return GF_OK;
    1037             :         }
    1038           1 :         gf_export_message(dumper, GF_OK, "SAF: Multiplexing %d tracks", s_count);
    1039             : 
    1040           1 :         if (dumper->out_name && !strcmp(dumper->out_name, "std"))
    1041             :                 is_stdout = 1;
    1042           1 :         strcpy(out_file, dumper->out_name ? dumper->out_name : "");
    1043             :         strcat(out_file, ".saf");
    1044           1 :         saf_f = is_stdout ? stdout : gf_fopen(out_file, "wb");
    1045             : 
    1046             :         samp_done = 0;
    1047           5 :         while (samp_done<tot_samp) {
    1048           3 :                 for (i=0; i<s_count; i++) {
    1049             :                         GF_ISOSample *samp;
    1050           3 :                         if (safs[i].last_sample==safs[i].nb_samp) continue;
    1051           3 :                         samp = gf_isom_get_sample(dumper->file, safs[i].track_num, safs[i].last_sample + 1, &di);
    1052           3 :                         gf_saf_mux_add_au(mux, safs[i].stream_id, (u32) (samp->DTS+samp->CTS_Offset), samp->data, samp->dataLength, (samp->IsRAP==RAP) ? 1 : 0);
    1053             :                         /*data is kept by muxer!!*/
    1054           3 :                         gf_free(samp);
    1055           3 :                         safs[i].last_sample++;
    1056           3 :                         samp_done ++;
    1057             :                 }
    1058             :                 while (1) {
    1059           9 :                         gf_saf_mux_for_time(mux, (u32) -1, 0, &data, &size);
    1060           6 :                         if (!data) break;
    1061           3 :                         gf_fwrite(data, size, saf_f);
    1062           3 :                         gf_free(data);
    1063             :                 }
    1064           3 :                 gf_set_progress("SAF Export", samp_done, tot_samp);
    1065           3 :                 if (dumper->flags & GF_EXPORT_DO_ABORT) break;
    1066             :         }
    1067           1 :         gf_saf_mux_for_time(mux, (u32) -1, 1, &data, &size);
    1068           1 :         if (data) {
    1069           1 :                 gf_fwrite(data, size, saf_f);
    1070           1 :                 gf_free(data);
    1071             :         }
    1072           1 :         if (!is_stdout)
    1073           1 :                 gf_fclose(saf_f);
    1074             : 
    1075           1 :         gf_saf_mux_del(mux);
    1076           1 :         return GF_OK;
    1077             : #else
    1078             :         return GF_NOT_SUPPORTED;
    1079             : #endif
    1080             : }
    1081             : 
    1082             : 
    1083          60 : static GF_Err gf_media_export_filters(GF_MediaExporter *dumper)
    1084             : {
    1085             :         char *args, szSubArgs[1024], szExt[30];
    1086             :         GF_Filter *file_out, *reframer, *remux=NULL, *src_filter;
    1087             :         GF_FilterSession *fsess;
    1088          60 :         GF_Err e = GF_OK;
    1089             :         u32 codec_id=0;
    1090             :         u32 sample_count=0;
    1091             :         Bool skip_write_filter = GF_FALSE;
    1092             :         Bool ext_forced = GF_FALSE;
    1093             :         Bool use_dynext = GF_FALSE;
    1094             : 
    1095          60 :         args = NULL;
    1096             :         strcpy(szExt, "");
    1097          60 :         if (dumper->trackID && dumper->file) {
    1098             :                 u32 msubtype = 0;
    1099             :                 u32 mtype = 0;
    1100             :                 u32 afmt = 0;
    1101             :                 GF_PixelFormat pfmt = 0;
    1102             :                 GF_ESD *esd;
    1103          55 :                 const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
    1104          55 :                 u32 track_num = gf_isom_get_track_by_id(dumper->file, dumper->trackID);
    1105          55 :                 if (!track_num) {
    1106           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] No tracks with ID %d in file\n", dumper->trackID));
    1107             :                         return GF_BAD_PARAM;
    1108             :                 }
    1109          55 :                 esd = gf_media_map_esd(dumper->file, track_num, 0);
    1110          55 :                 sample_count = gf_isom_get_sample_count(dumper->file, dumper->trackID);
    1111          55 :                 if (esd) {
    1112          15 :                         if (esd->decoderConfig->objectTypeIndication<GF_CODECID_LAST_MPEG4_MAPPING) {
    1113          12 :                                 codec_id = gf_codecid_from_oti(esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication);
    1114             : #ifndef GPAC_DISABLE_AV_PARSERS
    1115          12 :                                 if (esd->decoderConfig->decoderSpecificInfo && (codec_id==GF_CODECID_AAC_MPEG4)) {
    1116             :                                         GF_M4ADecSpecInfo acfg;
    1117           1 :                                         gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &acfg);
    1118           1 :                                         if (acfg.base_object_type == GF_M4A_USAC)
    1119             :                                                 codec_id = GF_CODECID_USAC;
    1120             :                                 }
    1121             : #endif
    1122             :                         } else {
    1123             :                                 codec_id = esd->decoderConfig->objectTypeIndication;
    1124             :                         }
    1125             :                 }
    1126          15 :                 if (!codec_id) {
    1127          40 :                         msubtype = gf_isom_get_media_subtype(dumper->file, track_num, 1);
    1128          40 :                         codec_id = gf_codec_id_from_isobmf(msubtype);
    1129             :                 }
    1130          55 :                 mtype = gf_isom_get_media_type(dumper->file, track_num);
    1131          55 :                 if (!codec_id) {
    1132           0 :                         pfmt = gf_pixel_fmt_from_qt_type(msubtype);
    1133           0 :                         if (pfmt) codec_id = GF_CODECID_RAW;
    1134             :                 }
    1135             : 
    1136          55 :                 if (!codec_id) {
    1137           0 :                         strcpy(szExt, gf_4cc_to_str(msubtype));
    1138             :                         ext_forced = GF_TRUE;
    1139          55 :                 } else if (codec_id==GF_CODECID_RAW) {
    1140           0 :                         switch (mtype) {
    1141           0 :                         case GF_ISOM_MEDIA_VISUAL:
    1142             :                         case GF_ISOM_MEDIA_AUXV:
    1143             :                         case GF_ISOM_MEDIA_PICT:
    1144           0 :                                 if (pfmt)
    1145           0 :                                         strcpy(szExt, gf_pixel_fmt_sname(pfmt));
    1146             :                                 break;
    1147           0 :                         case GF_ISOM_MEDIA_AUDIO:
    1148           0 :                                 afmt = gf_audio_fmt_from_isobmf(msubtype);
    1149           0 :                                 if (afmt)
    1150           0 :                                         strcpy(szExt, gf_audio_fmt_name(afmt));
    1151             :                                 break;
    1152           0 :                         default:
    1153           0 :                                 strcpy(szExt, gf_4cc_to_str(msubtype));
    1154             :                                 break;
    1155             :                         }
    1156             :                 } else {
    1157          55 :                         const char *sname = gf_codecid_file_ext(codec_id);
    1158          55 :                         if (export_ext && strstr(sname, export_ext+1)) {
    1159           8 :                                 szExt[0]=0;
    1160             :                         } else {
    1161             :                                 char *sep;
    1162             :                                 strncpy(szExt, sname, 29);
    1163          47 :                                 szExt[29]=0;
    1164          47 :                                 sep = strchr(szExt, '|');
    1165          47 :                                 if (sep) sep[0] = 0;
    1166             :                         }
    1167             :                 }
    1168          55 :                 switch (mtype) {
    1169          14 :                 case GF_ISOM_MEDIA_VISUAL:
    1170             :                 case GF_ISOM_MEDIA_AUXV:
    1171             :                 case GF_ISOM_MEDIA_PICT:
    1172             :                 case GF_ISOM_MEDIA_AUDIO:
    1173          14 :                         skip_write_filter = codec_id ? GF_TRUE : GF_FALSE;
    1174          14 :                         break;
    1175          41 :                 default:
    1176          41 :                         switch (codec_id) {
    1177           3 :                         case GF_CODECID_WEBVTT:
    1178             :                                 skip_write_filter = GF_TRUE;
    1179           3 :                                 break;
    1180          35 :                         case GF_CODECID_META_TEXT:
    1181             :                         case GF_CODECID_META_XML:
    1182             :                         case GF_CODECID_SUBS_TEXT:
    1183             :                         case GF_CODECID_SUBS_XML:
    1184             :                         case GF_CODECID_SIMPLE_TEXT:
    1185             :                                 //use dynamic extension
    1186          35 :                                 szExt[0] = 0;
    1187             :                                 use_dynext = GF_TRUE;
    1188          35 :                                 break;
    1189             :                         }
    1190             :                         break;
    1191             :                 }
    1192             :                 //TODO, move these two to filters one of these days
    1193          55 :                 if ((codec_id==GF_CODECID_VORBIS) || (codec_id==GF_CODECID_THEORA) || (codec_id==GF_CODECID_OPUS)) {
    1194           2 :                         char *outname = dumper->out_name;
    1195           2 :                         if (outname && !strcmp(outname, "std")) outname=NULL;
    1196           2 :                         if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
    1197             : #ifndef GPAC_DISABLE_AV_PARSERS
    1198           2 :                         return gf_dump_to_ogg(dumper, outname, track_num);
    1199             : #else
    1200             :                         return GF_NOT_SUPPORTED;
    1201             : #endif
    1202             : 
    1203             :                 }
    1204          53 :                 if (codec_id==GF_CODECID_SUBPIC) {
    1205             : #ifndef GPAC_DISABLE_AV_PARSERS
    1206             :                         char *dsi = NULL;
    1207             :                         u32 dsi_size = 0;
    1208           1 :                         if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo) {
    1209           1 :                                 dsi = esd->decoderConfig->decoderSpecificInfo->data;
    1210           1 :                                 dsi_size = esd->decoderConfig->decoderSpecificInfo->dataLength;
    1211             :                         }
    1212           1 :                         e = gf_dump_to_vobsub(dumper, dumper->out_name, track_num, dsi, dsi_size);
    1213             : #else
    1214             :                         e = GF_NOT_SUPPORTED;
    1215             : #endif
    1216           1 :                         if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
    1217           1 :                         return e;
    1218             :                 }
    1219          52 :                 if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
    1220             :         } else {
    1221           5 :                 const char *export_ext = dumper->out_name ? gf_file_ext_start(dumper->out_name) : NULL;
    1222             :                 skip_write_filter = GF_TRUE;
    1223           5 :                 if (!export_ext)
    1224             :                         use_dynext = GF_TRUE;
    1225             :         }
    1226             : 
    1227          57 :         fsess = gf_fs_new_defaults(0);
    1228          57 :         if (!fsess) {
    1229           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Failed to create filter session\n"));
    1230             :                 return GF_OUT_OF_MEM;
    1231             :         }
    1232             :         file_out = NULL;
    1233          57 :         args = NULL;
    1234             : 
    1235          57 :         if (dumper->flags & GF_EXPORT_REMUX) {
    1236           0 :                 file_out = gf_fs_load_destination(fsess, dumper->out_name, NULL, NULL, &e);
    1237           0 :                 if (!file_out) {
    1238           0 :                         gf_fs_del(fsess);
    1239           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot open destination %s\n", dumper->out_name));
    1240           0 :                         return e;
    1241             :                 }
    1242             :         }
    1243             :         //except in nhml inband file dump, create a sink filter
    1244          57 :         else if (!dumper->dump_file) {
    1245          57 :                 Bool no_ext = (dumper->flags & GF_EXPORT_NO_FILE_EXT) ? GF_TRUE : GF_FALSE;
    1246          57 :                 char *ext = gf_file_ext_start(dumper->out_name);
    1247             :                 //mux args, for now we only dump to file
    1248          57 :                 e = gf_dynstrcat(&args, "fout:dst=", NULL);
    1249          57 :                 e |= gf_dynstrcat(&args, dumper->out_name, NULL);
    1250             : 
    1251          57 :                 if (dumper->flags & GF_EXPORT_NHNT) {
    1252             :                         strcpy(szExt, "nhnt");
    1253           1 :                         e |= gf_dynstrcat(&args, ":clone", NULL);
    1254             :                         no_ext = GF_TRUE;
    1255           1 :                         if (!ext)
    1256           1 :                                 e |= gf_dynstrcat(&args, ":dynext", NULL);
    1257          56 :                 } else if (dumper->flags & GF_EXPORT_NHML) {
    1258             :                         strcpy(szExt, "nhml");
    1259          13 :                         e |= gf_dynstrcat(&args, ":clone", NULL);
    1260             :                         no_ext = GF_TRUE;
    1261          13 :                         if (!ext)
    1262          13 :                                 e |= gf_dynstrcat(&args, ":dynext", NULL);
    1263             :                 }
    1264             : 
    1265          57 :                 if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
    1266          14 :                         if (!dumper->sample_num) {
    1267             : 
    1268          13 :                                 ext = gf_file_ext_start(args);
    1269          13 :                                 if (ext) ext[0] = 0;
    1270          13 :                                 if (sample_count>=1000) {
    1271           0 :                                         e |= gf_dynstrcat(&args, "_$num%08d$", NULL);
    1272          13 :                                 } else if (sample_count) {
    1273          13 :                                         e |= gf_dynstrcat(&args, "_$num%03d$", NULL);
    1274             :                                 } else {
    1275           0 :                                         e |= gf_dynstrcat(&args, "_$num$", NULL);
    1276             :                                 }
    1277          13 :                                 ext = gf_file_ext_start(dumper->out_name);
    1278          13 :                                 if (ext) e |= gf_dynstrcat(&args, ext, NULL);
    1279             :                         }
    1280          14 :                         e |= gf_dynstrcat(&args, ":dynext", NULL);
    1281          43 :                 } else if (dumper->trackID && strlen(szExt) ) {
    1282          19 :                         if (!no_ext && !gf_file_ext_start(dumper->out_name)) {
    1283           0 :                                 if (args) gf_free(args);
    1284           0 :                                 args=NULL;
    1285           0 :                                 e = gf_dynstrcat(&args, "fout:dst=", NULL);
    1286           0 :                                 e |= gf_dynstrcat(&args, dumper->out_name, NULL);
    1287           0 :                                 e |= gf_dynstrcat(&args, szExt, ".");
    1288             :                         } else {
    1289          19 :                                 e |= gf_dynstrcat(&args, ":ext=", NULL);
    1290          19 :                                 e |= gf_dynstrcat(&args, szExt, NULL);
    1291             :                         }
    1292          24 :                 } else if ((dumper->trackID || dumper->track_type) && use_dynext) {
    1293          12 :                         e |= gf_dynstrcat(&args, ":dynext", NULL);
    1294             :                 }
    1295          57 :                 if (e) {
    1296           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load arguments for output file dumper\n"));
    1297           0 :                         if (args) gf_free(args);
    1298           0 :                         gf_fs_del(fsess);
    1299           0 :                         return e;
    1300             :                 }
    1301             : 
    1302          57 :                 file_out = gf_fs_load_filter(fsess, args, &e);
    1303          57 :                 if (!file_out) {
    1304           0 :                         gf_fs_del(fsess);
    1305           0 :                         if (args) gf_free(args);
    1306           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load output file dumper\n"));
    1307           0 :                         return e;
    1308             :                 }
    1309             :         }
    1310          57 :         if (args) gf_free(args);
    1311          57 :         args = NULL;
    1312             : 
    1313             :         //raw sample frame, force loading filter generic write in frame mode
    1314          57 :         if (dumper->flags & GF_EXPORT_RAW_SAMPLES) {
    1315          14 :                 e = gf_dynstrcat(&args, "writegen:frame", NULL);
    1316          14 :                 if (dumper->sample_num) {
    1317             :                         sprintf(szSubArgs, ":sstart=%d:send=%d", dumper->sample_num, dumper->sample_num);
    1318           1 :                         e |= gf_dynstrcat(&args, szSubArgs, NULL);
    1319             :                 }
    1320          14 :                 remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
    1321          14 :                 if (!remux || e) {
    1322           0 :                         gf_fs_del(fsess);
    1323           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
    1324           0 :                         if (args) gf_free(args);
    1325           0 :                         return e ? e : GF_FILTER_NOT_FOUND;
    1326             :                 }
    1327             :         }
    1328          43 :         else if (dumper->flags & GF_EXPORT_NHNT) {
    1329           1 :                 remux = gf_fs_load_filter(fsess, "nhntw:exporter", &e);
    1330           1 :                 if (!remux) {
    1331           0 :                         gf_fs_del(fsess);
    1332           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHNT write filter\n"));
    1333           0 :                         return e;
    1334             :                 }
    1335             :         }
    1336          42 :         else if (dumper->flags & GF_EXPORT_NHML) {
    1337          13 :                 e = gf_dynstrcat(&args, "nhmlw:exporter:name=", NULL);
    1338          13 :                 e |= gf_dynstrcat(&args, dumper->out_name, NULL);
    1339          13 :                 if (dumper->flags & GF_EXPORT_NHML_FULL)
    1340           1 :                         e |= gf_dynstrcat(&args, ":pckp", NULL);
    1341          13 :                 if (dumper->dump_file) {
    1342             :                         sprintf(szSubArgs, ":nhmlonly:filep=%p", dumper->dump_file);
    1343           0 :                         e |= gf_dynstrcat(&args, szSubArgs, NULL);
    1344             :                 }
    1345          13 :                 remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
    1346          13 :                 if (!remux || e) {
    1347           0 :                         gf_fs_del(fsess);
    1348           0 :                         if (args) gf_free(args);
    1349           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load NHML write filter\n"));
    1350           0 :                         return e ? e : GF_FILTER_NOT_FOUND;
    1351             :                 }
    1352          29 :         } else if (!skip_write_filter) {
    1353          13 :                 e = gf_dynstrcat(&args, "writegen:exporter", NULL);
    1354             :                 //extension has been forced, override ext at output of writegen
    1355          13 :                 if (ext_forced) {
    1356           0 :                         e |= gf_dynstrcat(&args, ":#Extension=", NULL);
    1357           0 :                         e |= gf_dynstrcat(&args, szExt, NULL);
    1358             :                 }
    1359             : 
    1360          13 :                 remux = e ? NULL : gf_fs_load_filter(fsess, args, &e);
    1361          13 :                 if (!remux) {
    1362           0 :                         gf_fs_del(fsess);
    1363           0 :                         if (args) gf_free(args);
    1364           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load stream->file filter\n"));
    1365           0 :                         return e;
    1366             :                 }
    1367             :         }
    1368          57 :         if (args) gf_free(args);
    1369          57 :         args = NULL;
    1370             : 
    1371             :         //force a reframer filter, connected to our input
    1372          57 :         e = gf_dynstrcat(&args, "reframer:SID=1", NULL);
    1373          57 :         if (dumper->trackID) {
    1374             :                 sprintf(szSubArgs, "#PID=%d", dumper->trackID);
    1375          52 :                 e |= gf_dynstrcat(&args, szSubArgs, NULL);
    1376             :         }
    1377          57 :         e |= gf_dynstrcat(&args, ":exporter", NULL);
    1378          57 :         if (dumper->flags & GF_EXPORT_SVC_LAYER)
    1379           0 :                 e |= gf_dynstrcat(&args, ":extract=layer", NULL);
    1380          57 :         if (dumper->flags & GF_EXPORT_WEBVTT_NOMERGE)
    1381           0 :                 e |= gf_dynstrcat(&args, ":merge", NULL);
    1382             : 
    1383          57 :         reframer = gf_fs_load_filter(fsess, args, &e);
    1384          57 :         if (!reframer || e) {
    1385           0 :                 gf_fs_del(fsess);
    1386           0 :                 if (args) gf_free(args);
    1387           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load reframer filter\n"));
    1388           0 :                 return e ? e : GF_FILTER_NOT_FOUND;
    1389             :         }
    1390          57 :         if (args) gf_free(args);
    1391          57 :         args = NULL;
    1392             : 
    1393             :         //we already have the file loaded, directly load the mp4dmx filter with this file
    1394          57 :         if (dumper->file) {
    1395             :                 //we want to expose every track
    1396          57 :                 e = gf_dynstrcat(&args, "mp4dmx:FID=1:noedit:alltk:allt", NULL);
    1397          57 :                 if (!e) {
    1398          57 :                         sprintf(szSubArgs, ":mov=%p", dumper->file);
    1399          57 :                         e = gf_dynstrcat(&args, szSubArgs, NULL);
    1400             :                 }
    1401             :                 
    1402             :                 //we want to expose every track
    1403          57 :                 src_filter = gf_fs_load_filter(fsess, args, &e);
    1404             : 
    1405          57 :                 gf_free(args);
    1406          57 :                 args = NULL;
    1407             :         } else {
    1408             :                 //we want to expose every track
    1409           0 :                 src_filter = gf_fs_load_source(fsess, dumper->in_name, "FID=1:noedit:alltk:allt", NULL, &e);
    1410             :         }
    1411             : 
    1412          57 :         if (!src_filter || e) {
    1413           0 :                 gf_fs_del(fsess);
    1414           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Exporter] Cannot load filter for input file \"%s\": %s\n", dumper->in_name, gf_error_to_string(e) ));
    1415           0 :                 return e;
    1416             :         }
    1417             : 
    1418          57 :         if (dumper->track_type) {
    1419           0 :                 const char *mtype = (dumper->track_type==1) ? "video" : "audio";
    1420           0 :                 if (dumper->trackID) {
    1421             :                         sprintf(szSubArgs, "%s%d", mtype, dumper->trackID);
    1422             :                 } else {
    1423             :                         sprintf(szSubArgs, "%s", mtype);
    1424             :                 }
    1425             :         }
    1426          57 :         else if (dumper->trackID) {
    1427             :                 sprintf(szSubArgs, "PID=%d", dumper->trackID);
    1428             :         }
    1429          57 :         if (remux) {
    1430          41 :                 gf_filter_set_source(file_out, remux, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
    1431          41 :                 gf_filter_set_source(remux, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
    1432             :         } else {
    1433          16 :                 gf_filter_set_source(file_out, reframer, (dumper->trackID || dumper->track_type) ? szSubArgs : NULL);
    1434             :         }
    1435             : 
    1436          57 :         e = gf_fs_run(fsess);
    1437          57 :         if (e>GF_OK) e = GF_OK;
    1438          57 :         if (!e) e = gf_fs_get_last_connect_error(fsess);
    1439          57 :         if (!e) e = gf_fs_get_last_process_error(fsess);
    1440             : 
    1441          57 :         if (!e) {
    1442          57 :                 if (dumper->file)
    1443          57 :                         gf_fs_print_unused_args(fsess, NULL);
    1444             :                 else
    1445           0 :                         gf_fs_print_unused_args(fsess, "alltk,allt,noedit");
    1446             :         }
    1447          57 :         gf_fs_print_non_connected(fsess);
    1448          57 :         if (dumper->print_stats_graph & 1) gf_fs_print_stats(fsess);
    1449          57 :         if (dumper->print_stats_graph & 2) gf_fs_print_connections(fsess);
    1450          57 :         gf_fs_del(fsess);
    1451          57 :         return e;
    1452             : }
    1453             : 
    1454             : GF_EXPORT
    1455          64 : GF_Err gf_media_export(GF_MediaExporter *dumper)
    1456             : {
    1457          64 :         if (!dumper) return GF_BAD_PARAM;
    1458          64 :         if (!dumper->out_name && !(dumper->flags & GF_EXPORT_PROBE_ONLY) && !dumper->dump_file) return GF_BAD_PARAM;
    1459             : 
    1460             :         //internal export not using filters
    1461             : 
    1462             : #ifndef GPAC_DISABLE_ISOM_WRITE
    1463          64 :         if (dumper->flags & GF_EXPORT_MP4) return gf_media_export_isom(dumper);
    1464             : #endif /*GPAC_DISABLE_ISOM_WRITE*/
    1465             : #ifndef GPAC_DISABLE_VTT
    1466          63 :         else if (dumper->flags & GF_EXPORT_WEBVTT_META) return gf_media_export_webvtt_metadata(dumper);
    1467             : #endif
    1468          62 :         else if (dumper->flags & GF_EXPORT_SIX) return gf_media_export_six(dumper);
    1469             : 
    1470             :         //the following ones should be moved to muxing filters
    1471          61 :         else if (dumper->flags & GF_EXPORT_SAF) return gf_media_export_saf(dumper);
    1472             : 
    1473             :         //the rest is handled by the generic exporter
    1474          60 :         return gf_media_export_filters(dumper);
    1475             : }
    1476             : 
    1477             : #endif /*GPAC_DISABLE_MEDIA_EXPORT*/

Generated by: LCOV version 1.13