blob: d07dd6da26e80bbf914599ba5de376494d144ea7 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre, Romain Bouqueau, Cyril Concolato
* Copyright (c) Telecom ParisTech 2000-2026
* All rights reserved
*
* This file is part of GPAC / Media Tools sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/filters.h>
#include <gpac/constants.h>
#include <gpac/internal/media_dev.h>
#include <gpac/xml.h>
#include <gpac/network.h>
#ifndef GPAC_DISABLE_ISOM
static void gf_media_update_bitrate_ex(GF_ISOFile *file, u32 track, Bool use_esd)
{
#ifndef GPAC_DISABLE_ISOM_WRITE
u32 i, count, timescale, db_size, cdur, csize;
u64 time_wnd, max_rate, avg_rate, bitrate;
Double br;
GF_ISOSample sample;
db_size = 0;
max_rate = avg_rate = time_wnd = bitrate = 0;
csize = 0;
cdur = 0;
if (gf_isom_get_media_type(file, track)==GF_ISOM_MEDIA_AUDIO) {
csize = gf_isom_get_constant_sample_size(file, track);
cdur = gf_isom_get_constant_sample_duration(file, track);
if (cdur > 1) cdur = 0;
}
memset(&sample, 0, sizeof(GF_ISOSample));
timescale = gf_isom_get_media_timescale(file, track);
count = gf_isom_get_sample_count(file, track);
if (csize && cdur) {
db_size = 0;
avg_rate = 8 * csize * timescale / cdur;
bitrate = avg_rate;
} else {
u32 rate = 0;
for (i=0; i<count; i++) {
u32 di;
GF_ISOSample *samp = gf_isom_get_sample_info_ex(file, track, i+1, &di, NULL, &sample);
if (!samp) break;
if (samp->dataLength > db_size) db_size = samp->dataLength;
avg_rate += samp->dataLength;
rate += samp->dataLength;
if (samp->DTS > time_wnd + timescale) {
if (rate > max_rate) max_rate = rate;
time_wnd = samp->DTS;
rate = 0;
}
}
}
br = (Double) (s64) gf_isom_get_media_duration(file, track);
br /= timescale;
if (br>0) {
GF_ESD *esd = NULL;
if (!csize || !cdur) {
bitrate = GF_FLOAT_TO_U32((Double) (s64)avg_rate / br);
bitrate *= 8;
max_rate *= 8;
}
if (!max_rate) max_rate = bitrate;
if (use_esd) esd = gf_isom_get_esd(file, track, 1);
if (esd && esd->decoderConfig) {
esd->decoderConfig->avgBitrate = (u32) bitrate;
esd->decoderConfig->maxBitrate = (u32) max_rate;
esd->decoderConfig->bufferSizeDB = db_size;
gf_isom_change_mpeg4_description(file, track, 1, esd);
} else {
/*move to bps*/
gf_isom_update_bitrate(file, track, 1, (u32) bitrate, (u32) max_rate, db_size);
}
if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
}
#endif
}
GF_EXPORT
void gf_media_update_bitrate(GF_ISOFile *file, u32 track)
{
gf_media_update_bitrate_ex(file, track, GF_FALSE);
}
#endif
void gf_media_get_video_timing(Double fps, u32 *timescale, u32 *dts_inc)
{
u32 fps_1000 = (u32) (fps*1000 + 0.5);
/*handle all drop-frame formats*/
if (fps_1000==29970) {
*timescale = 30000;
*dts_inc = 1001;
}
else if (fps_1000==23976) {
*timescale = 24000;
*dts_inc = 1001;
}
else if (fps_1000==59940) {
*timescale = 60000;
*dts_inc = 1001;
} else {
*timescale = fps_1000;
*dts_inc = 1000;
}
}
GF_EXPORT
u32 gf_dolby_vision_level(u32 width, u32 height, u64 fps_num, u64 fps_den, u32 codecid)
{
u32 dv_level = 0;
u32 fps = fps_den ? (u32) (fps_num/fps_den) : 25;
u64 level_check = width;
level_check *= height * fps;
if (codecid==GF_CODECID_AVC) {
if (level_check <= 1280*720*24) dv_level = 1;
else if (level_check <= 1280*720*30) dv_level = 2;
else if (level_check <= 1920*1080*24) dv_level = 3;
else if (level_check <= 1920*1080*30) dv_level = 4;
else if (level_check <= 1920*1080*60) dv_level = 5;
else if (level_check <= 3840*2160*24) dv_level = 6;
else if (level_check <= 3840*2160*30) dv_level = 7;
else if (level_check <= 3840*2160*48) dv_level = 8;
else if (level_check <= 3840*2160*60) dv_level = 9;
} else {
if (level_check <= 1280*720*24) dv_level = 1;
else if (level_check<= 1280*720*30) dv_level = 2;
else if (level_check <= 1920*1080*24) dv_level = 3;
else if (level_check <= 1920*1080*30) dv_level = 4;
else if (level_check <= 1920*1080*60) dv_level = 5;
else if (level_check <= 3840*2160*24) dv_level = 6;
else if (level_check <= 3840*2160*30) dv_level = 7;
else if (level_check <= 3840*2160*48) dv_level = 8;
else if (level_check <= 3840*2160*60) dv_level = 9;
else if (level_check <= 3840*2160*120) {
if (width == 7680) dv_level = 11;
else dv_level = 10;
}
else if (level_check <= 7680*4320*60) dv_level = 12;
else dv_level = 13;
}
return dv_level;
}
#ifndef GPAC_DISABLE_MEDIA_IMPORT
GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...)
{
#ifndef GPAC_DISABLE_LOG
if (gf_log_tool_level_on(GF_LOG_APP, e ? GF_LOG_WARNING : GF_LOG_INFO)) {
va_list args;
char szMsg[1024];
va_start(args, format);
vsnprintf(szMsg, 1024, format, args);
va_end(args);
GF_LOG((u32) (e ? GF_LOG_WARNING : GF_LOG_INFO), GF_LOG_APP, ("%s\n", szMsg) );
}
#endif
return e;
}
static GF_Err gf_import_afx_sc3dmc(GF_MediaImporter *import, Bool mult_desc_allowed)
{
GF_Err e;
Bool destroy_esd;
u32 size, track, di, dsi_len;
GF_ISOSample *samp;
u32 codecid;
char *dsi, *data;
if (import->flags & GF_IMPORT_PROBE_ONLY) {
import->tk_info[0].track_num = 1;
import->tk_info[0].stream_type = GF_STREAM_SCENE;
import->tk_info[0].codecid = GF_CODECID_AFX;
import->nb_tracks = 1;
return GF_OK;
}
e = gf_file_load_data(import->in_name, (u8 **) &data, &size);
if (e) return gf_import_message(import, e, "Opening file %s failed", import->in_name);
if ((s32) size < 0) return GF_IO_ERR;
codecid = GF_CODECID_AFX;
dsi = (char *)gf_malloc(1);
dsi_len = 1;
dsi[0] = GPAC_AFX_SCALABLE_COMPLEXITY;
destroy_esd = GF_FALSE;
if (!import->esd) {
import->esd = gf_odf_desc_esd_new(0);
destroy_esd = GF_TRUE;
}
/*update stream type/oti*/
if (!import->esd->decoderConfig) import->esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG);
if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
import->esd->decoderConfig->streamType = GF_STREAM_SCENE;
import->esd->decoderConfig->objectTypeIndication = codecid;
import->esd->decoderConfig->bufferSizeDB = size;
import->esd->decoderConfig->avgBitrate = 8*size;
import->esd->decoderConfig->maxBitrate = 8*size;
import->esd->slConfig->timestampResolution = 1000;
if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
if (import->esd->decoderConfig->decoderSpecificInfo->data) gf_free(import->esd->decoderConfig->decoderSpecificInfo->data);
import->esd->decoderConfig->decoderSpecificInfo->data = dsi;
import->esd->decoderConfig->decoderSpecificInfo->dataLength = dsi_len;
track = 0;
if (mult_desc_allowed)
track = gf_isom_get_track_by_id(import->dest, import->esd->ESID);
if (!track)
track = gf_isom_new_track(import->dest, import->esd->ESID, GF_ISOM_MEDIA_SCENE, 1000);
if (!track) {
e = gf_isom_last_error(import->dest);
goto exit;
}
gf_isom_set_track_enabled(import->dest, track, GF_TRUE);
if (!import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track);
import->final_trackID = import->esd->ESID;
if (import->source_magic)
gf_isom_set_track_magic(import->dest, track, import->source_magic);
e = gf_isom_new_mpeg4_description(import->dest, track, import->esd, (import->flags & GF_IMPORT_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
if (e) goto exit;
//gf_isom_set_visual_info(import->dest, track, di, w, h);
samp = gf_isom_sample_new();
samp->IsRAP = RAP;
samp->dataLength = size;
if (import->initial_time_offset) samp->DTS = (u64) (import->initial_time_offset*1000);
gf_import_message(import, GF_OK, "%s import %s", "SC3DMC", import->in_name);
/*we must start a track from DTS = 0*/
if (!gf_isom_get_sample_count(import->dest, track) && samp->DTS) {
/*todo - we could add an edit list*/
samp->DTS=0;
}
gf_set_progress("Importing SC3DMC", 0, 1);
if (import->flags & GF_IMPORT_USE_DATAREF) {
e = gf_isom_add_sample_reference(import->dest, track, di, samp, (u64) 0);
} else {
samp->data = data;
e = gf_isom_add_sample(import->dest, track, di, samp);
samp->data = NULL;
}
gf_set_progress("Importing SC3DMC", 1, 1);
gf_isom_sample_del(&samp);
gf_isom_update_duration(import->dest);
exit:
gf_free(data);
if (import->esd && destroy_esd) {
gf_odf_desc_del((GF_Descriptor *) import->esd);
import->esd = NULL;
}
return e;
}
static GF_Err gf_import_isomedia_track(GF_MediaImporter *import)
{
GF_Err e;
u64 offset, sampDTS, duration, dts_offset;
Bool is_nalu_video=GF_FALSE;
u32 inject_rap_sample_group = 0;
u32 track, di, trackID, track_in, i, num_samples, mtype, w, h, sr, sbr_sr, ch, mstype, cur_extract_mode, cdur, bps;
u64 mtimescale;
s32 trans_x, trans_y;
s16 layer;
char *lang;
u8 *sai_buffer = NULL;
u32 sai_buffer_size = 0, sai_buffer_alloc = 0;
const char *orig_name = gf_url_get_resource_name(gf_isom_get_filename(import->orig));
Bool sbr, ps;
GF_ISOSample *samp;
GF_ESD *origin_esd;
GF_InitialObjectDescriptor *iod;
Bool is_cenc;
GF_ISOTrackCloneFlags clone_flags;
sampDTS = 0;
if (import->flags & GF_IMPORT_PROBE_ONLY) {
u32 track_count = gf_isom_get_track_count(import->orig);
if (track_count > GF_IMPORT_MAX_TRACKS) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Track count (%u) exceeds maximum (%u), will ignore extra tracks.\n", track_count, GF_IMPORT_MAX_TRACKS));
track_count = GF_IMPORT_MAX_TRACKS;
}
for (i=0; i<track_count; i++) {
import->tk_info[i].track_num = gf_isom_get_track_id(import->orig, i+1);
mtype = gf_isom_get_media_type(import->orig, i+1);
switch (mtype) {
case GF_ISOM_MEDIA_VISUAL:
import->tk_info[i].stream_type = GF_STREAM_VISUAL;
break;
case GF_ISOM_MEDIA_AUDIO:
import->tk_info[i].stream_type = GF_STREAM_AUDIO;
break;
case GF_ISOM_MEDIA_TEXT:
case GF_ISOM_MEDIA_SUBT:
case GF_ISOM_MEDIA_MPEG_SUBT:
import->tk_info[i].stream_type = GF_STREAM_TEXT;
break;
case GF_ISOM_MEDIA_SCENE:
import->tk_info[i].stream_type = GF_STREAM_SCENE;
break;
default:
import->tk_info[i].stream_type = mtype;
break;
}
if (import->tk_info[i].stream_type == GF_STREAM_VISUAL) {
gf_isom_get_visual_info(import->orig, i+1, 1, &import->tk_info[i].video_info.width, &import->tk_info[i].video_info.height);
} else if (import->tk_info[i].stream_type == GF_STREAM_AUDIO) {
gf_isom_get_audio_info(import->orig, i+1, 1, &import->tk_info[i].audio_info.sample_rate, &import->tk_info[i].audio_info.nb_channels, NULL);
}
lang = NULL;
gf_isom_get_media_language(import->orig, i+1, &lang);
if (lang) {
import->tk_info[i].lang = GF_4CC(' ', lang[0], lang[1], lang[2]);
gf_free(lang);
lang = NULL;
}
gf_media_get_rfc_6381_codec_name(import->orig, i+1, 1, import->tk_info[i].szCodecProfile, GF_FALSE, GF_FALSE);
import->nb_tracks ++;
}
return GF_OK;
}
trackID = import->trackID;
if (!trackID) {
if (gf_isom_get_track_count(import->orig) != 1) return gf_import_message(import, GF_BAD_PARAM, "Several tracks in MP4 - please indicate track to import");
trackID = gf_isom_get_track_id(import->orig, 1);
}
track_in = gf_isom_get_track_by_id(import->orig, trackID);
if (!track_in) return gf_import_message(import, GF_URL_ERROR, "Cannot find track ID %d in file", trackID);
origin_esd = gf_isom_get_esd(import->orig, track_in, 1);
if (import->esd && origin_esd) {
origin_esd->OCRESID = import->esd->OCRESID;
/*there may be other things to import...*/
}
ps = GF_FALSE;
sbr = GF_FALSE;
sbr_sr = 0;
w = h = 0;
cur_extract_mode = gf_isom_get_nalu_extract_mode(import->orig, track_in);
iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(import->orig);
if (iod && (iod->tag != GF_ODF_IOD_TAG)) {
gf_odf_desc_del((GF_Descriptor *) iod);
iod = NULL;
}
mtype = gf_isom_get_media_type(import->orig, track_in);
if (mtype==GF_ISOM_MEDIA_VISUAL) {
Bool skip_profile=GF_FALSE;
u8 PL = iod ? iod->visual_profileAndLevel : 0xFE;
gf_isom_get_visual_info(import->orig, track_in, 1, &w, &h);
/*for MPEG-4 visual, always check size (don't trust input file)*/
if (origin_esd && origin_esd->decoderConfig) {
switch (origin_esd->decoderConfig->objectTypeIndication) {
#ifndef GPAC_DISABLE_AV_PARSERS
case GF_CODECID_MPEG4_PART2:
if (origin_esd->decoderConfig->decoderSpecificInfo) {
GF_M4VDecSpecInfo dsi;
gf_m4v_get_config(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
w = dsi.width;
h = dsi.height;
PL = dsi.VideoPL;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Missing DecoderSpecificInfo in MPEG-4 Visual (Part2) stream\n"));
}
break;
#endif
case GF_CODECID_MPEG1:
case GF_CODECID_MPEG2_422:
case GF_CODECID_MPEG2_SNR:
case GF_CODECID_MPEG2_HIGH:
case GF_CODECID_MPEG2_MAIN:
case GF_CODECID_MPEG2_SIMPLE:
case GF_CODECID_MPEG2_SPATIAL:
case GF_CODECID_AVC:
case GF_CODECID_SVC:
case GF_CODECID_MVC:
break;
default:
skip_profile=GF_TRUE;
break;
}
}
if (!skip_profile)
gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, PL);
}
else if (mtype==GF_ISOM_MEDIA_AUDIO) {
Bool skip_profile = GF_FALSE;
u8 PL = iod ? iod->audio_profileAndLevel : 0xFE;
bps = 16;
sr = ch = sbr_sr = 0;
sbr = GF_FALSE;
ps = GF_FALSE;
gf_isom_get_audio_info(import->orig, track_in, 1, &sr, &ch, &bps);
if (origin_esd && origin_esd->decoderConfig) {
switch (origin_esd->decoderConfig->objectTypeIndication) {
case GF_CODECID_AAC_MPEG4:
case GF_CODECID_AAC_MPEG2_MP:
case GF_CODECID_AAC_MPEG2_LCP:
case GF_CODECID_AAC_MPEG2_SSRP:
#ifndef GPAC_DISABLE_AV_PARSERS
if (origin_esd->decoderConfig->decoderSpecificInfo) {
GF_M4ADecSpecInfo dsi;
gf_m4a_get_config(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
sr = dsi.base_sr;
if (dsi.has_sbr) sbr_sr = dsi.sbr_sr;
ch = dsi.nb_chan;
PL = dsi.audioPL;
sbr = dsi.has_sbr ? ((dsi.base_object_type==GF_M4A_AAC_SBR || dsi.base_object_type==GF_M4A_AAC_PS) ? 2 : 1) : GF_FALSE;
ps = dsi.has_ps;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Missing DecoderSpecificInfo in MPEG-4 AAC stream\n"));
}
break;
#endif
case GF_CODECID_MPEG_AUDIO:
break;
default:
skip_profile=GF_TRUE;
break;
}
}
if (!skip_profile)
gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_AUDIO, PL);
}
else if (mtype==GF_ISOM_MEDIA_SUBPIC) {
w = h = 0;
trans_x = trans_y = 0;
layer = 0;
if (origin_esd && origin_esd->decoderConfig && (origin_esd->decoderConfig->objectTypeIndication == GF_CODECID_SUBPIC)) {
gf_isom_get_track_layout_info(import->orig, track_in, &w, &h, &trans_x, &trans_y, &layer);
}
}
gf_odf_desc_del((GF_Descriptor *) iod);
if ( ! gf_isom_get_track_count(import->dest)) {
u32 timescale;
if (import->moov_timescale<0)
timescale = gf_isom_get_media_timescale(import->orig, track_in);
else
timescale = gf_isom_get_timescale(import->orig);
gf_isom_set_timescale(import->dest, timescale);
}
clone_flags = GF_ISOM_CLONE_TRACK_NO_QT;
if (import->asemode == GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) {
clone_flags = 0;
} else {
const char *dst = gf_isom_get_filename(import->dest);
if (dst && strstr(dst, ".mov"))
clone_flags = 0;
}
clone_flags |= GF_ISOM_CLONE_RESET_DURATION;
if (import->flags & GF_IMPORT_USE_DATAREF)
clone_flags |= GF_ISOM_CLONE_TRACK_KEEP_DREF;
if (import->target_trackID && (import->target_trackID==(u32)-1))
clone_flags |= GF_ISOM_CLONE_TRACK_DROP_ID;
e = gf_isom_clone_track(import->orig, track_in, import->dest, clone_flags, &track);
if (e) goto exit;
if (import->target_trackID && (import->target_trackID!=(u32)-1)) {
u32 new_tk_id = import->target_trackID;
gf_isom_set_track_id(import->dest, track, new_tk_id);
}
if ((gf_isom_get_track_count(import->dest)==1) && gf_isom_has_keep_utc_times(import->dest)) {
u64 cdate, mdate;
gf_isom_get_creation_time(import->orig, &cdate, &mdate);
gf_isom_set_creation_time(import->dest, cdate, mdate);
}
di = 1;
if (import->esd && import->esd->ESID) {
e = gf_isom_set_track_id(import->dest, track, import->esd->ESID);
if (e) goto exit;
}
import->final_trackID = gf_isom_get_track_id(import->dest, track);
if (import->esd && import->esd->dependsOnESID) {
gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_DECODE, import->esd->dependsOnESID);
}
if (import->trackID && !(import->flags & GF_IMPORT_KEEP_REFS)) {
gf_isom_remove_track_references(import->dest, track);
}
if (import->source_magic)
gf_isom_set_track_magic(import->dest, track, import->source_magic);
mstype = gf_isom_get_media_subtype(import->orig, track_in, di);
switch (mtype) {
case GF_ISOM_MEDIA_VISUAL:
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - Video (size %d x %d)", orig_name, trackID, w, h);
break;
case GF_ISOM_MEDIA_AUXV:
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - Auxiliary Video (size %d x %d)", orig_name, trackID, w, h);
break;
case GF_ISOM_MEDIA_PICT:
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - Picture sequence (size %d x %d)", orig_name, trackID, w, h);
break;
case GF_ISOM_MEDIA_AUDIO:
{
if (ps) {
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - HE-AACv2 (SR %d - SBR-SR %d - %d channels)", orig_name, trackID, sr, sbr_sr, ch);
} else if (sbr) {
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - HE-AAC (SR %d - SBR-SR %d - %d channels)", orig_name, trackID, sr, sbr_sr, ch);
} else {
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - Audio (SR %d - %d channels)", orig_name, trackID, sr, ch);
}
if (import->asemode != GF_IMPORT_AUDIO_SAMPLE_ENTRY_NOT_SET) {
gf_isom_get_audio_info(import->orig, track_in, 1, &sr, &ch, &bps);
gf_isom_set_audio_info(import->dest, track, 1, sr, ch, bps, import->asemode);
}
}
break;
case GF_ISOM_MEDIA_SUBPIC:
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - VobSub (size %d x %d)", orig_name, trackID, w, h);
break;
default:
{
char szT[GF_4CC_MSIZE];
mstype = gf_isom_get_mpeg4_subtype(import->orig, track_in, di);
if (!mstype) mstype = gf_isom_get_media_subtype(import->orig, track_in, di);
gf_strcpy(szT, gf_4cc_to_str(mtype));
gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - media type \"%s:%s\"", orig_name, trackID, szT, gf_4cc_to_str(mstype));
}
break;
}
//this may happen with fragmented files
dts_offset = 0;
samp = gf_isom_get_sample_info(import->orig, track_in, 1, &di, &offset);
if (samp) {
dts_offset = samp->DTS;
gf_isom_sample_del(&samp);
}
is_cenc = gf_isom_is_cenc_media(import->orig, track_in, 0);
if (gf_isom_is_media_encrypted(import->orig, track_in, 0)) {
gf_isom_get_original_format_type(import->orig, track_in, 0, &mstype);
}
cdur = gf_isom_get_constant_sample_duration(import->orig, track_in);
gf_isom_enable_raw_pack(import->orig, track_in, 2048);
mtimescale = gf_isom_get_media_timescale(import->orig, track_in);
duration = 0;
if ((import->duration.num>0) && import->duration.den) {
duration = (u64) (((Double)import->duration.num * mtimescale) / import->duration.den);
}
gf_isom_set_nalu_extract_mode(import->orig, track_in, GF_ISOM_NALU_EXTRACT_INSPECT);
if (import->xps_inband) {
if (is_cenc ) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOM import] CENC media detected - cannot switch parameter set storage mode\n"));
} else if (import->flags & GF_IMPORT_USE_DATAREF) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOM import] Cannot switch parameter set storage mode when using data reference\n"));
} else {
switch (mstype) {
case GF_ISOM_SUBTYPE_AVC_H264:
gf_isom_set_nalu_extract_mode(import->orig, track_in, GF_ISOM_NALU_EXTRACT_INSPECT | GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG);
gf_isom_avc_set_inband_config(import->dest, track, 1, (import->xps_inband==2) ? GF_TRUE : GF_FALSE);
break;
case GF_ISOM_SUBTYPE_HVC1:
gf_isom_set_nalu_extract_mode(import->orig, track_in, GF_ISOM_NALU_EXTRACT_INSPECT | GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG);
gf_isom_hevc_set_inband_config(import->dest, track, 1, (import->xps_inband==2) ? GF_TRUE : GF_FALSE);
break;
case GF_ISOM_SUBTYPE_VVC1:
gf_isom_set_nalu_extract_mode(import->orig, track_in, GF_ISOM_NALU_EXTRACT_INSPECT | GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG);
gf_isom_vvc_set_inband_config(import->dest, track, 1, (import->xps_inband==2) ? GF_TRUE : GF_FALSE);
break;
}
}
}
switch (mstype) {
case GF_ISOM_SUBTYPE_AVC_H264:
case GF_ISOM_SUBTYPE_SVC_H264:
case GF_ISOM_SUBTYPE_MVC_H264:
case GF_ISOM_SUBTYPE_AVC2_H264:
case GF_ISOM_SUBTYPE_AVC3_H264:
case GF_ISOM_SUBTYPE_AVC4_H264:
case GF_ISOM_SUBTYPE_HEV1:
case GF_ISOM_SUBTYPE_HVC1:
case GF_ISOM_SUBTYPE_LHE1:
case GF_ISOM_SUBTYPE_LHV1:
case GF_ISOM_SUBTYPE_HVT1:
case GF_ISOM_SUBTYPE_VVC1:
case GF_ISOM_SUBTYPE_VVI1:
is_nalu_video = GF_TRUE;
break;
}
num_samples = gf_isom_get_sample_count(import->orig, track_in);
if (is_cenc) {
u32 container_type;
e = gf_isom_cenc_get_sample_aux_info(import->orig, track_in, 0, 1, &container_type, NULL, NULL);
if (e)
goto exit;
if (container_type==GF_ISOM_BOX_UUID_PSEC) {
e = gf_isom_piff_allocate_storage(import->dest, track, 0, 0, NULL);
} else {
e = gf_isom_cenc_allocate_storage(import->dest, track);
}
if (e) goto exit;
e = gf_isom_clone_pssh(import->dest, import->orig, GF_FALSE);
if (e) goto exit;
}
for (i=0; i<num_samples; i++) {
if (import->flags & GF_IMPORT_USE_DATAREF) {
samp = gf_isom_get_sample_info(import->orig, track_in, i+1, &di, &offset);
if (!samp) {
e = gf_isom_last_error(import->orig);
goto exit;
}
samp->DTS -= dts_offset;
if (duration && !gf_sys_old_arch_compat() && gf_timestamp_greater_or_equal(samp->DTS, mtimescale, import->duration.num, import->duration.den)) {
gf_isom_sample_del(&samp);
break;
}
if ((import->flags & GF_IMPORT_FORCE_SYNC) && ((samp->IsRAP==SAP_TYPE_3) || (samp->IsRAP==SAP_TYPE_4))) {
samp->IsRAP = SAP_TYPE_1;
}
e = gf_isom_add_sample_reference(import->dest, track, di, samp, offset);
} else {
samp = gf_isom_get_sample(import->orig, track_in, i+1, &di);
if (!samp) {
/*couldn't get the sample, but still move on*/
e = gf_isom_last_error(import->orig);
if (e==GF_ISOM_INCOMPLETE_FILE) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[ISOM import] Incomplete file detected, aborting track #%d import after %d / %d samples\n", track_in, i+1, num_samples));
e = GF_OK;
}
goto exit;
}
samp->DTS -= dts_offset;
/*if packed samples (raw) and import duration is set, adjust the number of samples to add*/
if (samp->nb_pack && duration && (samp->DTS + samp->nb_pack*cdur > duration) ) {
u32 nb_samp = (u32) ( (duration - samp->DTS) / cdur);
u32 csize = samp->dataLength / samp->nb_pack;
if (!nb_samp) {
gf_isom_sample_del(&samp);
break;
}
samp->dataLength = csize*nb_samp;
samp->nb_pack = nb_samp;
duration = samp->DTS-1;
}
/*if not first sample and same DTS as previous sample, force DTS++*/
if (i && (samp->DTS<=sampDTS)) {
if (i+1 < num_samples) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[ISOM import] 0-duration sample detected at DTS %u - adjusting\n", samp->DTS));
}
samp->DTS = sampDTS + 1;
}
if (duration && !gf_sys_old_arch_compat() && gf_timestamp_greater_or_equal(samp->DTS, mtimescale, import->duration.num, import->duration.den)) {
gf_isom_sample_del(&samp);
break;
}
if ((import->flags & GF_IMPORT_FORCE_SYNC) && ((samp->IsRAP==SAP_TYPE_3) || (samp->IsRAP==SAP_TYPE_4))) {
samp->IsRAP = SAP_TYPE_1;
}
e = gf_isom_add_sample(import->dest, track, di, samp);
}
sampDTS = samp->DTS;
if (samp->nb_pack)
i+= samp->nb_pack-1;
//when importing files indicating SAP3 as sync points, we will drop all SAP3 points - check if we need to add 'rap '
//sample to group info, ie if it was not present in source track - cf #2992
if (!(import->flags & GF_IMPORT_FORCE_SYNC) && (samp->IsRAP == SAP_TYPE_3)) {
if (!inject_rap_sample_group) {
u32 sgdi=0;
gf_isom_get_sample_to_group_info(import->orig, track_in, i+1, GF_ISOM_SAMPLE_GROUP_RAP, 0, &sgdi);
inject_rap_sample_group = sgdi ? 1 : 2;
}
if (inject_rap_sample_group == 2) {
e = gf_isom_set_sample_rap_group(import->dest, track, i+1, GF_TRUE, 0);
}
}
gf_isom_sample_del(&samp);
//this will also copy all sample to group mapping, including seig for CENC
gf_isom_copy_sample_info(import->dest, track, import->orig, track_in, i+1);
if (e)
goto exit;
if (is_cenc) {
u32 container_type;
Bool Is_Encrypted;
Bool is_mkey=GF_FALSE;
u32 crypt_byte_block, skip_byte_block;
const u8 *key_info=NULL;
u32 key_info_len = 0;
e = gf_isom_get_sample_cenc_info(import->orig, track_in, i+1, &Is_Encrypted, &crypt_byte_block, &skip_byte_block, &key_info, &key_info_len);
if (e) goto exit;
if (key_info) {
is_mkey = key_info[0];
}
if (Is_Encrypted) {
sai_buffer_size = sai_buffer_alloc;
e = gf_isom_cenc_get_sample_aux_info(import->orig, track_in, i+1, di, &container_type, &sai_buffer, &sai_buffer_size);
if (e) goto exit;
if (sai_buffer_size > sai_buffer_alloc)
sai_buffer_alloc = sai_buffer_size;
e = gf_isom_track_cenc_add_sample_info(import->dest, track, container_type, sai_buffer, sai_buffer_size, is_nalu_video, GF_FALSE, is_mkey);
} else {
//we don't set container type since we don't add data to the container (senc/...)
e = gf_isom_track_cenc_add_sample_info(import->dest, track, 0, NULL, 0, is_nalu_video, GF_FALSE, is_mkey);
}
if (e)
goto exit;
}
gf_set_progress("Importing ISO File", i+1, num_samples);
if (duration && gf_sys_old_arch_compat() && (sampDTS > duration))
break;
}
//adjust last sample duration
if (i==num_samples) {
u32 dur = gf_isom_get_sample_duration(import->orig, track_in, num_samples);
gf_isom_set_last_sample_duration(import->dest, track, dur);
} else {
s64 mediaOffset;
if (gf_isom_get_edit_list_type(import->orig, track_in, &mediaOffset)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ISOBMF Import] Multiple edits found in source media, import may be broken\n"));
}
gf_isom_update_edit_list_duration(import->dest, track);
gf_isom_update_duration(import->dest);
}
if (gf_isom_has_time_offset(import->orig, track_in)==2) {
e = gf_isom_set_composition_offset_mode(import->dest, track, GF_TRUE);
if (e)
goto exit;
}
if (import->esd) {
if (!import->esd->slConfig) {
import->esd->slConfig = origin_esd ? origin_esd->slConfig : NULL;
if (origin_esd) origin_esd->slConfig = NULL;
}
if (!import->esd->decoderConfig) {
import->esd->decoderConfig = origin_esd ? origin_esd->decoderConfig : NULL;
if (origin_esd) origin_esd->decoderConfig = NULL;
}
}
gf_media_update_bitrate_ex(import->dest, track, GF_TRUE);
if (mtype == GF_ISOM_MEDIA_VISUAL) {
if (import->is_alpha) {
e = gf_isom_set_image_sequence_alpha(import->dest, track, di, GF_FALSE);
if (e) goto exit;
}
}
gf_isom_update_duration(import->dest);
exit:
if (sai_buffer) gf_free(sai_buffer);
if (origin_esd) gf_odf_desc_del((GF_Descriptor *) origin_esd);
gf_isom_set_nalu_extract_mode(import->orig, track_in, cur_extract_mode);
return e;
}
static GF_Err gf_import_isomedia(GF_MediaImporter *import)
{
u32 nb_tracks, i;
if (import->trackID)
return gf_import_isomedia_track(import);
if (!import->orig) return GF_BAD_PARAM;
nb_tracks = gf_isom_get_track_count(import->orig);
for (i=0; i<nb_tracks; i++) {
import->trackID = gf_isom_get_track_id(import->orig, i+1);
if (import->trackID) {
GF_Err e = gf_import_isomedia_track(import);
import->trackID = 0;
if (e) return e;
}
}
return GF_OK;
}
#endif
#ifndef GPAC_DISABLE_ISOM_WRITE
GF_EXPORT
GF_Err gf_media_nal_rewrite_samples(GF_ISOFile *file, u32 track, u32 new_size)
{
u32 i, count, di, remain, msize;
char *buffer;
msize = 4096;
buffer = (char*)gf_malloc(sizeof(char)*msize);
count = gf_isom_get_sample_count(file, track);
for (i=0; i<count; i++) {
GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, &di);
GF_BitStream *oldbs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
GF_BitStream *newbs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
u32 prev_size = 8*gf_isom_get_nalu_length_field(file, track, di);
if (!prev_size) return GF_NON_COMPLIANT_BITSTREAM;
remain = samp->dataLength;
while (remain) {
u32 size = gf_bs_read_int(oldbs, prev_size);
gf_bs_write_int(newbs, size, new_size);
remain -= prev_size/8;
if (size>msize) {
msize = size;
buffer = (char*)gf_realloc(buffer, sizeof(char)*msize);
}
gf_bs_read_data(oldbs, buffer, size);
gf_bs_write_data(newbs, buffer, size);
remain -= size;
}
gf_bs_del(oldbs);
gf_free(samp->data);
samp->data = NULL;
samp->dataLength = 0;
gf_bs_get_content(newbs, &samp->data, &samp->dataLength);
gf_bs_del(newbs);
gf_isom_update_sample(file, track, i+1, samp, GF_TRUE);
gf_isom_sample_del(&samp);
}
gf_free(buffer);
return GF_OK;
}
#endif
#ifndef GPAC_DISABLE_MEDIA_IMPORT
GF_EXPORT
GF_Err gf_media_import_chapters_file(GF_MediaImporter *import)
{
s32 read=0;
GF_Err e;
u32 state, offset;
u32 cur_chap;
u64 ts;
Bool found_chap = GF_FALSE;
u32 i, h, m, s, ms, fr, fps;
char line[1024];
char szTemp[1025];
char szTitle[1024];
FILE *f = gf_fopen(import->in_name, "rt");
if (!f) return GF_URL_ERROR;
read = (s32) gf_fread(line, 4, f);
if (read < 0) {
e = GF_IO_ERR;
goto err_exit;
}
if (read < 4) {
e = GF_URL_ERROR;
goto err_exit;
}
if ((line[0]==(char)(0xFF)) && (line[1]==(char)(0xFE))) {
if (!line[2] && !line[3]) {
e = GF_NOT_SUPPORTED;
goto err_exit;
}
offset = 2;
} else if ((line[0]==(char)(0xFE)) && (line[1]==(char)(0xFF))) {
if (!line[2] && !line[3]) {
e = GF_NOT_SUPPORTED;
goto err_exit;
}
offset = 2;
} else if ((line[0]==(char)(0xEF)) && (line[1]==(char)(0xBB)) && (line[2]==(char)(0xBF))) {
/*we handle UTF8 as asci*/
offset = 3;
} else {
offset = 0;
}
gf_fseek(f, offset, SEEK_SET);
if (import->flags & GF_IMPORT_PROBE_ONLY) {
Bool is_chap_or_sub = GF_FALSE;
import->nb_tracks = 0;
while (!is_chap_or_sub && (gf_fgets(line, 1024, f) != NULL)) {
char *sep;
strlwr(line);
if (strstr(line, "addchapter(")) is_chap_or_sub = GF_TRUE;
else if (strstr(line, "-->")) is_chap_or_sub = GF_TRUE;
else if ((sep = strstr(line, "chapter")) != NULL) {
sep+=7;
if (!strncmp(sep+1, "name", 4)) is_chap_or_sub = GF_TRUE;
else if (!strncmp(sep+2, "name", 4)) is_chap_or_sub = GF_TRUE;
else if (!strncmp(sep+3, "name", 4)) is_chap_or_sub = GF_TRUE;
else if (strstr(line, "Zoom") || strstr(line, "zoom")) is_chap_or_sub = GF_TRUE;
}
}
gf_fclose(f);
if (is_chap_or_sub) {
import->nb_tracks = 1;
import->tk_info[0].stream_type = GF_STREAM_TEXT;
import->tk_info[0].is_chapter = GF_TRUE;
return GF_OK;
}
return GF_NOT_SUPPORTED;
}
e = gf_isom_remove_chapter(import->dest, 0, 0);
if (e) goto err_exit;
if (!import->video_fps.num || !import->video_fps.den ) {
/*try to figure out the frame rate*/
for (i=0; i<gf_isom_get_track_count(import->dest); i++) {
GF_ISOSample *samp;
u32 timescale, inc;
u32 mtype = gf_isom_get_media_type(import->dest, i+1);
if (!gf_isom_is_video_handler_type(mtype)) continue;
if (gf_isom_get_sample_count(import->dest, i+1) < 20) continue;
samp = gf_isom_get_sample_info(import->dest, 1, 2, NULL, NULL);
inc = (u32) samp->DTS;
if (!inc) inc=1;
timescale = gf_isom_get_media_timescale(import->dest, i+1);
import->video_fps.num = timescale;
import->video_fps.den = inc;
gf_isom_sample_del(&samp);
GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("[Chapter import] Guessed video frame rate %u/%u\n", timescale, inc));
break;
}
if (!import->video_fps.num || !import->video_fps.den) {
import->video_fps.num = 25;
import->video_fps.den = 1;
}
}
cur_chap = 0;
ts = 0;
state = 0;
while (gf_fgets(line, 1024, f) != NULL) {
char *title = NULL;
u32 off = 0;
char *sL;
while (1) {
u32 len = (u32) strlen(line);
if (!len) break;
switch (line[len-1]) {
case '\n':
case '\t':
case '\r':
case ' ':
line[len-1] = 0;
continue;
}
break;
}
while (line[off]==' ') off++;
if (!strlen(line+off)) continue;
sL = line+off;
szTitle[0] = 0;
/*ZoomPlayer chapters*/
if (!strnicmp(sL, "AddChapter(", 11)) {
u32 nb_fr;
sscanf(sL, "AddChapter(%u,%1023s)", &nb_fr, szTitle);
ts = gf_timestamp_rescale(nb_fr, import->video_fps.num, 1000 * import->video_fps.den);
sL = strchr(sL, ',');
gf_strcpy(szTitle, sL+1);
sL = strrchr(szTitle, ')');
if (sL) sL[0] = 0;
} else if (!strnicmp(sL, "AddChapterBySecond(", 19)) {
u32 nb_s;
sscanf(sL, "AddChapterBySecond(%u,%1023s)", &nb_s, szTitle);
ts = nb_s;
ts *= 1000;
sL = strchr(sL, ',');
gf_strcpy(szTitle, sL+1);
sL = strrchr(szTitle, ')');
if (sL) sL[0] = 0;
} else if (!strnicmp(sL, "AddChapterByTime(", 17)) {
sscanf(sL, "AddChapterByTime(%u,%u,%u,%1023s)", &h, &m, &s, szTitle);
ts = 3600*h + 60*m + s;
ts *= 1000;
sL = strchr(sL, ',');
if (sL) sL = strchr(sL+1, ',');
if (sL) sL = strchr(sL+1, ',');
if (sL) gf_strcpy(szTitle, sL+1);
sL = strrchr(szTitle, ')');
if (sL) sL[0] = 0;
}
/*regular or SMPTE time codes*/
else if ((strlen(sL)>=8) && (sL[2]==':') && (sL[5]==':')) {
title = NULL;
if (strlen(sL)==8) {
sscanf(sL, "%02u:%02u:%02u", &h, &m, &s);
ts = (h*3600 + m*60+s)*1000;
}
else {
gf_strcpy(szTemp, sL);
char *szTS = szTemp;
char *tok = strrchr(szTS, ' ');
if (tok) {
title = strchr(sL, ' ') + 1;
while (title[0]==' ') title++;
if (strlen(title)) gf_strcpy(szTitle, title);
tok[0] = 0;
}
ts = 0;
h = m = s = ms = 0;
if (sscanf(szTS, "%u:%u:%u;%u/%u", &h, &m, &s, &fr, &fps)==5) {
ts = (h*3600 + m*60+s)*1000 + 1000*fr/fps;
} else if (sscanf(szTS, "%u:%u:%u;%u", &h, &m, &s, &fr)==4) {
ts = (h*3600 + m*60+s);
ts = (s64) (((import->video_fps.num*((s64)ts) + import->video_fps.den*fr) * 1000 ) / import->video_fps.num ) ;
} else if (sscanf(szTS, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
//microseconds used
if (ms>=1000) ms /= 1000;
ts = (h*3600 + m*60+s)*1000+ms;
} else if (sscanf(szTS, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
//microseconds used
if (ms>=1000) ms /= 1000;
ts = (h*3600 + m*60+s)*1000+ms;
} else if (sscanf(szTS, "%u:%u:%u:%u", &h, &m, &s, &ms) == 4) {
//microseconds used
if (ms>=1000) ms /= 1000;
ts = (h*3600 + m*60+s)*1000+ms;
} else if (sscanf(szTS, "%u:%u:%u", &h, &m, &s) == 3) {
ts = (h*3600 + m*60+s) * 1000;
}
}
}
/*CHAPTERX= and CHAPTERXNAME=*/
else if (!strnicmp(sL, "CHAPTER", 7)) {
u32 idx;
char *str;
gf_strcpy(szTemp, sL);
str = strrchr(szTemp, '=');
if (!str) continue;
str[0] = 0;
strlwr(szTemp);
idx = cur_chap;
str = strchr(sL, '=');
str++;
if (strstr(szTemp, "name")) {
sscanf(szTemp, "chapter%uname", &idx);
gf_strcpy(szTitle, str);
if (idx!=cur_chap) {
cur_chap=idx;
state = 0;
}
state++;
} else {
sscanf(szTemp, "chapter%u", &idx);
if (idx!=cur_chap) {
cur_chap=idx;
state = 0;
}
state++;
ts = 0;
h = m = s = ms = 0;
if (sscanf(str, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
//microseconds used
if (ms>=1000) ms/=1000;
ts = (h*3600 + m*60+s)*1000+ms;
} else if (sscanf(str, "%u:%u:%u:%u", &h, &m, &s, &ms) == 4) {
//microseconds used
if (ms>=1000) ms/=1000;
ts = (h*3600 + m*60+s)*1000+ms;
} else if (sscanf(str, "%u:%u:%u", &h, &m, &s) == 3) {
ts = (h*3600 + m*60+s) * 1000;
}
}
if (state==2) {
found_chap = GF_TRUE;
e = gf_isom_add_chapter(import->dest, 0, ts, szTitle);
if (e) goto err_exit;
state = 0;
}
continue;
}
else continue;
found_chap = GF_TRUE;
if (strlen(szTitle)) {
e = gf_isom_add_chapter(import->dest, 0, ts, szTitle);
} else {
e = gf_isom_add_chapter(import->dest, 0, ts, NULL);
}
if (e) goto err_exit;
}
err_exit:
gf_fclose(f);
if (!found_chap) return GF_NOT_FOUND;
return e;
}
GF_EXPORT
GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, GF_Fraction import_fps, Bool use_qt)
{
GF_Err e;
u32 i;
GF_MediaImporter *import = NULL;
//remove all chapter info
gf_isom_remove_chapter(file, 0, 0);
restart_check:
//remove all chapter tracks
for (i=0; i<gf_isom_get_track_count(file); i++) {
if (gf_isom_get_reference_count(file, i+1, GF_ISOM_REF_CHAP)) {
u32 chap_track=0;
gf_isom_get_reference(file, i+1, GF_ISOM_REF_CHAP, 1, &chap_track);
if (chap_track) {
gf_isom_remove_track(file, chap_track);
goto restart_check;
}
}
}
GF_SAFEALLOC(import, GF_MediaImporter);
import->dest = file;
import->in_name = chap_file;
import->video_fps = import_fps;
import->streamFormat = "CHAP";
e = gf_media_import(import);
if (e || !import->final_trackID) {
gf_free(import);
return e;
}
if (use_qt) {
u32 chap_track = gf_isom_get_track_by_id(file, import->final_trackID);
u32 nb_sdesc = gf_isom_get_sample_description_count(file, chap_track);
for (i=0; i<nb_sdesc; i++) {
gf_isom_set_media_subtype(file, chap_track, i+1, GF_ISOM_SUBTYPE_TEXT);
}
}
//imported chapter is a track, set reference
for (i=0; i<gf_isom_get_track_count(file); i++) {
u32 mtype = gf_isom_get_media_type(file, i+1);
switch (mtype) {
case GF_ISOM_MEDIA_VISUAL:
case GF_ISOM_MEDIA_AUXV:
case GF_ISOM_MEDIA_PICT:
case GF_ISOM_MEDIA_AUDIO:
gf_isom_set_track_reference(file, i+1, GF_ISOM_REF_CHAP, import->final_trackID);
break;
}
}
gf_free(import);
return GF_OK;
}
static Bool on_import_setup_failure(GF_Filter *f, void *on_setup_error_udta, GF_Err e)
{
GF_MediaImporter *importer = (GF_MediaImporter *)on_setup_error_udta;
if (importer)
importer->last_error = e;
return GF_FALSE;
}
GF_EXPORT
GF_Err gf_media_import(GF_MediaImporter *importer)
{
GF_Err e;
u32 i, count;
GF_FilterSession *fsess=NULL;
char *args = NULL;
char szSubArg[1024];
char szFilterID[20];
Bool source_id_set = GF_FALSE;
Bool source_is_isom = GF_FALSE;
GF_Filter *isobmff_mux, *source;
GF_Filter *filter_orig;
char *ext;
char *fmt = NULL;
if (!importer || (!importer->dest && (importer->flags!=GF_IMPORT_PROBE_ONLY)) || (!importer->in_name && !importer->orig) ) return GF_BAD_PARAM;
if (importer->orig) return gf_import_isomedia(importer);
if (importer->force_ext) {
ext = importer->force_ext;
} else {
ext = gf_file_ext_start(importer->in_name);
if (!ext) ext = "";
}
if (importer->streamFormat) fmt = importer->streamFormat;
/*if isobmf and probing or no filter, direct copy*/
if (gf_isom_probe_file(importer->in_name)) {
u64 magic = 1;
magic <<= 32;
magic |= (importer->source_magic & 0xFFFFFFFFUL);
importer->source_magic = magic;
source_is_isom = GF_TRUE;
if ((!importer->filter_chain && !importer->filter_dst_opts && !importer->run_in_session && !importer->start_time)
|| (importer->flags & GF_IMPORT_PROBE_ONLY)
) {
importer->orig = gf_isom_open(importer->in_name, GF_ISOM_OPEN_READ, NULL);
if (importer->orig) {
e = gf_import_isomedia(importer);
gf_isom_delete(importer->orig);
importer->orig = NULL;
return e;
}
}
}
/*SC3DMC*/
if (!strnicmp(ext, ".s3d", 4) || (fmt && !stricmp(fmt, "SC3DMC")) )
return gf_import_afx_sc3dmc(importer, GF_TRUE);
/* chapter */
else if (!strnicmp(ext, ".txt", 4) || !strnicmp(ext, ".chap", 5) || (fmt && !stricmp(fmt, "CHAP")) ) {
e = gf_media_import_chapters_file(importer);
if (e==GF_NOT_FOUND) {
fmt = NULL;
} else {
return e;
}
}
e = GF_OK;
importer->last_error = GF_OK;
if (importer->flags & GF_IMPORT_PROBE_ONLY) {
GF_Filter *prober, *src_filter;
fsess = gf_fs_new_defaults(0);
if (!fsess) {
return gf_import_message(importer, GF_BAD_PARAM, "[Importer] Cannot load filter session for import");
}
prober = gf_fs_load_filter(fsess, "probe:log=null", &e);
src_filter = gf_fs_load_source(fsess, importer->in_name, "index=0", NULL, &e);
if (e) {
gf_fs_run(fsess);
gf_fs_del(fsess);
return gf_import_message(importer, e, "[Importer] Cannot load filter for input file \"%s\"", importer->in_name);
}
#ifdef GPAC_ENABLE_COVERAGE
if (gf_sys_is_cov_mode()) {
on_import_setup_failure(NULL, NULL, GF_OK);
}
#endif
gf_filter_set_setup_failure_callback(prober, src_filter, on_import_setup_failure, importer);
gf_fs_run(fsess);
if (importer->last_error) {
gf_fs_del(fsess);
return gf_import_message(importer, importer->last_error, "[Importer] Error probing %s", importer->in_name);
}
importer->nb_tracks = 0;
count = gf_filter_get_ipid_count(prober);
for (i=0; i<count; i++) {
if (importer->nb_tracks >= GF_IMPORT_MAX_TRACKS)
break;
const GF_PropertyValue *p;
struct __track_import_info *tki = &importer->tk_info[importer->nb_tracks];
GF_FilterPid *pid = gf_filter_get_ipid(prober, i);
p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
tki->stream_type = p ? p->value.uint : GF_STREAM_UNKNOWN;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
tki->codecid = p ? p->value.uint : GF_CODECID_NONE;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_LANGUAGE);
if (p && p->value.string && (strlen(p->value.string)>=3))
tki->lang = GF_4CC(p->value.string[0], p->value.string[1], p->value.string[2], ' ');
p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
tki->track_num = p ? p->value.uint : 1;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
if (p) tki->mpeg4_es_id = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_SERVICE_ID);
if (p) tki->prog_num = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
if (p) {
Double d = (Double) p->value.lfrac.num;
if (d<0) d = -d;
d*=1000;
if (p->value.lfrac.den) d /= p->value.lfrac.den;
if (d > importer->probe_duration) importer->probe_duration = (u64) d;
}
p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
if (p) {
tki->video_info.width = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
if (p) tki->video_info.height = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
if (p) {
tki->video_info.FPS = p->value.frac.num;
if (p->value.frac.den) tki->video_info.FPS /= p->value.frac.den;
}
p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
if (p && (p->value.frac.num>0)) tki->video_info.par = (p->value.frac.num << 16) | p->value.frac.den;
}
p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
if (p) {
tki->audio_info.sample_rate = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
if (p) tki->audio_info.nb_channels = p->value.uint;
p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLES_PER_FRAME);
if (p) tki->audio_info.samples_per_frame = p->value.uint;
}
/* p = gf_filter_pid_get_property(pid, GF_PROP_PID_CAN_DATAREF);
if (p) importer->flags |= GF_IMPORT_USE_DATAREF;
*/
p = gf_filter_pid_get_property(pid, GF_PROP_PID_SUBTYPE);
if (p) tki->media_subtype = p->value.uint;
importer->nb_tracks++;
}
gf_fs_del(fsess);
return GF_OK;
}
if (importer->run_in_session) {
fsess = importer->run_in_session;
} else {
fsess = gf_fs_new_defaults(fmt ? GF_FS_FLAG_NO_PROBE : 0);
if (!fsess) {
return gf_import_message(importer, GF_BAD_PARAM, "[Importer] Cannot load filter session for import");
}
}
filter_orig=NULL;
if (importer->run_in_session) {
sprintf(szFilterID, "%u", (u32) ( (importer->source_magic & 0xFFFFFFFFUL) ) );
} else {
gf_strcpy(szFilterID, "1");
}
if (!importer->run_in_session) {
//mux args
e = gf_dynstrcat(&args, "mp4mx:importer", ":");
snprintf(szSubArg, sizeof(szSubArg), "file=%p", importer->dest);
e |= gf_dynstrcat(&args, szSubArg, ":");
if (importer->preselection) {
e |= gf_dynstrcat(&args, "preselection=", ":");
e |= gf_dynstrcat(&args, importer->preselection, NULL);
}
}
if (importer->trackID) {
snprintf(szSubArg, sizeof(szSubArg), "SID=%s#PID=%d", szFilterID, importer->trackID);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->filter_dst_opts)
e |= gf_dynstrcat(&args, importer->filter_dst_opts, ":gfloc:");
if (importer->flags & GF_IMPORT_FORCE_MPEG4)
e |= gf_dynstrcat(&args, "m4sys", ":");
if (importer->flags & GF_IMPORT_USE_DATAREF)
e |= gf_dynstrcat(&args, "dref", ":");
if (importer->flags & GF_IMPORT_NO_EDIT_LIST)
e |= gf_dynstrcat(&args, "edits=no", ":");
if (importer->flags & GF_IMPORT_FORCE_PACKED)
e |= gf_dynstrcat(&args, "pack_nal", ":");
if (importer->xps_inband==1)
e |= gf_dynstrcat(&args, "xps_inband=all", ":");
else if (importer->xps_inband==2)
e |= gf_dynstrcat(&args, "xps_inband=both", ":");
if (importer->esd && importer->esd->ESID) {
snprintf(szSubArg, sizeof(szSubArg), "trackid=%d", importer->esd->ESID);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
else if (importer->target_trackID) {
snprintf(szSubArg, sizeof(szSubArg), "trackid=%u", importer->target_trackID);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->flags & GF_IMPORT_FORCE_SYNC)
e |= gf_dynstrcat(&args, ":forcesync", NULL);
if (importer->duration.den) {
snprintf(szSubArg, sizeof(szSubArg), "dur=%d/%d", importer->duration.num, importer->duration.den);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->frames_per_sample) {
snprintf(szSubArg, sizeof(szSubArg), "pack3gp=%d", importer->frames_per_sample);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->moov_timescale) {
snprintf(szSubArg, sizeof(szSubArg), "moovts=%d", importer->moov_timescale);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_2) { e |= gf_dynstrcat(&args, "ase=v0s", ":"); }
else if (importer->asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v0_BS) { e |= gf_dynstrcat(&args, "ase=v0bs", ":"); }
else if (importer->asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG) { e |= gf_dynstrcat(&args, "ase=v1", ":"); }
else if (importer->asemode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF) { e |= gf_dynstrcat(&args, "ase=v1qt", ":"); }
if (source_is_isom) {
if (!gf_sys_find_global_arg("xps_inband")
&& (!importer->filter_dst_opts || !strstr(importer->filter_dst_opts, "xps_inband"))
) {
e |= gf_dynstrcat(&args, "xps_inband=auto", ":");
}
if (gf_isom_has_keep_utc_times(importer->dest) ) { e |= gf_dynstrcat(&args, "keep_utc", ":"); }
}
if (importer->start_time) {
snprintf(szSubArg, sizeof(szSubArg), "start=%f", importer->start_time);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (e) {
gf_fs_del(fsess);
gf_free(args);
return gf_import_message(importer, e, "[Importer] Cannot load ISOBMFF muxer arguments");
}
if (!importer->run_in_session) {
isobmff_mux = gf_fs_load_filter(fsess, args, &e);
gf_free(args);
args = NULL;
if (!isobmff_mux) {
gf_fs_del(fsess);
gf_free(args);
return gf_import_message(importer, e, "[Importer] Cannot load ISOBMFF muxer");
}
} else {
if (args) {
gf_dynstrcat(&importer->update_mux_args, args, ":");
gf_free(args);
args = NULL;
}
isobmff_mux = NULL;
}
//filter chain
if (importer->filter_chain) {
GF_Filter *prev_filter=NULL;
char *fargs = (char *) importer->filter_chain;
while (fargs) {
GF_Filter *f;
char *sep;
Bool end_of_sub_chain = GF_FALSE;
if (importer->is_chain_old_syntax) {
sep = strstr(fargs, "@@");
} else {
sep = strstr(fargs, "@");
if (sep && (sep[1] == '@'))
end_of_sub_chain = GF_TRUE;
}
if (sep) sep[0] = 0;
f = gf_fs_load_filter(fsess, fargs, &e);
if (!f) {
if (!importer->run_in_session)
gf_fs_del(fsess);
return gf_import_message(importer, e, "[Importer] Cannot load filter %s", fargs);
}
if (prev_filter) {
gf_filter_set_source(f, prev_filter, NULL);
}
prev_filter = f;
if (!filter_orig) filter_orig = f;
if (!sep) end_of_sub_chain = GF_TRUE;
//we cannot directly set the source id in case we run in an existing session
if (end_of_sub_chain && prev_filter) {
const char *prev_id;
if (importer->trackID) {
if (gf_filter_get_id(prev_filter)) {
if (!importer->run_in_session)
gf_fs_del(fsess);
return gf_import_message(importer, GF_FILTER_NOT_FOUND, "[Importer] last filter in chain cannot use filter ID (%s)", fargs);
}
gf_filter_assign_id(prev_filter, szFilterID);
source_id_set=GF_TRUE;
}
prev_id = gf_filter_get_id(prev_filter);
if (!prev_id) {
gf_filter_assign_id(prev_filter, NULL);
prev_id = gf_filter_get_id(prev_filter);
}
if (importer->run_in_session) {
gf_dynstrcat(&importer->update_mux_sid, prev_id, importer->update_mux_sid ? "," : ":SID=");
} else {
gf_assert(isobmff_mux);
gf_filter_set_source(isobmff_mux, prev_filter, NULL);
}
prev_filter = NULL;
}
if (!sep) break;
sep[0] = '@';
if (importer->is_chain_old_syntax || end_of_sub_chain) {
fargs = sep+2;
} else {
fargs = sep+1;
}
}
}
//source args
e = gf_dynstrcat(&args, "importer:index=0", ":");
if (importer->trackID && !source_id_set) {
snprintf(szSubArg, sizeof(szSubArg), "FID=%s", szFilterID);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (fmt) {
snprintf(szSubArg, sizeof(szSubArg), "ext=%s", fmt);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->filter_src_opts) e |= gf_dynstrcat(&args, importer->filter_src_opts, ":");
if (importer->flags & GF_IMPORT_SBR_IMPLICIT) e |= gf_dynstrcat(&args, "sbr=imp", ":");
else if (importer->flags & GF_IMPORT_SBR_EXPLICIT) e |= gf_dynstrcat(&args, "sbr=exp", ":");
if (importer->flags & GF_IMPORT_PS_IMPLICIT) e |= gf_dynstrcat(&args, "ps=imp", ":");
else if (importer->flags & GF_IMPORT_PS_EXPLICIT) e |= gf_dynstrcat(&args, "ps=exp", ":");
if (importer->flags & GF_IMPORT_OVSBR) e |= gf_dynstrcat(&args, "ovsbr", ":");
//avoids message at end of import
if (importer->flags & GF_IMPORT_FORCE_PACKED) e |= gf_dynstrcat(&args, "nal_length=0", ":");
if (importer->flags & GF_IMPORT_SET_SUBSAMPLES) e |= gf_dynstrcat(&args, "subsamples", ":");
if (importer->flags & GF_IMPORT_NO_SEI) e |= gf_dynstrcat(&args, "nosei", ":");
if (importer->flags & GF_IMPORT_KEEP_AV1_TEMPORAL_OBU) e |= gf_dynstrcat(&args, "temporal_delim", ":");
if (importer->flags & GF_IMPORT_SVC_NONE) e |= gf_dynstrcat(&args, "nosvc", ":");
if (importer->flags & GF_IMPORT_SAMPLE_DEPS) e |= gf_dynstrcat(&args, "deps", ":");
if (importer->flags & GF_IMPORT_FORCE_MPEG4) e |= gf_dynstrcat(&args, "mpeg4", ":");
if (importer->keep_audelim) e |= gf_dynstrcat(&args, "audelim", ":");
if (importer->video_fps.num && importer->video_fps.den) {
snprintf(szSubArg, sizeof(szSubArg), "fps=%d/%d", importer->video_fps.num, importer->video_fps.den);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->is_alpha) e |= gf_dynstrcat(&args, "#Alpha", ":");
if (importer->streamFormat && !strcmp(importer->streamFormat, "VTT")) e |= gf_dynstrcat(&args, "webvtt", ":");
//SRT-related legacy params
if (importer->fontName) {
e |= gf_dynstrcat(&args, "fontname=", ":");
e |= gf_dynstrcat(&args, importer->fontName, NULL);
}
if (importer->fontName) {
snprintf(szSubArg, sizeof(szSubArg), "fontsize=%d", importer->fontSize);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->text_width && importer->text_height) {
snprintf(szSubArg, sizeof(szSubArg), "width=%d:height=%d:txtx=%d:txty=%d", importer->text_width, importer->text_height, importer->text_x, importer->text_y);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->source_magic) {
snprintf(szSubArg, sizeof(szSubArg), "#SrcMagic="LLU, importer->source_magic);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (importer->track_index) {
snprintf(szSubArg, sizeof(szSubArg), "#MuxIndex=%d", importer->track_index);
e |= gf_dynstrcat(&args, szSubArg, ":");
}
if (e) {
if (!importer->run_in_session)
gf_fs_del(fsess);
gf_free(args);
return gf_import_message(importer, e, "[Importer] Cannot load arguments for input %s", importer->in_name);
}
source = gf_fs_load_source(fsess, importer->in_name, args, NULL, &e);
gf_free(args);
args = NULL;
if (e) {
if (!importer->run_in_session)
gf_fs_del(fsess);
return gf_import_message(importer, e, "[Importer] Cannot load filter for input file \"%s\"", importer->in_name);
}
if (filter_orig)
gf_filter_set_source(filter_orig, source, NULL);
//source is setup, we run in an external session, nothing left to do
if (importer->run_in_session)
return GF_OK;
gf_fs_run(fsess);
if (!importer->last_error) importer->last_error = gf_fs_get_last_connect_error(fsess);
if (!importer->last_error) importer->last_error = gf_fs_get_last_process_error(fsess);
if (importer->last_error) {
gf_fs_print_non_connected(fsess);
if (importer->print_stats_graph & 1) gf_fs_print_stats(fsess);
if (importer->print_stats_graph & 2) gf_fs_print_connections(fsess);
if (!importer->run_in_session)
gf_fs_del(fsess);
return gf_import_message(importer, importer->last_error, "[Importer] Error importing %s", importer->in_name);
}
importer->final_trackID = gf_isom_get_last_created_track_id(importer->dest);
if (importer->esd && importer->final_trackID) {
u32 tk_num = gf_isom_get_track_by_id(importer->dest, importer->final_trackID);
GF_ESD *esd = gf_isom_get_esd(importer->dest, tk_num, 1);
if (esd && !importer->esd->decoderConfig) {
importer->esd->decoderConfig = esd->decoderConfig;
esd->decoderConfig = NULL;
}
if (esd && !importer->esd->slConfig) {
importer->esd->slConfig = esd->slConfig;
esd->slConfig = NULL;
}
if (esd) {
gf_odf_desc_del((GF_Descriptor *) esd);
}
if (!importer->esd->ESID) {
importer->esd->ESID = importer->final_trackID;
}
}
if (!e) gf_fs_print_unused_args(fsess, "index,fps,mpeg4");
gf_fs_print_non_connected(fsess);
if (importer->print_stats_graph & 1) gf_fs_print_stats(fsess);
if (importer->print_stats_graph & 2) gf_fs_print_connections(fsess);
gf_fs_del(fsess);
if (!importer->final_trackID) {
return gf_import_message(importer, GF_NOT_SUPPORTED, "[Importer] No valid track to import in input file \"%s\"", importer->in_name);
}
return GF_OK;
}
#endif /*GPAC_DISABLE_MEDIA_IMPORT*/