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*/
|