From: Alessio Treglia Date: Wed, 16 Jan 2013 16:08:33 +0000 (+0000) Subject: Imported Upstream version 0.5.0+svn4281~dfsg1 X-Git-Tag: archive/raspbian/1.0.1+dfsg1-4+rpi1~1^2~15^2~13 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=32aaace1f109f466b90258ed3598394edb6d302c;p=gpac.git Imported Upstream version 0.5.0+svn4281~dfsg1 --- diff --git a/README b/README index ab1c8cd..ccb6ea6 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ mozilla SpiderMonkey javascript engine. GPAC currently supports local playback, http progressive download, Adaptive HTTP Streaming (MPEG-DASH, HLS), RTP/RTSP streaming over UDP (unicast or multicast) or TCP and TS demuxing (from file, IP or DVB4Linux). GPAC also features MP4Box, a multimedia swiss-army knife for the prompt. -For compilation and installation instruction, check INSTALL file +For compilation and installation instruction, check INSTALLME file For GPAC configuration instruction, check gpac/doc/configuration.html or gpac/doc/man/gpac.1 (man gpac when installed) diff --git a/applications/mp42ts/main.c b/applications/mp42ts/main.c index 0eedc25..ee6f3cd 100644 --- a/applications/mp42ts/main.c +++ b/applications/mp42ts/main.c @@ -36,6 +36,8 @@ #include #endif +#define USE_ISOBMF_REWRITE + #ifdef GPAC_DISABLE_ISOM @@ -137,13 +139,16 @@ typedef struct u32 image_repeat_ms, nb_repeat_last; void *dsi; u32 dsi_size; - u32 nalu_size; + void *dsi_and_rap; Bool loop; Bool is_repeat; u64 ts_offset; M2TSProgram *prog; - char nalu_delim[6]; + +#ifndef USE_ISOBMF_REWRITE + u32 nalu_size; +#endif } GF_ESIMP4; typedef struct @@ -215,6 +220,7 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) pck.flags |= GF_ESI_DATA_HAS_DTS; } +#ifndef USE_ISOBMF_REWRITE if (priv->nalu_size) { Bool nalu_delim_sent = 0; u32 remain = priv->sample->dataLength; @@ -271,7 +277,10 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) ptr += size; } - } else { + } else +#endif //USE_ISOBMF_REWRITE + + { if (priv->sample->IsRAP && priv->dsi && priv->dsi_size) { pck.data = priv->dsi; @@ -375,14 +384,21 @@ static void fill_isom_es_ifce(M2TSProgram *prog, GF_ESInterface *ifce, GF_ISOFil priv->dsi_size = dcd->decoderSpecificInfo->dataLength; memcpy(priv->dsi, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); break; + case GPAC_OTI_VIDEO_HEVC: + gf_isom_set_nalu_extract_mode(mp4, track_num, GF_ISOM_NALU_EXTRACT_LAYER_ONLY | GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG | GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG); + break; case GPAC_OTI_VIDEO_AVC: { -#ifndef GPAC_DISABLE_AV_PARSERS +#ifdef USE_ISOBMF_REWRITE + gf_isom_set_nalu_extract_mode(mp4, track_num, GF_ISOM_NALU_EXTRACT_LAYER_ONLY | GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG | GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG); + +#elif !defined(GPAC_DISABLE_AV_PARSERS) GF_AVCConfigSlot *slc; u32 i; GF_BitStream *bs; GF_AVCConfig *avccfg = gf_isom_avc_config_get(mp4, track_num, 1); priv->nalu_size = avccfg->nal_unit_size; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); for (i=0; isequenceParameterSets);i++) { slc = gf_list_get(avccfg->sequenceParameterSets, i); @@ -398,9 +414,7 @@ static void fill_isom_es_ifce(M2TSProgram *prog, GF_ESInterface *ifce, GF_ISOFil gf_bs_del(bs); gf_odf_avc_cfg_del(avccfg); #endif - priv->nalu_delim[3] = 1; - priv->nalu_delim[4] = 0; /*this will be nal header*/ - priv->nalu_delim[5] = 0xF0 /*7 "all supported NALUs" (=111) + rbsp trailing (10000)*/; + } break; } diff --git a/applications/mp4box/filedump.c b/applications/mp4box/filedump.c index 147426e..285deca 100644 --- a/applications/mp4box/filedump.c +++ b/applications/mp4box/filedump.c @@ -873,13 +873,51 @@ void dump_file_timestamps(GF_ISOFile *file, char *inName) } -static void dump_nalu_type_name(FILE *dump, char *ptr, Bool is_svc) +static void dump_nalu(FILE *dump, char *ptr, Bool is_svc, Bool is_hevc) { - u8 type = ptr[0] & 0x1F; + u8 type; u8 dependency_id, quality_id, temporal_id; u8 track_ref_index; - u32 data_offset; + u32 data_offset, sps_id, pps_id; + if (is_hevc) { + type = (ptr[0] & 0x7E) >> 1; + fprintf(dump, "code=\"%d\" type=\"", type); + switch (type) { + case GF_HEVC_NALU_SLICE_TRAIL_N: fputs("TRAIL_N slice segment", dump); break; + case GF_HEVC_NALU_SLICE_TRAIL_R: fputs("TRAIL_R slice segment", dump); break; + case GF_HEVC_NALU_SLICE_TSA_N: fputs("TSA_N slice segment", dump); break; + case GF_HEVC_NALU_SLICE_TSA_R: fputs("TSA_R slice segment", dump); break; + case GF_HEVC_NALU_SLICE_STSA_N: fputs("STSA_N slice segment", dump); break; + case GF_HEVC_NALU_SLICE_STSA_R: fputs("STSA_R slice segment", dump); break; + case GF_HEVC_NALU_SLICE_RADL_N: fputs("RADL_N slice segment", dump); break; + case GF_HEVC_NALU_SLICE_RADL_R: fputs("RADL_R slice segment", dump); break; + case GF_HEVC_NALU_SLICE_RASL_N: fputs("RASL_N slice segment", dump); break; + case GF_HEVC_NALU_SLICE_RASL_R: fputs("RASL_R slice segment", dump); break; + case GF_HEVC_NALU_SLICE_BLA_W_LP: fputs("Broken link access slice (W LP)", dump); break; + case GF_HEVC_NALU_SLICE_BLA_W_DLP: fputs("Broken link access slice (W DLP)", dump); break; + case GF_HEVC_NALU_SLICE_BLA_N_LP: fputs("Broken link access slice (N LP)", dump); break; + case GF_HEVC_NALU_SLICE_IDR_W_DLP: fputs("IDR slice (W DLP)", dump); break; + case GF_HEVC_NALU_SLICE_IDR_N_LP: fputs("IDR slice (N LP)", dump); break; + case GF_HEVC_NALU_SLICE_CRA: fputs("CRA slice", dump); break; + + case GF_HEVC_NALU_VID_PARAM: fputs("Video Parameter Set", dump); break; + case GF_HEVC_NALU_SEQ_PARAM: fputs("Sequence Parameter Set", dump); break; + case GF_HEVC_NALU_PIC_PARAM: fputs("Picture Parameter Set", dump); break; + case GF_HEVC_NALU_ACCESS_UNIT: fputs("AU Delimiter", dump); break; + case GF_HEVC_NALU_END_OF_SEQ: fputs("End of Sequence", dump); break; + case GF_HEVC_NALU_END_OF_STREAM: fputs("End of Stream", dump); break; + case GF_HEVC_NALU_FILLER_DATA: fputs("Filler Data", dump); break; + case GF_HEVC_NALU_SEI_PREFIX: fputs("SEI Prefix", dump); break; + case GF_HEVC_NALU_SEI_SUFFIX: fputs("SEI Suffix", dump); break; + default: + fputs("UNKNOWN", dump); break; + } + fputs("\"", dump); + return; + } + + type = ptr[0] & 0x1F; fprintf(dump, "code=\"%d\" type=\"", type); switch (type) { case GF_AVC_NALU_NON_IDR_SLICE: fputs("Non IDR slice", dump); break; @@ -888,15 +926,27 @@ static void dump_nalu_type_name(FILE *dump, char *ptr, Bool is_svc) case GF_AVC_NALU_DP_C_SLICE: fputs("DP Type C slice", dump); break; case GF_AVC_NALU_IDR_SLICE: fputs("IDR slice", dump); break; case GF_AVC_NALU_SEI: fputs("SEI Message", dump); break; - case GF_AVC_NALU_SEQ_PARAM: fputs("SequenceParameterSet", dump); break; - case GF_AVC_NALU_PIC_PARAM: fputs("PictureParameterSet", dump); break; + case GF_AVC_NALU_SEQ_PARAM: + fputs("SequenceParameterSet", dump); + gf_avc_get_sps_info(ptr, strlen(ptr), &sps_id, NULL, NULL, NULL, NULL); + fprintf(dump, "\" sps_id=\"%d", sps_id); + break; + case GF_AVC_NALU_PIC_PARAM: + fputs("PictureParameterSet", dump); + gf_avc_get_pps_info(ptr+1, strlen(ptr)-1, &pps_id, &sps_id); + fprintf(dump, "\" pps_id=\"%d\" sps_id=\"%d", pps_id, sps_id); + break; case GF_AVC_NALU_ACCESS_UNIT: fputs("AccessUnit delimiter", dump); break; case GF_AVC_NALU_END_OF_SEQ: fputs("EndOfSequence", dump); break; case GF_AVC_NALU_END_OF_STREAM: fputs("EndOfStream", dump); break; case GF_AVC_NALU_FILLER_DATA: fputs("Filler data", dump); break; case GF_AVC_NALU_SEQ_PARAM_EXT: fputs("SequenceParameterSetExtension", dump); break; case GF_AVC_NALU_SVC_PREFIX_NALU: fputs("SVCPrefix", dump); break; - case GF_AVC_NALU_SVC_SUBSEQ_PARAM: fputs("SVCSubsequenceParameterSet", dump); break; + case GF_AVC_NALU_SVC_SUBSEQ_PARAM: + fputs("SVCSubsequenceParameterSet", dump); + gf_avc_get_sps_info(ptr, strlen(ptr), &sps_id, NULL, NULL, NULL, NULL); + fprintf(dump, "\" sps_id=\"%d", sps_id); + break; case GF_AVC_NALU_SLICE_AUX: fputs("Auxiliary Slice", dump); break; case GF_AVC_NALU_SVC_SLICE: @@ -922,10 +972,13 @@ static void dump_nalu_type_name(FILE *dump, char *ptr, Bool is_svc) void dump_file_nal(GF_ISOFile *file, u32 trackID, char *inName) { - u32 i, count, track, nalh_size; + u32 i, count, track, nalh_size, timescale, cur_extract_mode; + Bool is_hevc = 0; FILE *dump; + s32 countRef; #ifndef GPAC_DISABLE_AV_PARSERS GF_AVCConfig *avccfg, *svccfg; + GF_HEVCConfig *hevccfg; GF_AVCConfigSlot *slc; #endif @@ -941,20 +994,25 @@ void dump_file_nal(GF_ISOFile *file, u32 trackID, char *inName) count = gf_isom_get_sample_count(file, track); - fprintf(dump, "\n", trackID, count); + timescale = gf_isom_get_media_timescale(file, track); + + cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track); + + fprintf(dump, "\n", trackID, count, timescale); #ifndef GPAC_DISABLE_AV_PARSERS avccfg = gf_isom_avc_config_get(file, track, 1); svccfg = gf_isom_svc_config_get(file, track, 1); + hevccfg = gf_isom_hevc_config_get(file, track, 1); fprintf(dump, " \n"); #define DUMP_ARRAY(arr, name)\ if (arr) {\ for (i=0; idata , svccfg ? 1 : 0);\ - fprintf(dump, ">\n");\ + fprintf(dump, " <%s number=\"%d\" size=\"%d\"", name, i+1, slc->size);\ + dump_nalu(dump, slc->data , svccfg ? 1 : 0, is_hevc);\ + fprintf(dump, "/>\n");\ }\ }\ @@ -972,10 +1030,42 @@ void dump_file_nal(GF_ISOFile *file, u32 trackID, char *inName) DUMP_ARRAY(svccfg->sequenceParameterSets, "SVCSPSArray") DUMP_ARRAY(svccfg->pictureParameterSets, "SVCPPSArray") } + if (hevccfg) { + nalh_size = hevccfg->nal_unit_size; + is_hevc = 1; + for (i=0; iparam_array); i++) { + GF_HEVCParamArray *ar = gf_list_get(hevccfg->param_array, i); + if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { + DUMP_ARRAY(ar->nalus, "HEVCSPSArray") + } else if (ar->type==GF_HEVC_NALU_PIC_PARAM) { + DUMP_ARRAY(ar->nalus, "HEVCPPSArray") + } else if (ar->type==GF_HEVC_NALU_VID_PARAM) { + DUMP_ARRAY(ar->nalus, "HEVCVPSArray") + } else { + DUMP_ARRAY(ar->nalus, "HEVCUnknownPSArray") + } + } + } #endif fprintf(dump, " \n"); + /*for testing dependency*/ + countRef = gf_isom_get_reference_count(file, track, GF_ISOM_REF_SCAL); + if (countRef > 0) + { + u32 refTrackID; + fprintf(dump, " \n"); + for (i = 1; i <= (u32) countRef; i++) + { + gf_isom_get_reference_ID(file, track, GF_ISOM_REF_SCAL, i, &refTrackID); + fprintf(dump, " \n", i, refTrackID); + } + + fprintf(dump, " \n"); + } + fprintf(dump, " \n"); + gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT); for (i=0; i\n"); } idx++; @@ -1021,6 +1111,9 @@ void dump_file_nal(GF_ISOFile *file, u32 trackID, char *inName) fprintf(dump, "\n"); if (inName) fclose(dump); + if (avccfg) gf_odf_avc_cfg_del(avccfg); + if (svccfg) gf_odf_avc_cfg_del(svccfg); + gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode); } #ifndef GPAC_DISABLE_ISOM_DUMP @@ -1300,8 +1393,12 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) || (msub_type==GF_ISOM_SUBTYPE_MPEG4_CRYP) || (msub_type==GF_ISOM_SUBTYPE_AVC_H264) || (msub_type==GF_ISOM_SUBTYPE_AVC2_H264) + || (msub_type==GF_ISOM_SUBTYPE_AVC3_H264) + || (msub_type==GF_ISOM_SUBTYPE_AVC4_H264) || (msub_type==GF_ISOM_SUBTYPE_SVC_H264) || (msub_type==GF_ISOM_SUBTYPE_LSR1) + || (msub_type==GF_ISOM_SUBTYPE_HVC1) + || (msub_type==GF_ISOM_SUBTYPE_HEV1) ) { esd = gf_isom_get_esd(file, trackNum, 1); if (!esd) { @@ -1369,7 +1466,7 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) } } if (avccfg->chroma_bit_depth) { - fprintf(stderr, "\tChroma format %d - Luma bit depth %d - chroma bot depth %d\n", avccfg->chroma_format, avccfg->luma_bit_depth, avccfg->chroma_bit_depth); + fprintf(stderr, "\tChroma format %d - Luma bit depth %d - chroma bit depth %d\n", avccfg->chroma_format, avccfg->luma_bit_depth, avccfg->chroma_bit_depth); } gf_odf_avc_cfg_del(avccfg); } @@ -1392,7 +1489,42 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) gf_odf_avc_cfg_del(svccfg); } #endif /*GPAC_DISABLE_AV_PARSERS*/ + + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_HEVC) { +#ifndef GPAC_DISABLE_AV_PARSERS + GF_HEVCConfig *hevccfg; +#endif + + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + if (full_dump) fprintf(stderr, "\t"); + fprintf(stderr, "HEVC Video - Visual Size %d x %d\n", w, h); +#ifndef GPAC_DISABLE_AV_PARSERS + hevccfg = gf_isom_hevc_config_get(file, trackNum, 1); + if (!hevccfg ) { + fprintf(stderr, "\n\n\tNon-compliant HEVC track: hvcC not found in sample description\n"); + } else { + u32 k; + fprintf(stderr, "\tHEVC Info: Profile IDC %d - Level IDC %d - Chroma Format %d\n", hevccfg->profile_idc, hevccfg->level_idc, hevccfg->chromaFormat); + fprintf(stderr, "\tNAL Unit length bits: %d - profile compatibility 0x%08X\n", 8*hevccfg->nal_unit_size, hevccfg->profile_compatibility_indications); + fprintf(stderr, "\tParameter Sets: "); + for (k=0; kparam_array); k++) { + GF_HEVCParamArray *ar=gf_list_get(hevccfg->param_array, k); + if (ar->type==GF_HEVC_NALU_SEQ_PARAM) { + fprintf(stderr, "%d SPS ", gf_list_count(ar->nalus)); + } + else if (ar->type==GF_HEVC_NALU_PIC_PARAM) { + fprintf(stderr, "%d PPS ", gf_list_count(ar->nalus)); + } + if (ar->type==GF_HEVC_NALU_VID_PARAM) { + fprintf(stderr, "%d VPS ", gf_list_count(ar->nalus)); + } + } + fprintf(stderr, "\n\tBit Depth luma %d - Chroma %d - %d temporal layers\n", hevccfg->luma_bit_depth, hevccfg->chroma_bit_depth, hevccfg->numTemporalLayers); + gf_odf_hevc_cfg_del(hevccfg); + } +#endif /*GPAC_DISABLE_AV_PARSERS*/ } + /*OGG media*/ else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_MEDIA_OGG) { char *szName; @@ -1444,7 +1576,8 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) if (full_dump) fprintf(stderr, "\t"); if (e) fprintf(stderr, "Corrupted AAC Config\n"); else { - fprintf(stderr, "MPEG-%d Audio %s - %d Channel(s) - SampleRate %d", is_mp2 ? 2 : 4, gf_m4a_object_type_name(a_cfg.base_object_type), a_cfg.nb_chan, a_cfg.base_sr); + fprintf(stderr, "%s - %d Channel(s) - SampleRate %d", gf_m4a_object_type_name(a_cfg.base_object_type), a_cfg.nb_chan, a_cfg.base_sr); + if (is_mp2) fprintf(stderr, " (MPEG-2 Signaling)"); if (a_cfg.has_sbr) fprintf(stderr, " - SBR SampleRate %d", a_cfg.sbr_sr); if (a_cfg.has_ps) fprintf(stderr, " - PS"); fprintf(stderr, "\n"); @@ -1946,7 +2079,7 @@ void DumpMovieInfo(GF_ISOFile *file) fprintf(stderr, "\tTempo (BPM): %d\n", tag[1]); } } - if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_TRACKNUMBER, &tag, &tag_len)==GF_OK) fprintf(stderr, "\tTrackNumber: %d / %d\n", tag[3], tag[5]); + if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_TRACKNUMBER, &tag, &tag_len)==GF_OK) fprintf(stderr, "\tTrackNumber: %d / %d\n", (0xff00 & (tag[2]<<8)) | (0xff & tag[3]), (0xff00 & (tag[4]<<8)) | (0xff & tag[5])); if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_TRACK, &tag, &tag_len)==GF_OK) fprintf(stderr, "\tTrack: %s\n", tag); if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_GROUP, &tag, &tag_len)==GF_OK) fprintf(stderr, "\tGroup: %s\n", tag); diff --git a/applications/mp4box/fileimport.c b/applications/mp4box/fileimport.c index e1976a8..d364d37 100644 --- a/applications/mp4box/fileimport.c +++ b/applications/mp4box/fileimport.c @@ -41,6 +41,7 @@ #ifndef GPAC_DISABLE_VRML #include #endif +#include #ifndef GPAC_DISABLE_ISOM_WRITE @@ -155,6 +156,27 @@ void convert_file_info(char *inName, u32 trackID) if (!found && trackID) fprintf(stderr, "Cannot find track %d in file\n", trackID); } +static void set_chapter_track(GF_ISOFile *file, u32 track, u32 chapter_ref_trak) +{ + u64 ref_duration, chap_duration; + Double scale; + + gf_isom_set_track_reference(file, chapter_ref_trak, GF_4CC('c','h','a','p'), gf_isom_get_track_id(file, track) ); + gf_isom_set_track_enabled(file, track, 0); + + ref_duration = gf_isom_get_media_duration(file, chapter_ref_trak); + chap_duration = gf_isom_get_media_duration(file, track); + scale = (Double) (s64) gf_isom_get_media_timescale(file, track); + scale /= gf_isom_get_media_timescale(file, chapter_ref_trak); + ref_duration = (u64) (ref_duration * scale); + + if (chap_duration < ref_duration) { + chap_duration -= gf_isom_get_sample_duration(file, track, gf_isom_get_sample_count(file, track)); + chap_duration = ref_duration - chap_duration; + gf_isom_set_last_sample_duration(file, track, (u32) chap_duration); + } +} + GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double force_fps, u32 frames_per_sample) { u32 track_id, i, timescale, track, stype, profile, level, new_timescale, rescale, svc_mode; @@ -425,7 +447,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc import.video_fps = force_fps; import.frames_per_sample = frames_per_sample; import.flags = import_flags; - + if (!import.nb_tracks) { u32 count, o_count; o_count = gf_isom_get_track_count(import.dest); @@ -458,13 +480,12 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (handler_name) gf_isom_set_handler_name(import.dest, i+1, handler_name); else if (!keep_handler) { char szHName[1024]; - char *fName = strrchr(inName, '/'); - if (!fName) fName = strrchr(inName, '\\'); + const char *fName = gf_url_get_resource_name((const char *)inName); + fName = strchr(fName, '.'); + if (fName) fName += 1; + else fName = "?"; - if (!fName) fName = inName; - else fName = fName+1; - - sprintf(szHName, "%s - Imported with GPAC %s", fName, GPAC_FULL_VERSION); + sprintf(szHName, "*%s@GPAC%s", fName, GPAC_FULL_VERSION); gf_isom_set_handler_name(import.dest, i+1, szHName); } if (handler) gf_isom_set_media_type(import.dest, i+1, handler); @@ -480,8 +501,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc gf_isom_set_media_subtype(import.dest, i+1, 1, stype); if (is_chap && chap_ref) { - gf_isom_set_track_reference(import.dest, chap_ref, GF_4CC('c','h','a','p'), gf_isom_get_track_id(import.dest, i+1) ); - gf_isom_set_track_enabled(import.dest, i+1, 0); + set_chapter_track(import.dest, i+1, chap_ref); } if (profile || level) @@ -492,8 +512,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc gf_isom_set_composition_offset_mode(import.dest, i+1, negative_cts_offset); - /*when importing SVC we ALWAYS have AVC+SVC single track mode*/ - if (gf_isom_get_avc_svc_type(import.dest, i+1, 1)==GF_ISOM_AVCTYPE_AVC_SVC) + if (gf_isom_get_avc_svc_type(import.dest, i+1, 1)>=GF_ISOM_AVCTYPE_AVC_SVC) check_track_for_svc = i+1; } } else { @@ -547,13 +566,12 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (handler_name) gf_isom_set_handler_name(import.dest, track, handler_name); else if (!keep_handler) { char szHName[1024]; - char *fName = strrchr(inName, '/'); - if (!fName) fName = strrchr(inName, '\\'); - - if (!fName) fName = inName; - else fName = fName+1; + const char *fName = gf_url_get_resource_name((const char *)inName); + fName = strchr(fName, '.'); + if (fName) fName += 1; + else fName = "?"; - sprintf(szHName, "%s - Imported with GPAC %s", fName, GPAC_FULL_VERSION); + sprintf(szHName, "%s@GPAC%s", fName, GPAC_FULL_VERSION); gf_isom_set_handler_name(import.dest, track, szHName); } if (handler) gf_isom_set_media_type(import.dest, track, handler); @@ -569,8 +587,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc gf_isom_set_media_subtype(import.dest, track, 1, stype); if (is_chap && chap_ref) { - gf_isom_set_track_reference(import.dest, chap_ref, GF_4CC('c','h','a','p'), gf_isom_get_track_id(import.dest, i+1) ); - gf_isom_set_track_enabled(import.dest, track, 0); + set_chapter_track(import.dest, track, chap_ref); } if (profile || level) @@ -618,8 +635,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc gf_isom_set_composition_offset_mode(import.dest, track, negative_cts_offset); - /*when importing SVC we ALWAYS have AVC+SVC single track mode*/ - if (gf_isom_get_avc_svc_type(import.dest, track, 1)==GF_ISOM_AVCTYPE_AVC_SVC) + if (gf_isom_get_avc_svc_type(import.dest, track, 1)>=GF_ISOM_AVCTYPE_AVC_SVC) check_track_for_svc = track; } if (track_id) fprintf(stderr, "WARNING: Track ID %d not found in file\n", track_id); @@ -635,6 +651,16 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc } } + /*force to rewrite all dependencies*/ + for (i = 1; i <= gf_isom_get_track_count(import.dest); i++) + { + e = gf_isom_rewrite_track_dependencies(import.dest, i); + if (e) { + fprintf(stderr, "Warning: track ID %d has references to a track not imported\n", gf_isom_get_track_id(import.dest, i)); + e = GF_OK; + } + } + if (check_track_for_svc) { if (svc_mode) { e = gf_media_split_svc(import.dest, check_track_for_svc, (svc_mode==2) ? 1 : 0); @@ -1359,8 +1385,10 @@ GF_Err cat_isomedia_file(GF_ISOFile *dest, char *fileName, u32 import_flags, Dou for (i=0; iDTS; - samp->DTS = (u64) (ts_scale * samp->DTS + insert_dts); + samp->DTS = (u64) (ts_scale * samp->DTS + (new_track ? 0 : insert_dts)); samp->CTS_Offset = (u32) (samp->CTS_Offset * ts_scale); if (gf_isom_is_self_contained(orig, i+1, di)) { @@ -1607,7 +1651,18 @@ GF_Err cat_isomedia_file(GF_ISOFile *dest, char *fileName, u32 import_flags, Dou gf_isom_set_last_sample_duration(dest, dst_tk, (u32) insert_dts); } - if (merge_edits) { + if (new_track && insert_dts) { + u64 media_dur = gf_isom_get_media_duration(orig, i+1); + /*convert from media time to track time*/ + Double rescale = (Float) gf_isom_get_timescale(dest); + rescale /= (Float) gf_isom_get_media_timescale(dest, dst_tk); + /*convert from orig to dst time scale*/ + rescale *= ts_scale; + + gf_isom_set_edit_segment(dest, dst_tk, 0, (u64) (s64) (insert_dts*rescale), 0, GF_ISOM_EDIT_EMPTY); + gf_isom_set_edit_segment(dest, dst_tk, (u64) (s64) (insert_dts*rescale), (u64) (s64) (media_dur*rescale), 0, GF_ISOM_EDIT_NORMAL); + } + else if (merge_edits) { /*convert from media time to track time*/ Double rescale = (Float) gf_isom_get_timescale(dest); rescale /= (Float) gf_isom_get_media_timescale(dest, dst_tk); diff --git a/applications/mp4box/main.c b/applications/mp4box/main.c index 2051753..3418979 100644 --- a/applications/mp4box/main.c +++ b/applications/mp4box/main.c @@ -213,6 +213,7 @@ void PrintGeneralUsage() " * Note 1: some tracks may be removed in the process\n" " * Note 2: always on for *.3gp *.3g2 *.3gpp\n" " -ipod rewrites the file for iPod\n" + " -psp rewrites the file for PSP devices\n" " -brand ABCD[:v] sets major brand of file, with optional version\n" " -ab ABCD adds given brand to file's alternate brand list\n" " -rb ABCD removes given brand from file's alternate brand list\n" @@ -271,14 +272,21 @@ void PrintDASHUsage() " Note: for onDemand profile, sets duration of a subsegment\n" " -frag time_in_ms Specifies a fragment duration of time_in_ms.\n" " * Note: By default, this is the DASH duration\n" - " -out filename specifies output file name\n" - " * Note: By default input (MP4,3GP) file is overwritten\n" + " -out filename specifies output MPD file name.\n" " -tmp dirname specifies directory for temporary file creation\n" " * Note: Default temp dir is OS-dependent\n" " -profile NAME specifies the target DASH profile: \"onDemand\", \"live\", \"main\", \"simple\", \"full\"\n" " * This will set default option values to ensure conformance to the desired profile\n" " * Default profile is \"full\" in static mode, \"live\" in dynamic mode\n" "\n" + "Input media files to dash can use the following modifiers\n" + " \":id=NAME\" sets the representation ID to NAME\n" + " \":period=NAME\" sets the representation's period to NAME. Multiple periods may be used\n" + " period appear in the MPD in the same order as specified with this option\n" + " \":bandwidth=VALUE\" sets the representation's bandwidth to a given value\n" + " \":role=VALUE\" sets the role of this representation (cf DASH spec).\n" + " media with different roles belong to different adaptation sets.\n" + "\n" " -rap segments begin with random access points\n" " Note: segment duration may not be exactly what asked by\n" " \"-dash\" since encoded video data is not modified\n" @@ -312,7 +320,7 @@ void PrintDASHUsage() " -daisy-chain uses daisy-chain SIDX instead of hierarchical. Ignored if frags/sidx is 0.\n" " -single-segment uses a single segment for the whole file (OnDemand profile). \n" " -single-file uses a single file for the whole file (default). \n" - " -bs-switching MODE sets bitstream switching to \"yes\" (default), \"no\" or \"single\" to test with single input.\n" + " -bs-switching MODE sets bitstream switching to \"yes\" (default), \"merge\", \"no\" or \"single\" to test with single input.\n" " -dash-ts-prog N program_number to be considered in case of an MPTS input file.\n" "\n"); } @@ -563,6 +571,7 @@ void PrintExtractUsage() " -saf remux file to SAF multiplex\n" " -dvbhdemux demux DVB-H file into IP Datagrams\n" " * Note: can be used when encoding scene descriptions\n" + " -raw-layer ID same as -raw but skips SVC/MVC extractors when extracting\n" " -diod extracts file IOD in raw format when supported\n" "\n"); } @@ -1292,6 +1301,7 @@ GF_DashSegmenterInput *set_dash_input(GF_DashSegmenterInput *dash_inputs, char * if (!strnicmp(opts, "id=", 3)) strncpy(di->representationID, opts+3, 99); else if (!strnicmp(opts, "period=", 7)) strncpy(di->periodID, opts+7, 99); else if (!strnicmp(opts, "bandwidth=", 10)) di->bandwidth = atoi(opts+10); + else if (!strnicmp(opts, "role=", 5)) strncpy(di->role, opts+5, 99); if (!sep) break; sep[0] = ':'; @@ -1321,9 +1331,10 @@ int mp4boxMain(int argc, char **argv) s32 subsegs_per_sidx; u32 *brand_add = NULL; u32 *brand_rem = NULL; - u32 i, stat_level, hint_flags, info_track_id, import_flags, nb_add, nb_cat, ismaCrypt, agg_samples, nb_sdp_ex, max_ptime, raw_sample_num, split_size, nb_meta_act, nb_track_act, rtp_rate, major_brand, nb_alt_brand_add, nb_alt_brand_rem, old_interleave, car_dur, minor_version, conv_type, nb_tsel_acts, program_number, bitstream_switching_mode, dump_nal, time_shift_depth; + GF_DashSwitchingMode bitstream_switching_mode = GF_DASH_BSMODE_INBAND; + u32 i, stat_level, hint_flags, info_track_id, import_flags, nb_add, nb_cat, ismaCrypt, agg_samples, nb_sdp_ex, max_ptime, raw_sample_num, split_size, nb_meta_act, nb_track_act, rtp_rate, major_brand, nb_alt_brand_add, nb_alt_brand_rem, old_interleave, car_dur, minor_version, conv_type, nb_tsel_acts, program_number, dump_nal, time_shift_depth, dash_dynamic; Bool HintIt, needSave, FullInter, Frag, HintInter, dump_std, dump_rtp, dump_mode, regular_iod, trackID, remove_sys_tracks, remove_hint, force_new, remove_root_od, import_subtitle, dump_chap; - Bool print_sdp, print_info, open_edit, track_dump_type, dump_isom, dump_cr, force_ocr, encode, do_log, do_flat, dump_srt, dump_ttxt, dump_timestamps, do_saf, dump_m2ts, dump_cart, do_hash, verbose, force_cat, align_cat, pack_wgt, single_group, dash_dynamic, dash_live; + Bool print_sdp, print_info, open_edit, track_dump_type, dump_isom, dump_cr, force_ocr, encode, do_log, do_flat, dump_srt, dump_ttxt, dump_timestamps, do_saf, dump_m2ts, dump_cart, do_hash, verbose, force_cat, align_cat, pack_wgt, single_group, dash_live; char *inName, *outName, *arg, *mediaSource, *tmpdir, *input_ctx, *output_ctx, *drm_file, *avi2raw, *cprt, *chap_file, *pes_dump, *itunes_tags, *pack_file, *raw_cat, *seg_name, *dash_ctx_file; char **mpd_base_urls = NULL; @@ -1379,8 +1390,8 @@ int mp4boxMain(int argc, char **argv) dump_nal = 0; FullInter = HintInter = encode = do_log = old_interleave = do_saf = do_hash = verbose = 0; dump_mode = Frag = force_ocr = remove_sys_tracks = agg_samples = remove_hint = keep_sys_tracks = remove_root_od = single_group = 0; - conv_type = HintIt = needSave = print_sdp = print_info = regular_iod = dump_std = open_edit = dump_isom = dump_rtp = dump_cr = dump_chap = dump_srt = dump_ttxt = force_new = dump_timestamps = dump_m2ts = dump_cart = import_subtitle = force_cat = pack_wgt = dash_dynamic = dash_live = 0; - bitstream_switching_mode = 1; + conv_type = HintIt = needSave = print_sdp = print_info = regular_iod = dump_std = open_edit = dump_isom = dump_rtp = dump_cr = dump_chap = dump_srt = dump_ttxt = force_new = dump_timestamps = dump_m2ts = dump_cart = import_subtitle = force_cat = pack_wgt = dash_live = 0; + dash_dynamic = 0; /*align cat is the new default behaviour for -cat*/ align_cat = 1; subsegs_per_sidx = 0; @@ -1468,6 +1479,12 @@ int mp4boxMain(int argc, char **argv) trackID = atoi(argv[i+1]); i++; } + else if (!stricmp(arg, "-raw-layer")) { + CHECK_NEXT_ARG + track_dump_type = GF_EXPORT_NATIVE | GF_EXPORT_SVC_LAYER; + trackID = atoi(argv[i+1]); + i++; + } else if (!stricmp(arg, "-qcp")) { CHECK_NEXT_ARG track_dump_type = GF_EXPORT_NATIVE | GF_EXPORT_USE_QCP; @@ -1623,6 +1640,7 @@ int mp4boxMain(int argc, char **argv) else if (!stricmp(arg, "-isma")) { conv_type = GF_ISOM_CONV_TYPE_ISMA; open_edit = 1; } else if (!stricmp(arg, "-3gp")) { conv_type = GF_ISOM_CONV_TYPE_3GPP; open_edit = 1; } else if (!stricmp(arg, "-ipod")) { conv_type = GF_ISOM_CONV_TYPE_IPOD; open_edit = 1; } + else if (!stricmp(arg, "-psp")) { conv_type = GF_ISOM_CONV_TYPE_PSP; open_edit = 1; } else if (!stricmp(arg, "-ismax")) { conv_type = GF_ISOM_CONV_TYPE_ISMA_EX; open_edit = 1; } else if (!stricmp(arg, "-no-sys") || !stricmp(arg, "-nosys")) { remove_sys_tracks = 1; open_edit = 1; } @@ -1685,14 +1703,16 @@ int mp4boxMain(int argc, char **argv) i++; } else if (!stricmp(arg, "-bs-switching")) { CHECK_NEXT_ARG - if (!stricmp(argv[i+1], "no") || !stricmp(argv[i+1], "off")) bitstream_switching_mode = 0; - else if (!stricmp(argv[i+1], "single")) bitstream_switching_mode = 2; - else bitstream_switching_mode = 1; + if (!stricmp(argv[i+1], "no") || !stricmp(argv[i+1], "off")) bitstream_switching_mode = GF_DASH_BSMODE_NONE; + else if (!stricmp(argv[i+1], "merge")) bitstream_switching_mode = GF_DASH_BSMODE_MERGED; + else if (!stricmp(argv[i+1], "single")) bitstream_switching_mode = GF_DASH_BSMODE_SINGLE; + else if (!stricmp(argv[i+1], "single_merge")) bitstream_switching_mode = GF_DASH_BSMODE_SINGLE_MERGED; + else bitstream_switching_mode = GF_DASH_BSMODE_INBAND; i++; } else if (!stricmp(arg, "-dynamic")) { dash_dynamic = 1; } - else if (!strnicmp(arg, "-dash-live", 10)) { - dash_dynamic = 1; + else if (!strnicmp(arg, "-dash-live", 10) || !strnicmp(arg, "-ddbg-live", 10)) { + dash_dynamic = !strnicmp(arg, "-ddbg-live", 10) ? 2 : 1; dash_live = 1; if (arg[10]=='=') { dash_ctx_file = arg+11; @@ -1876,7 +1896,8 @@ int mp4boxMain(int argc, char **argv) tracks = gf_realloc(tracks, sizeof(TrackAction) * (nb_track_act+1)); tracks[nb_track_act].act_type = 4; - strcpy(szTK, argv[i+1]); + assert(strlen(argv[i+1])+1 <= sizeof(szTK)); + strncpy(szTK, argv[i+1], sizeof(szTK)); ext = strchr(szTK, '='); if (!ext) { fprintf(stderr, "Bad format for track par - expecting ID=PAR_NUM:PAR_DEN got %s\n", argv[i+1]); @@ -1885,7 +1906,13 @@ int mp4boxMain(int argc, char **argv) if (!stricmp(ext+1, "none")) { tracks[nb_track_act].par_num = tracks[nb_track_act].par_den = -1; } else { - sscanf(ext+1, "%d:%d", &tracks[nb_track_act].par_num, &tracks[nb_track_act].par_den); + sscanf(ext+1, "%d", &tracks[nb_track_act].par_num); + ext = strchr(ext+1, ':'); + if (!ext) { + fprintf(stderr, "Bad format for track par - expecting ID=PAR_NUM:PAR_DEN got %s\n", argv[i+1]); + MP4BOX_EXIT_WITH_CODE(1); + } + sscanf(ext+1, "%d", &tracks[nb_track_act].par_den); } ext[0] = 0; tracks[nb_track_act].trackID = atoi(szTK); @@ -2542,11 +2569,30 @@ int mp4boxMain(int argc, char **argv) } for (i=0; i<(u32) argc; i++) { if (!strcmp(argv[i], "-add")) { - e = import_file(file, argv[i+1], import_flags, import_fps, agg_samples); + char *src = argv[i+1]; + + e = import_file(file, src, import_flags, import_fps, agg_samples); if (e) { - fprintf(stderr, "Error importing %s: %s\n", argv[i+1], gf_error_to_string(e)); - gf_isom_delete(file); - MP4BOX_EXIT_WITH_CODE(1); + while (src) { + char *sep = strchr(src, '+'); + if (sep) sep[0] = 0; + + e = import_file(file, src, import_flags, import_fps, agg_samples); + + if (sep) { + sep[0] = '+'; + src = sep+1; + } else { + src= NULL; + } + if (e) + break; + } + if (e) { + fprintf(stderr, "Error importing %s: %s\n", argv[i+1], gf_error_to_string(e)); + gf_isom_delete(file); + MP4BOX_EXIT_WITH_CODE(1); + } } i++; } @@ -2706,14 +2752,19 @@ int mp4boxMain(int argc, char **argv) char c = (char) gf_prompt_get_char(); if (c=='q') break; } - fprintf(stderr, "sleep for %d ms\n", sleep_for); - gf_sleep(sleep_for); + if (sleep_for) { + if (dash_dynamic != 2) { + fprintf(stderr, "sleep for %d ms\n", sleep_for); + gf_sleep(sleep_for); + } + } } else { break; } } if (dash_ctx) gf_cfg_del(dash_ctx); + if (e) fprintf(stderr, "Error DASHing file: %s\n", gf_error_to_string(e)); gf_sys_close(); MP4BOX_EXIT_WITH_CODE( (e!=GF_OK) ? 1 : 0 ); @@ -3172,6 +3223,8 @@ int mp4boxMain(int argc, char **argv) switch (gf_isom_get_media_subtype(file, i+1, 1)) { case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: fprintf(stderr, "Forcing AVC/H264 SAR to 1:1...\n"); gf_media_change_par(file, i+1, 1, 1); break; @@ -3405,8 +3458,8 @@ int mp4boxMain(int argc, char **argv) n = t = 0; memset(_t, 0, sizeof(char)*8); tlen = (itag==GF_ISOM_ITUNE_DISK) ? 6 : 8; - if (sscanf(val, "%u/%u", &n, &t) == 2) { _t[3]=n; _t[5]=t;} - else if (sscanf(val, "%u", &n) == 1) { _t[3]=n;} + if (sscanf(val, "%u/%u", &n, &t) == 2) { _t[3]=n; _t[2]=n>>8; _t[5]=t; _t[4]=t>>8; } + else if (sscanf(val, "%u", &n) == 1) { _t[3]=n; _t[2]=n>>8;} else tlen = 0; if (tlen) gf_isom_apple_set_tag(file, itag, _t, tlen); } diff --git a/applications/mp4client/main.c b/applications/mp4client/main.c index caa330b..7fcd459 100644 --- a/applications/mp4client/main.c +++ b/applications/mp4client/main.c @@ -1155,7 +1155,13 @@ int main (int argc, char **argv) fprintf(stderr, "Loading modules\n"); str = gf_cfg_get_key(cfg_file, "General", "ModulesDirectory"); - assert( str ); + if (! str ) { + fprintf(stderr, "Mmodule directory not found - check the configuration file exit and the \"ModulesDirectory\" key is set\n"); + gf_cfg_del(cfg_file); + gf_sys_close(); + if (logfile) fclose(logfile); + return 1; + } user.modules = gf_modules_new((const unsigned char *) str, cfg_file); if (user.modules) i = gf_modules_get_count(user.modules); diff --git a/applications/ts2hds/f4m.c b/applications/ts2hds/f4m.c index b09aa49..92b31b6 100644 --- a/applications/ts2hds/f4m.c +++ b/applications/ts2hds/f4m.c @@ -29,7 +29,7 @@ #include -#define ADOBE_INLINED_BOOTSTRAP +//#define ADOBE_INLINED_BOOTSTRAP struct __tag_adobe_stream { @@ -47,6 +47,20 @@ struct __tag_adobe_multirate GF_List *streams; }; +static GF_Err adobe_gen_stream_manifest(AdobeStream *as) +{ + fprintf(as->f, "\n"); + fprintf(as->f, "\n"); + fprintf(as->f, "%s\n", as->id); + if (as->base_url) + fprintf(as->f, "%s\n", as->base_url); + fprintf(as->f, "\n", as->id, as->bitrate, as->id, as->bitrate); + fprintf(as->f, "\n", as->id, as->bitrate, as->bitrate, as->id, as->bitrate); + fprintf(as->f, "\n"); + + return GF_OK; +} + AdobeMultirate *adobe_alloc_multirate_manifest(char *id) { AdobeMultirate *am = gf_calloc(1, sizeof(AdobeMultirate)); @@ -70,6 +84,16 @@ AdobeMultirate *adobe_alloc_multirate_manifest(char *id) AdobeStream *as = gf_calloc(1, sizeof(AdobeStream)); as->id = "HD"; as->bitrate = 100; + sprintf(filename, "%s_%s_%d.f4m", am->id, as->id, as->bitrate); + as->f = fopen(filename, "wt"); + if (!as->f) { + fprintf(stderr, "Couldn't create Adobe stream manifest file: %s\n", filename); + assert(0); + gf_list_del(am->streams); + gf_free(as); + gf_free(am); + return NULL; + } gf_list_add(am->streams, as); } @@ -86,6 +110,8 @@ void adobe_free_multirate_manifest(AdobeMultirate *am) for (i=0; istreams); i++) { AdobeStream *as = gf_list_get(am->streams, i); assert(as); + if (as->f) + fclose(as->f); //TODO: base_url and id may be stored as gf_strdup in the future gf_list_rem(am->streams, i); gf_free(as); @@ -97,6 +123,7 @@ void adobe_free_multirate_manifest(AdobeMultirate *am) GF_Err adobe_gen_multirate_manifest(AdobeMultirate* am, char *bootstrap, size_t bootstrap_size) { + GF_Err e; u32 i; #ifdef ADOBE_INLINED_BOOTSTRAP char bootstrap64[GF_MAX_PATH]; @@ -104,7 +131,7 @@ GF_Err adobe_gen_multirate_manifest(AdobeMultirate* am, char *bootstrap, size_t #endif fprintf(am->f, "\n"); - fprintf(am->f, "\n"); + fprintf(am->f, "\n"); fprintf(am->f, "%s\n", am->id); fprintf(am->f, "%s\n", am->base_url); fprintf(am->f, "live\n"); @@ -123,7 +150,6 @@ GF_Err adobe_gen_multirate_manifest(AdobeMultirate* am, char *bootstrap, size_t } fprintf(am->f, "\n\n"); #else - fprintf(am->f, "\n", as->id, as->bitrate, as->id, as->bitrate); { char filename[GF_MAX_PATH]; FILE *bstfile; @@ -133,7 +159,13 @@ GF_Err adobe_gen_multirate_manifest(AdobeMultirate* am, char *bootstrap, size_t fclose(bstfile); } #endif - fprintf(am->f, "\n", am->id, as->id, as->bitrate, as->bitrate, as->id, as->bitrate); + e = adobe_gen_stream_manifest(as); + if (!e) { + if (!am->base_url && !as->base_url) + fprintf(stderr, "Warning: no base_url specified\n"); + + fprintf(am->f, "\n", am->id, as->id, as->bitrate, as->bitrate); + } } fprintf(am->f, "\n"); diff --git a/applications/ts2hds/f4v.c b/applications/ts2hds/f4v.c index f49284b..9a80b65 100644 --- a/applications/ts2hds/f4v.c +++ b/applications/ts2hds/f4v.c @@ -61,7 +61,7 @@ GF_Err adobize_segment(GF_ISOFile *isom_file, AdobeHDSCtx *ctx) afra->entry_count = 1; ae->time = init_seg_time; - ae->offset = 31663; + ae->offset = 3999; gf_list_add(afra->local_access_entries, ae); afra->global_entries = 0; @@ -101,7 +101,7 @@ GF_Err adobize_segment(GF_ISOFile *isom_file, AdobeHDSCtx *ctx) asrt->flags = 0; asrt->segment_run_entry_count = 1; { - asre->first_segment = 1; + asre->first_segment = ctx->segnum; asre->fragment_per_segment = 1; } e = gf_list_add(asrt->segment_run_entry_table, asre); @@ -124,7 +124,7 @@ GF_Err adobize_segment(GF_ISOFile *isom_file, AdobeHDSCtx *ctx) afrt->type = GF_4CC('a', 'f', 'r', 't'); afrt->version = 0; afrt->flags = 0; - afrt->timescale = 1000; + afrt->timescale = gf_isom_get_timescale(isom_file); afrt->fragment_run_entry_count = 1; { afre->first_fragment = 1; diff --git a/applications/ts2hds/main.c b/applications/ts2hds/main.c index 0e98a52..09fc32b 100644 --- a/applications/ts2hds/main.c +++ b/applications/ts2hds/main.c @@ -141,8 +141,7 @@ int main(int argc, char **argv) e = GF_OK; memset(&ctx, 0, sizeof(ctx)); - ctx.multirate_manifest = adobe_alloc_multirate_manifest(output); - ctx.curr_time = 6000; + ctx.curr_time = 0; ctx.segnum = 1; /*********************************************/ @@ -153,6 +152,8 @@ int main(int argc, char **argv) goto exit; } + ctx.multirate_manifest = adobe_alloc_multirate_manifest(output); + #if 0 /*'moov' conversion tests*/ { char metamoov64[GF_MAX_PATH]; diff --git a/bin/smartphone 2003 (armv4)/release/install/gpac.inf b/bin/smartphone 2003 (armv4)/release/install/gpac.inf index 0bce1e0..870c6ea 100644 --- a/bin/smartphone 2003 (armv4)/release/install/gpac.inf +++ b/bin/smartphone 2003 (armv4)/release/install/gpac.inf @@ -46,12 +46,7 @@ js32.dll = 1 gm_aac_in.dll = 1 gm_bifs_dec.dll = 1 gm_ctx_load.dll = 1 -gm_dummy_in.dll = 1 -gm_ffmpeg_in.dll = 1 -avcodec-52.dll = 1 -avformat-52.dll = 1 -avutil-50.dll = 1 -swscale-0.dll = 1 +gm_dummy_in.dll = 1 gm_ft_font.dll = 1 gm_gapi.dll = 1 gm_gpac_js.dll = 1 @@ -71,7 +66,12 @@ gm_wav_out.dll = 1 gm_widgetman.dll = 1 gm_xvid_dec.dll = 1 gm_ogg.dll = 1 - +;commenting out ffmpeg since we no longer have a recent version (and no one uses smartphone 2003 anymore ...) +;gm_ffmpeg_in.dll = 1 +;avcodec-52.dll = 1 +;avformat-52.dll = 1 +;avutil-50.dll = 1 +;swscale-0.dll = 1 ;================================================== diff --git a/configure b/configure index e3220ac..a5ca4e1 100755 --- a/configure +++ b/configure @@ -18,13 +18,7 @@ fi #remember the ./configure command line -for v in "$@"; do - r="${v#*=}" - l="${v%$r}" - test "$r" = "${r#* }" || r="'$r'" - GPAC_CONFIGURATION="${GPAC_CONFIGURATION# } ${l}${r}" -done - +GPAC_CONFIGURATION="$@" TMPC="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.c" TMPH="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.h" @@ -150,6 +144,7 @@ disable_isoff_write="no" disable_isoff_frag="no" disable_isoff_hint="no" disable_isoff_frag="no" +disable_isoff_hds="no" disable_streaming="no" disable_player="no" disable_scenegraph="no" @@ -283,6 +278,7 @@ Configuration options for libgpac - all options can be enabled with --enable-opt --disable-isoff-write disable ISO File Format edit/write --disable-isoff-hint disable ISO File Format hinting --disable-isoff-frag disable fragments in ISO File Format + --disable-isoff-hds disable HDS support in ISO File Format --disable-streaming disable RTP/RTSP/SDP --disable-dvbx disable DVB-specific tools (MPE, FEC, DSM-CC) --disable-vobsub disable VobSub support @@ -294,7 +290,7 @@ Configuration options for libgpac - all options can be enabled with --enable-opt Extra libraries configuration. You can turn a libray off or force using the local version in gpac/extra_lib/ --use-js=OPT force SpiderMonkey ECMAScript OPT=[no,local] --use-ft=OPT force FreeType OPT=[no,local] - --use-zlib=OPT force ZLIB OPT=[no,local] + --use-zlib=OPT force ZLIB OPT=[no,system,local] --use-jpeg=OPT force JPEG OPT=[no,local] --use-png=OPT force PNG OPT=[no,local] --use-faad=OPT force FAAD OPT=[no,local] @@ -653,7 +649,7 @@ if test "$cross_prefix" = "" ; then has_zlib="system" fi fi -if test "$has_zlib" = "no" ; then +if test "$has_zlib" = "force-no" ; then if $cc -o $TMPO $TMPC -I"$local_inc/zlib" -L$local_lib -lz 2> /dev/null ; then has_zlib="local" fi @@ -869,6 +865,8 @@ int main( void ) { jsval *vp; JSObject *obj = JS_NewObjectForConstructor(c, vp); EOF if $cc -o $TMPO $TMPC $js_flags $LDFLAGS $js_lib 2> /dev/null ; then js_flags="-DUSE_FFDEV_12 $js_flags" + elif grep JSMutableHandleValue $js_inc/jsapi.h > /dev/null 2>&1 ; then + js_flags="-DUSE_FFDEV_17 $js_flags" elif ! grep JS_ConstructObject $js_inc/jsapi.h > /dev/null 2>&1 ; then js_flags="-DUSE_FFDEV_16 $js_flags" elif grep JSHandleObject $js_inc/jsapi.h > /dev/null 2>&1 ; then @@ -1713,8 +1711,13 @@ for opt do echo fi fi + has_zlib=$tmp_has_zlib + elif test "$tmp_has_zlib" = "no" ; then + echo + echo "WARNING!! : you have forced not to use ZLIB. This will disable some core functionalities of GPAC." + echo + has_zlib="force-no" fi - has_png=$tmp_has_png ;; --use-ogg=*) has_ogg=${opt#--use-ogg=} ;; @@ -1729,7 +1732,7 @@ for opt do --enable-pulseaudio=*) has_pulseaudio="yes" ;; - --disable-all) has_pulseaudio="no"; has_alsa="no"; disable_core_tools="yes"; disable_3d="yes"; disable_svg="yes"; disable_vrml="yes"; disable_od="yes"; disable_bifs="yes"; disable_bifs_enc="yes"; disable_laser="yes"; disable_seng="yes"; disable_qtvr="yes"; disable_avi="yes"; disable_ogg="yes"; disable_m2ps="yes"; disable_m2ts="yes"; disable_m2ts_mux="yes"; disable_parsers="yes"; disable_import="yes"; disable_export="yes"; disable_swf="yes"; disable_scene_stats="yes"; disable_scene_dump="yes"; disable_scene_encode="yes"; disable_loader_isoff="yes"; disable_od_dump="yes"; disable_od_parse="yes"; disable_isom_dump="yes"; disable_mcrypt="yes"; disable_isoff="yes"; disable_isoff_write="yes"; disable_isoff_hint="yes"; disable_isoff_frag="yes"; disable_streaming="yes"; disable_x3d="yes"; disable_loader_bt="yes"; disable_loader_xmt="yes"; has_dvb4linux="no"; disable_player="yes"; disable_vobsub="yes"; disable_scenegraph="yes"; disable_dvbx="yes"; disable_ttxt="yes"; disable_saf="yes"; disable_smgr="yes"; disable_mpd="yes"; disable_dash="yes" + --disable-all) has_pulseaudio="no"; has_alsa="no"; disable_core_tools="yes"; disable_3d="yes"; disable_svg="yes"; disable_vrml="yes"; disable_od="yes"; disable_bifs="yes"; disable_bifs_enc="yes"; disable_laser="yes"; disable_seng="yes"; disable_qtvr="yes"; disable_avi="yes"; disable_ogg="yes"; disable_m2ps="yes"; disable_m2ts="yes"; disable_m2ts_mux="yes"; disable_parsers="yes"; disable_import="yes"; disable_export="yes"; disable_swf="yes"; disable_scene_stats="yes"; disable_scene_dump="yes"; disable_scene_encode="yes"; disable_loader_isoff="yes"; disable_od_dump="yes"; disable_od_parse="yes"; disable_isom_dump="yes"; disable_mcrypt="yes"; disable_isoff="yes"; disable_isoff_write="yes"; disable_isoff_hint="yes"; disable_isoff_frag="yes"; disable_streaming="yes"; disable_x3d="yes"; disable_loader_bt="yes"; disable_loader_xmt="yes"; has_dvb4linux="no"; disable_player="yes"; disable_vobsub="yes"; disable_scenegraph="yes"; disable_dvbx="yes"; disable_ttxt="yes"; disable_saf="yes"; disable_smgr="yes"; disable_mpd="yes"; disable_dash="yes"; disable_isoff_hds = "yes" ;; --disable-3d) disable_3d="yes" @@ -1866,6 +1869,10 @@ for opt do ;; --enable-isoff-frag) disable_isoff_frag="no" ;; + --disable-isoff-hds) disable_isoff_hds="yes" + ;; + --enable-isoff-hds) disable_isoff_hds="no" + ;; --disable-streaming) disable_streaming="yes" ;; --enable-streaming) disable_streaming="no" @@ -2405,6 +2412,11 @@ if test "$disable_isoff_frag" = "yes" ; then echo "ISO File Format fragments disabled" echo "#define GPAC_DISABLE_ISOM_FRAGMENTS" >> $TMPH fi +if test "$disable_isoff_hds" = "yes" ; then + echo "ISO File Format Adobe HDS disabled" + echo "#define GPAC_DISABLE_ISOM_ADOBE" >> $TMPH +fi + if test "$disable_streaming" = "yes" ; then echo "RTP/RTSP/SDP streaming disabled" echo "#define GPAC_DISABLE_STREAMING" >> $TMPH @@ -2692,6 +2704,9 @@ else echo "#define GPAC_HAS_SPIDERMONKEY" >> $TMPH fi echo "CONFIG_ZLIB=$has_zlib" >> config.mak +if test "$has_zlib" = "no" ; then + echo "#define GPAC_DISABLE_ZLIB" >> $TMPH +fi echo "CONFIG_FT=$has_ft" >> config.mak echo "CONFIG_JPEG=$has_jpeg" >> config.mak @@ -2765,6 +2780,7 @@ echo "DISABLE_CORE_TOOLS=$disable_core_tools" >> config.mak echo "DISABLE_OD_DUMP=$disable_od_dump" >> config.mak echo "DISABLE_OD_PARSE=$disable_od_parse" >> config.mak echo "MINIMAL_OD=$disable_od" >> config.mak +echo "DISABLE_ISOM_ADOBE=$disable_isoff_hds" >> config.mak if test "$disable_parsers" = "yes" ; then disable_m2ts_mux="yes" diff --git a/doc/configuration.html b/doc/configuration.html index 1062baf..1eae4e4 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -10,7 +10,7 @@
GPAC Configuration file documentation
Version 0.5.0

-Last Modified $LastChangedDate: 2012-11-15 18:57:26 +0000 (Thu, 15 Nov 2012) $ +Last Modified $LastChangedDate: 2012-12-06 19:08:32 +0000 (Thu, 06 Dec 2012) $



@@ -728,9 +728,12 @@ For debug purposes, instructs the player to switch representation every N segmen DisableSwitching [value: yes, no]

Disables automatic adaptation logic. Default is no

-KeepFiles [value: yes, no] +MemoryStorage [value: yes, no] +

+Files are only stored in memory and destroyed after playback, no disk IO is used. Default is no

+UseMaxResolution [value: yes, no]

-Forces files to be kept on disk. Default is no

+Forces the player to set the output video resolution to the max resolution available instead of resizing the window. Default is yes

StartRepresentation [value: minBandwidth, maxBandwidth, minQuality, maxQuality]

Instructs the DASH client to start playing the indicated representation before doing any switching. Default is minBandwidth.

diff --git a/include/gpac/avparse.h b/include/gpac/avparse.h index 3bd79e6..c8b4f29 100644 --- a/include/gpac/avparse.h +++ b/include/gpac/avparse.h @@ -220,6 +220,7 @@ u32 gf_ac3_get_channels(u32 acmod); u32 gf_ac3_get_bitrate(u32 brcode); GF_Err gf_avc_get_sps_info(char *sps, u32 sps_size, u32 *sps_id, u32 *width, u32 *height, s32 *par_n, s32 *par_d); +GF_Err gf_avc_get_pps_info(char *pps, u32 pps_size, u32 *pps_id, u32 *sps_id); const char *gf_avc_get_profile_name(u8 video_prof); #endif /*GPAC_DISABLE_AV_PARSERS*/ diff --git a/include/gpac/configuration.h b/include/gpac/configuration.h index 320d552..ae829b5 100644 --- a/include/gpac/configuration.h +++ b/include/gpac/configuration.h @@ -58,6 +58,9 @@ #define MOZILLA_1_8_BRANCH #endif +/*zlib enabled*/ +//#define GPAC_DISABLE_ZLIB + /*libjpeg enabled*/ #define GPAC_HAS_JPEG diff --git a/include/gpac/constants.h b/include/gpac/constants.h index eab19e1..21409cb 100644 --- a/include/gpac/constants.h +++ b/include/gpac/constants.h @@ -268,6 +268,8 @@ enum GPAC_OTI_VIDEO_AVC = 0x21, /*!OTI for AVC Parameter sets streams*/ GPAC_OTI_VIDEO_AVC_PS = 0x22, + /*!OTI for HEVC video */ + GPAC_OTI_VIDEO_HEVC = 0x23, /*!OTI for MPEG-4 AAC streams*/ GPAC_OTI_AUDIO_AAC_MPEG4 = 0x40, @@ -563,6 +565,40 @@ enum #define GF_AVC_TYPE2_SI 9 + +/*HEVC NAL unit types*/ +enum { + GF_HEVC_NALU_SLICE_TRAIL_N = 0, + GF_HEVC_NALU_SLICE_TRAIL_R = 1, + GF_HEVC_NALU_SLICE_TSA_N = 2, + GF_HEVC_NALU_SLICE_TSA_R = 3, + GF_HEVC_NALU_SLICE_STSA_N = 4, + GF_HEVC_NALU_SLICE_STSA_R = 5, + GF_HEVC_NALU_SLICE_RADL_N = 6, + GF_HEVC_NALU_SLICE_RADL_R = 7, + GF_HEVC_NALU_SLICE_RASL_N = 8, + GF_HEVC_NALU_SLICE_RASL_R = 9, + + GF_HEVC_NALU_SLICE_BLA_W_LP = 16, + GF_HEVC_NALU_SLICE_BLA_W_DLP = 17, + GF_HEVC_NALU_SLICE_BLA_N_LP = 18, + + GF_HEVC_NALU_SLICE_IDR_W_DLP = 19, + GF_HEVC_NALU_SLICE_IDR_N_LP = 20, + GF_HEVC_NALU_SLICE_CRA = 21, + + GF_HEVC_NALU_VID_PARAM = 32, + GF_HEVC_NALU_SEQ_PARAM = 33, + GF_HEVC_NALU_PIC_PARAM = 34, + GF_HEVC_NALU_ACCESS_UNIT = 35, + GF_HEVC_NALU_END_OF_SEQ = 36, + GF_HEVC_NALU_END_OF_STREAM = 37, + GF_HEVC_NALU_FILLER_DATA = 38, + GF_HEVC_NALU_SEI_PREFIX = 39, + GF_HEVC_NALU_SEI_SUFFIX = 40, +}; + + /*rate sizes - note that these sizes INCLUDE the rate_type header byte*/ static const unsigned int GF_QCELP_RATE_TO_SIZE [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; static const unsigned int GF_QCELP_RATE_TO_SIZE_NB = 7; diff --git a/include/gpac/dash.h b/include/gpac/dash.h index 2f865cf..828ea7d 100644 --- a/include/gpac/dash.h +++ b/include/gpac/dash.h @@ -83,7 +83,7 @@ struct _gf_dash_io /*resetup the file session with a new resource to get - this allows persistent connection usage with HTTP servers*/ GF_Err (*setup_from_url)(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, const char *url); /*set download range for the file session*/ - GF_Err (*set_range)(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, u64 start_range, u64 end_range); + GF_Err (*set_range)(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, u64 start_range, u64 end_range, Bool discontinue_cache); /*initialize the file session - all the headers shall be fetched before returning*/ GF_Err (*init)(GF_DASHFileIO *dashio, GF_DASHFileIOSession session); /*download the content - synchronous call: all the file shall be fetched before returning*/ @@ -142,7 +142,7 @@ const char *gf_dash_get_url(GF_DashClient *dash); void gf_dash_get_info(GF_DashClient *dash, const char **title, const char **source); /*switches quality up or down*/ -void gf_dash_switch_quality(GF_DashClient *dash, Bool switch_up); +void gf_dash_switch_quality(GF_DashClient *dash, Bool switch_up, Bool force_immediate_switch); /*indicates whether the DASH client is running or not. For the moment, the DASH client is always run by an internal thread*/ Bool gf_dash_is_running(GF_DashClient *dash); @@ -174,11 +174,13 @@ const char *gf_dash_group_get_segment_mime(GF_DashClient *dash, u32 idx); const char *gf_dash_group_get_segment_init_url(GF_DashClient *dash, u32 idx, u64 *start_range, u64 *end_range); /*returns the URL and byte range of the next media resource to play in this group. -If switching occured and no bitstream switching is possible, also set the url and byte range of the media file required to intialize the playback +If switching occured, sets switching_index to the new representation index. +If no bitstream switching is possible, also set the url and byte range of the media file required to intialize +the playback of the next segment original_url is optional and may be used to het the URI of the segment */ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, const char **url, u64 *start_range, u64 *end_range, - const char **switching_url, u64 *switching_start_range, u64 *switching_end_range, + s32 *switching_index, const char **switching_url, u64 *switching_start_range, u64 *switching_end_range, const char **original_url); /*discards the first media resource in the queue of this group*/ void gf_dash_group_discard_segment(GF_DashClient *dash, u32 idx); @@ -209,6 +211,8 @@ void gf_dash_seek(GF_DashClient *dash, Double start_range); Double gf_dash_get_playback_start_range(GF_DashClient *dash); /*when seeking, this flag is set when the seek is outside of the previously playing segment.*/ Bool gf_dash_group_segment_switch_forced(GF_DashClient *dash, u32 idx); +/*get video info for this group if video*/ +GF_Err gf_dash_group_get_video_info(GF_DashClient *dash, u32 idx, u32 *max_width, u32 *max_height); /*returns the start_time of the first segment in the queue (usually the one being played)*/ Double gf_dash_group_current_segment_start_time(GF_DashClient *dash, u32 idx); @@ -216,6 +220,9 @@ Double gf_dash_group_current_segment_start_time(GF_DashClient *dash, u32 idx); /*allow reloading of MPD on the local file system - usefull for testing live generators*/ void gf_dash_allow_local_mpd_update(GF_DashClient *dash, Bool allow_local_mpd_update); +/*gets media info for representation*/ +GF_Err gf_dash_group_get_representation_info(GF_DashClient *dash, u32 idx, u32 representation_idx, u32 *width, u32 *height, u32 *audio_samplerate, u32 *bandwidth, const char **codecs); + #endif //GPAC_DISABLE_DASH_CLIENT diff --git a/include/gpac/download.h b/include/gpac/download.h index a28f9d2..b94b95e 100644 --- a/include/gpac/download.h +++ b/include/gpac/download.h @@ -170,6 +170,9 @@ extern "C" { GF_NETIO_SESSION_NOT_CACHED = 1<<1, /*indicates that the connection to the server should be kept once the download is successfully completed*/ GF_NETIO_SESSION_PERSISTENT = 1<<2, + /*file is stored in memory, and the cache name is set to gpac://%u@%p, where %d is the size in bytes and %d is the the pointer to the memory. + Memory cached files are destroyed upon downloader destruction*/ + GF_NETIO_SESSION_MEMORY_CACHE = 1<<3, }; @@ -318,9 +321,10 @@ extern "C" { *\param sess the download session *\param start_range HTTP download start range in byte *\param end_range HTTP download end range in byte + *\param discontinue If set, forces a new cache entry if byte range are not continuous. Otherwise a single cache entry is used to reconstruct the file *\note this can only be used when the session is not threaded */ - GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range); + GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache); /*! *\brief get cache file name * diff --git a/include/gpac/internal/compositor_dev.h b/include/gpac/internal/compositor_dev.h index afee21f..95f7ee0 100644 --- a/include/gpac/internal/compositor_dev.h +++ b/include/gpac/internal/compositor_dev.h @@ -246,7 +246,7 @@ struct __tag_compositor /*options*/ u32 aspect_ratio, antiAlias, texture_text_mode; Bool high_speed, stress_mode; - Bool force_opengl_2d; + Bool force_opengl_2d, was_opengl; #ifdef OPENGL_RASTER Bool opengl_raster; #endif @@ -331,7 +331,7 @@ struct __tag_compositor /*screen buffer for direct access*/ GF_VideoSurface hw_surface; /*output buffer is configured in video memory*/ - Bool video_memory, request_video_memory; + Bool video_memory, request_video_memory, was_system_memory; /*indicate if overlays were prezsent in the previous frame*/ Bool last_had_overlays; GF_RasterCallback raster_callbacks; diff --git a/include/gpac/internal/isomedia_dev.h b/include/gpac/internal/isomedia_dev.h index 151cf0b..eac3380 100644 --- a/include/gpac/internal/isomedia_dev.h +++ b/include/gpac/internal/isomedia_dev.h @@ -198,8 +198,13 @@ enum GF_ISOM_BOX_TYPE_PASP = GF_4CC( 'p', 'a', 's', 'p' ), GF_ISOM_BOX_TYPE_AVC1 = GF_4CC( 'a', 'v', 'c', '1' ), GF_ISOM_BOX_TYPE_AVC2 = GF_4CC( 'a', 'v', 'c', '2' ), + GF_ISOM_BOX_TYPE_AVC3 = GF_4CC( 'a', 'v', 'c', '3' ), + GF_ISOM_BOX_TYPE_AVC4 = GF_4CC( 'a', 'v', 'c', '4' ), GF_ISOM_BOX_TYPE_SVCC = GF_4CC( 's', 'v', 'c', 'C' ), GF_ISOM_BOX_TYPE_SVC1 = GF_4CC( 's', 'v', 'c', '1' ), + GF_ISOM_BOX_TYPE_HVCC = GF_4CC( 'h', 'v', 'c', 'C' ), + GF_ISOM_BOX_TYPE_HVC1 = GF_4CC( 'h', 'v', 'c', '1' ), + GF_ISOM_BOX_TYPE_HEV1 = GF_4CC( 'h', 'e', 'v', '1' ), /*LASeR extension*/ GF_ISOM_BOX_TYPE_LSRC = GF_4CC( 'l', 's', 'r', 'C' ), @@ -528,6 +533,13 @@ typedef struct char *name; /*private for editing*/ Bool is_unpacked; + /*private for checking dependency*/ + u32 originalFile; + u32 originalID; + + /*private for SVC/MVC extractors resolution*/ + s32 extractor_mode; + Bool has_base_layer; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS u64 dts_at_seg_start; @@ -788,6 +800,7 @@ typedef struct GF_SLConfig *slc; } GF_LASeRSampleEntryBox; +/*rewrites avcC based on the given esd - this destroys the esd*/ GF_Err LSR_UpdateESD(GF_LASeRSampleEntryBox *lsr, GF_ESD *esd); typedef struct @@ -841,6 +854,12 @@ typedef struct GF_AVCConfig *config; } GF_AVCConfigurationBox; +typedef struct +{ + GF_ISOM_BOX + GF_HEVCConfig *config; +} GF_HEVCConfigurationBox; + typedef struct { GF_ISOM_VISUAL_SAMPLE_ENTRY @@ -851,6 +870,9 @@ typedef struct /*avc extensions - we merged with regular 'mp4v' box to handle isma E&A signaling of AVC*/ GF_AVCConfigurationBox *avc_config; GF_AVCConfigurationBox *svc_config; + /*hevc extension*/ + GF_HEVCConfigurationBox *hevc_config; + GF_MPEG4BitRateBox *bitrate; /*ext descriptors*/ GF_MPEG4ExtensionDescriptorsBox *descr; @@ -861,6 +883,8 @@ typedef struct } GF_MPEGVisualSampleEntryBox; +Bool gf_isom_is_nalu_based_entry(GF_MediaBox *mdia, GF_SampleEntryBox *_entry); +GF_Err gf_isom_nalu_sample_rewrite(GF_MediaBox *mdia, GF_ISOSample *sample, u32 sampleNumber, GF_MPEGVisualSampleEntryBox *entry); /*this is the default visual sdst (to handle unknown media)*/ typedef struct @@ -2326,6 +2350,11 @@ GF_Err gf_isom_datamap_add_data(GF_DataMap *ptr, char *data, u32 dataSize); #define GF_ISOM_GET_FRAG_SYNC(flag) ( ! ( ( (flag) >> 16) & 0x1)) #define GF_ISOM_GET_FRAG_DEG(flag) (flag) & 0x7FFF +#define GF_ISOM_GET_FRAG_LEAD(flag) ( (flag) >> 26) & 0x3 +#define GF_ISOM_GET_FRAG_DEPENDS(flag) ( (flag) >> 24) & 0x3 +#define GF_ISOM_GET_FRAG_DEPENDED(flag) ( (flag) >> 22) & 0x3 +#define GF_ISOM_GET_FRAG_REDUNDANT(flag) ( (flag) >> 20) & 0x3 + #define GF_ISOM_GET_FRAG_DEPEND_FLAGS(lead, depends, depended, redundant) ( (lead<<26) | (depends<<24) | (depended<<22) | (redundant<<20) ) #define GF_ISOM_RESET_FRAG_DEPEND_FLAGS(flags) flags = flags & 0xFFFFF @@ -2416,6 +2445,7 @@ GF_ISOFile *gf_isom_new_movie(); GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *the_file, u32 trackNumber); GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber); GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID); +GF_TrackBox *gf_isom_get_track_from_original_id(GF_MovieBox *moov, u32 originalID, u32 originalFile); u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 trackID); /*open a movie*/ GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tmp_dir); @@ -2444,6 +2474,9 @@ GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample); GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex); Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex); + +GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, u32 TrackID); + /*check the TimeToSample for the given time and return the Sample number if the entry is not found, return the closest sampleNumber in prevSampleNumber and 0 in sampleNumber if the DTS required is after all DTSs in the list, set prevSampleNumber and SampleNumber to 0 @@ -2575,8 +2608,11 @@ GF_Err minf_AddBox(GF_Box *s, GF_Box *a); GF_Err mdia_AddBox(GF_Box *s, GF_Box *a); GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); -GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd); +/*rewrites avcC based on the given esd - this destroys the esd*/ +GF_Err AVC_HEVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd); void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc); +void HEVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc); + GF_Err reftype_AddRefTrack(GF_TrackReferenceTypeBox *ref, u32 trackID, u16 *outRefIndex); GF_XMLBox *gf_isom_get_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool *is_binary); @@ -3250,7 +3286,11 @@ GF_Err avcc_Size(GF_Box *s); GF_Box *avc1_New(); GF_Box *avc2_New(); +GF_Box *avc3_New(); +GF_Box *avc4_New(); GF_Box *svc1_New(); +GF_Box *hvc1_New(); +GF_Box *hev1_New(); GF_Box *m4ds_New(); void m4ds_del(GF_Box *s); @@ -3846,6 +3886,13 @@ GF_Err cslg_Size(GF_Box *s); GF_Err cslg_Read(GF_Box *s, GF_BitStream *bs); GF_Err cslg_dump(GF_Box *a, FILE * trace); +GF_Box *hvcc_New(); +void hvcc_del(GF_Box *); +GF_Err hvcc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hvcc_Size(GF_Box *s); +GF_Err hvcc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hvcc_dump(GF_Box *a, FILE * trace); + #endif /*GPAC_DISABLE_ISOM*/ #ifdef __cplusplus diff --git a/include/gpac/internal/media_dev.h b/include/gpac/internal/media_dev.h index 4ff8ab1..daf53d2 100644 --- a/include/gpac/internal/media_dev.h +++ b/include/gpac/internal/media_dev.h @@ -49,9 +49,15 @@ GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...); /*returns 0 if not a start code, or size of start code (3 or 4 bytes). If start code, bitstream is positionned AFTER start code*/ -u32 AVC_IsStartCode(GF_BitStream *bs); +u32 gf_media_nalu_is_start_code(GF_BitStream *bs); + /*returns size of chunk between current and next startcode (excluding startcode sizes), 0 if no more startcodes (eos)*/ -u32 AVC_NextStartCode(GF_BitStream *bs); +u32 gf_media_nalu_next_start_code_bs(GF_BitStream *bs); + +/*return nb bytes from current data until the next start code and set the size of the next start code (3 or 4 bytes) +returns data_len if no startcode found and sets sc_size to 0 (last nal in payload)*/ +u32 gf_media_nalu_next_start_code(u8 *data, u32 data_len, u32 *sc_size); + /*returns NAL unit type - bitstream must be sync'ed!!*/ u8 AVC_NALUType(GF_BitStream *bs); Bool SVC_NALUIsSlice(u8 type); @@ -133,6 +139,7 @@ typedef struct u32 slice_group_count; /* num_slice_groups_minus1 + 1*/ /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 sent*/ u32 status; + } AVC_PPS; typedef struct @@ -205,29 +212,163 @@ typedef struct /*return sps ID or -1 if error*/ -s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos); +s32 gf_media_avc_read_sps(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos); /*return pps ID or -1 if error*/ -s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc); +s32 gf_media_avc_read_pps(char *pps_data, u32 pps_size, AVCState *avc); /*return sps ID or -1 if error*/ -s32 AVC_ReadSeqParamSetExtId(char *spse_data, u32 spse_size); +s32 gf_media_avc_read_sps_ext(char *spse_data, u32 spse_size); /*is slice an IDR*/ -Bool AVC_SliceIsIDR(AVCState *avc); +Bool gf_media_avc_slice_is_IDR(AVCState *avc); /*is slice containing intra MB only*/ -Bool AVC_SliceIsIntra(AVCState *avc); +Bool gf_media_avc_slice_is_intra(AVCState *avc); /*parses NALU, updates avc state and returns: 1 if NALU part of new frame 0 if NALU part of prev frame -1 if bitstream error */ -s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc); +s32 gf_media_avc_parse_nalu(GF_BitStream *bs, u32 nal_hdr, AVCState *avc); /*remove SEI messages not allowed in MP4*/ /*nota: 'buffer' remains unmodified but cannot be set const*/ -u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc); +u32 gf_media_avc_reformat_sei(char *buffer, u32 nal_size, AVCState *avc); #ifndef GPAC_DISABLE_ISOM -GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d); +GF_Err gf_media_avc_change_par(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d); #endif + + +typedef struct +{ + Bool profile_present_flag, level_present_flag, tier_flag; + u8 profile_space; + u8 profile_idc; + u32 profile_compatibility_flag; + u8 level_idc; +} HEVC_SublayerPTL; + +typedef struct +{ + u8 profile_space, tier_flag, profile_idc, level_idc; + u32 profile_compatibility_flag; + + HEVC_SublayerPTL sub_ptl[8]; +} HEVC_ProfileTierLevel; + +typedef struct +{ + u32 num_negative_pics; + u32 num_positive_pics; + s32 delta_poc[16]; +} HEVC_ReferencePictureSets; + +typedef struct +{ + s32 id, vps_id; + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 stored*/ + u32 state; + u32 width, height; + + HEVC_ProfileTierLevel ptl; + + u8 chroma_format_idc; + Bool cw_flag ; + u32 cw_left, cw_right, cw_top, cw_bottom; + u8 bit_depth_luma; + u8 bit_depth_chroma; + u8 log2_max_pic_order_cnt_lsb; + Bool separate_colour_plane_flag; + + u32 max_CU_width, max_CU_height, max_CU_depth; + u32 bitsSliceSegmentAddress; + + u32 num_short_term_ref_pic_sets, num_long_term_ref_pic_sps; + HEVC_ReferencePictureSets rps[64]; +} HEVC_SPS; + +typedef struct +{ + s32 id; + u32 sps_id; + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 stored*/ + u32 state; + + Bool dependent_slice_segments_enabled_flag, tiles_enabled_flag, uniform_spacing_flag; + u32 num_extra_slice_header_bits; + Bool slice_segment_header_extension_present_flag, output_flag_present_flag; +} HEVC_PPS; + +typedef struct +{ + u16 avg_bit_rate, max_bit_rate, avg_pic_rate; + u8 constand_pic_rate_idc; +} HEVC_RateInfo; + +typedef struct +{ + s32 id; + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 stored*/ + u32 state; + u8 max_sub_layer; + HEVC_ProfileTierLevel ptl; + + HEVC_SublayerPTL sub_ptl[8]; + HEVC_RateInfo rates[8]; + +} HEVC_VPS; + +typedef struct +{ + AVCSeiRecoveryPoint recovery_point; + AVCSeiPicTiming pic_timing; + +} HEVC_SEI; + +typedef struct +{ + u8 nal_unit_type; + s8 temporal_id; + + u32 frame_num, poc_lsb, slice_type; + + s32 redundant_pic_cnt; + + s32 poc; + u32 poc_msb, poc_msb_prev, poc_lsb_prev, frame_num_prev; + s32 frame_num_offset, frame_num_offset_prev; + + HEVC_SPS *sps; + HEVC_PPS *pps; +} HEVCSliceInfo; + +typedef struct +{ + HEVC_SPS sps[16]; /* range allowed in the spec is 0..15 */ + s8 sps_active_idx; /*currently active sps; must be initalized to -1 in order to discard not yet decodable SEIs*/ + + HEVC_PPS pps[64]; + + HEVC_VPS vps[16]; + + HEVCSliceInfo s_info; + HEVC_SEI sei; + + Bool is_svc; +} HEVCState; + +enum +{ + GF_HEVC_TYPE_B = 0, + GF_HEVC_TYPE_P = 1, + GF_HEVC_TYPE_I = 2, +}; +s32 gf_media_hevc_read_vps(char *data, u32 size, HEVCState *hevc); +s32 gf_media_hevc_read_sps(char *data, u32 size, HEVCState *hevc); +s32 gf_media_hevc_read_pps(char *data, u32 size, HEVCState *hevc); +s32 gf_media_hevc_parse_nalu(GF_BitStream *bs, HEVCState *hevc, u8 *nal_unit_type, u8 *temporal_id); +Bool gf_media_hevc_slice_is_intra(HEVCState *hevc); +Bool gf_media_hevc_slice_is_IDR(HEVCState *hevc); + + #endif /*GPAC_DISABLE_AV_PARSERS*/ typedef struct diff --git a/include/gpac/internal/mpd.h b/include/gpac/internal/mpd.h index dfee351..cbb6e42 100644 --- a/include/gpac/internal/mpd.h +++ b/include/gpac/internal/mpd.h @@ -94,7 +94,7 @@ typedef struct #define GF_MPD_SEGMENT_BASE \ u32 timescale; \ u64 presentation_time_offset; \ - u32 index_range; \ + GF_MPD_ByteRange *index_range; \ Bool index_range_exact; \ GF_MPD_URL *initialization_segment; \ GF_MPD_URL *representation_index; \ @@ -124,6 +124,7 @@ typedef struct GF_MPD_ByteRange *media_range; char *index; GF_MPD_ByteRange *index_range; + u64 duration; } GF_MPD_SegmentURL; typedef struct @@ -329,6 +330,7 @@ GF_MPD *gf_mpd_new(); void gf_mpd_del(GF_MPD *mpd); /*frees a GF_MPD_SegmentURL structure (type-casted to void *)*/ void gf_mpd_segment_url_free(void *ptr); +void gf_mpd_segment_base_free(void *ptr); typedef struct _gf_file_get GF_FileDownload; struct _gf_file_get diff --git a/include/gpac/internal/smjs_api.h b/include/gpac/internal/smjs_api.h index 756347b..f53bd16 100644 --- a/include/gpac/internal/smjs_api.h +++ b/include/gpac/internal/smjs_api.h @@ -52,6 +52,10 @@ typedef struct /*new APIs*/ #if (JS_VERSION>=185) +#ifdef USE_FFDEV_17 +#define USE_FFDEV_16 +#endif + #ifdef USE_FFDEV_16 #define USE_FFDEV_15 #endif @@ -85,6 +89,24 @@ typedef double jsdouble; #define JS_NewDouble(c, v) v #define JS_PropertyStub_forSetter JS_StrictPropertyStub +#if defined(USE_FFDEV_17) + +#define SMJS_DECL_FUNC_PROP_SET(func_name) JSBool func_name(JSContext *c, JSHandleObject __hobj, JSHandleId __hid, JSBool strict, JSMutableHandleValue __vp) +#define SMJS_FUNC_PROP_SET(func_name) SMJS_DECL_FUNC_PROP_SET(func_name) { JSObject *obj = *(__hobj._); jsid id = *(__hid._); jsval *vp = __vp._; +#define SMJS_FUNC_PROP_SET_NOVP(func_name) SMJS_DECL_FUNC_PROP_SET(func_name) { JSObject *obj = *(__hobj._); jsid id = *(__hid._); + +#define SMJS_DECL_FUNC_PROP_GET(func_name) JSBool func_name(JSContext *c, JSHandleObject __hobj, JSHandleId __hid, JSMutableHandleValue __vp) +#define SMJS_FUNC_PROP_GET(func_name) SMJS_DECL_FUNC_PROP_GET( func_name ) { JSObject *obj = *(__hobj._); jsid id = *(__hid._); jsval *vp = __vp._; +#define SMJS_CALL_PROP_STUB() JS_PropertyStub(c, __hobj, __hid, __vp) +#define DECL_FINALIZE(func_name) void func_name(JSFreeOp *fop, JSObject *obj) { void *c = NULL; + +#define SMJS_FUNCTION_SPEC(__name, __fun, __argc) JS_FS(__name, __fun, __argc, 0) +#define SMJS_PROPERTY_SPEC(__name, __tinyid, __flags, __getter, __setter) \ + {__name, __tinyid, __flags, JSOP_WRAPPER(__getter), JSOP_WRAPPER(__setter)} + + +#else + #ifdef USE_FFDEV_15 #define SMJS_DECL_FUNC_PROP_SET(func_name) JSBool func_name(JSContext *c, JSHandleObject __hobj, JSHandleId __hid, JSBool strict, jsval *vp) @@ -105,8 +127,14 @@ typedef double jsdouble; #endif +#define SMJS_FUNC_PROP_SET_NOVP SMJS_FUNC_PROP_SET #define SMJS_FUNCTION_SPEC(__name, __fun, __argc) {__name, __fun, __argc, 0} +#define SMJS_PROPERTY_SPEC(__name, __tinyid, __flags, __getter, __setter) \ + {__name, __tinyid, __flags, __getter, __setter} + +#endif + #define SMJS_FUNCTION(__name) __name(JSContext *c, uintN argc, jsval *argsvp) #define SMJS_FUNCTION_EXT(__name, __ext) __name(JSContext *c, uintN argc, jsval *argsvp, __ext) #define SMJS_ARGS jsval *argv = JS_ARGV(c, argsvp); @@ -178,6 +206,7 @@ typedef double jsdouble; #define SMJS_DECL_FUNC_PROP_SET(func_name) JSBool func_name(JSContext *c, JSObject *obj, jsval id, jsval *vp) #define SMJS_FUNC_PROP_SET(func_name) SMJS_DECL_FUNC_PROP_SET(func_name) { +#define SMJS_FUNC_PROP_SET_NOVP SMJS_FUNC_PROP_SET #define SMJS_DECL_FUNC_PROP_GET(func_name) JSBool func_name(JSContext *c, JSObject *obj, jsval id, jsval *vp) #define SMJS_FUNC_PROP_GET(func_name) SMJS_DECL_FUNC_PROP_GET( func_name) { #define DECL_FINALIZE(func_name) void func_name(JSContext *c, JSObject *obj) { @@ -187,6 +216,7 @@ typedef double jsdouble; #define SMJS_PROP_GETTER jsval id #define JS_PropertyStub_forSetter JS_PropertyStub #define SMJS_FUNCTION_SPEC(__name, __fun, __argc) {__name, __fun, __argc, 0, 0} +#define SMJS_PROPERTY_SPEC(__name, __tinyid, __flags, __getter, __setter) {__name, __tinyid, __flags, __getter, __setter} #define SMJS_FUNCTION(__name) __name(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #define SMJS_FUNCTION_EXT(__name, __ext) __name(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, __ext) #define SMJS_ARGS diff --git a/include/gpac/isomedia.h b/include/gpac/isomedia.h index 9c001f2..73aa5f5 100644 --- a/include/gpac/isomedia.h +++ b/include/gpac/isomedia.h @@ -179,7 +179,11 @@ enum GF_AVCConfig to MPEG-4 ESD*/ GF_ISOM_SUBTYPE_AVC_H264 = GF_4CC( 'a', 'v', 'c', '1' ), GF_ISOM_SUBTYPE_AVC2_H264 = GF_4CC( 'a', 'v', 'c', '2' ), + GF_ISOM_SUBTYPE_AVC3_H264 = GF_4CC( 'a', 'v', 'c', '3' ), + GF_ISOM_SUBTYPE_AVC4_H264 = GF_4CC( 'a', 'v', 'c', '4' ), GF_ISOM_SUBTYPE_SVC_H264 = GF_4CC( 's', 'v', 'c', '1' ), + GF_ISOM_SUBTYPE_HVC1 = GF_4CC( 'h', 'v', 'c', '1' ), + GF_ISOM_SUBTYPE_HEV1 = GF_4CC( 'h', 'e', 'v', '1' ), /*3GPP(2) extension subtypes*/ GF_ISOM_SUBTYPE_3GP_H263 = GF_4CC( 's', '2', '6', '3' ), @@ -413,6 +417,9 @@ u32 gf_isom_get_track_id(GF_ISOFile *the_file, u32 trackNumber); /*return the track number of the track of specified ID, or 0 if error*/ u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID); +/*return the original trackID of the track number n, or 0 if error*/ +u32 gf_isom_get_track_original_id(GF_ISOFile *movie, u32 trackNumber); + /*gets the enable flag of a track 0: NO, 1: yes, 2: error*/ u8 gf_isom_is_track_enabled(GF_ISOFile *the_file, u32 trackNumber); @@ -625,8 +632,13 @@ return -1 if error, 0 if the reference is a NULL one, or the trackNumber */ GF_Err gf_isom_get_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrack); -/*Return 1 if the given track has a reference to the given TreckID of a given ReferenceType, 0 otherwise*/ -Bool gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 refTrackID); +/*Return the referenced track ID for a track and a given ReferenceType and Index +return -1 if error, 0 if the reference is a NULL one, or the trackNumber +*/ +GF_Err gf_isom_get_reference_ID(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrackID); + +/*Return referenceIndex if the given track has a reference to the given TreckID of a given ReferenceType, 0 otherwise*/ +u32 gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 refTrackID); u8 gf_isom_get_pl_indication(GF_ISOFile *the_file, u8 PL_Code); @@ -794,6 +806,9 @@ GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 ti returns error if trackID is already in used in the file*/ GF_Err gf_isom_set_track_id(GF_ISOFile *the_file, u32 trackNumber, u32 trackID); +/*force to rewrite all dependencies when trackID changes*/ +GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber); + /*Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several)*/ GF_Err gf_isom_add_sample(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample); @@ -1556,6 +1571,9 @@ GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 /*gets uncompressed SVC config - user is responsible for deleting it*/ GF_AVCConfig *gf_isom_svc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); +/*gets uncompressed HEVC config - user is responsible for deleting it*/ +GF_HEVCConfig *gf_isom_hevc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + typedef enum { GF_ISOM_AVCTYPE_NONE=0, @@ -1563,8 +1581,27 @@ typedef enum GF_ISOM_AVCTYPE_AVC_SVC, GF_ISOM_AVCTYPE_SVC_ONLY, } GF_ISOMAVCType; + u32 gf_isom_get_avc_svc_type(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +enum +{ + /*all extractors are rewritten*/ + GF_ISOM_NALU_EXTRACT_DEFAULT = 0, + /*all extractors are skipped but NALU data from this track is kept*/ + GF_ISOM_NALU_EXTRACT_LAYER_ONLY, + /*all extractors are kept (untouched sample) - used for dumping modes*/ + GF_ISOM_NALU_EXTRACT_INSPECT, + /*above mode is applied and PPS/SPS/... are appended in the front of every IDR*/ + GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG = 1<<16, + /*above mode is applied and all start codes are rewritten*/ + GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG = 2<<16, +}; + +GF_Err gf_isom_set_nalu_extract_mode(GF_ISOFile *the_file, u32 trackNumber, u32 nalu_extract_mode); +u32 gf_isom_get_nalu_extract_mode(GF_ISOFile *the_file, u32 trackNumber); + #ifndef GPAC_DISABLE_ISOM_WRITE /*creates new AVC config*/ GF_Err gf_isom_avc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); @@ -1576,6 +1613,16 @@ GF_Err gf_isom_svc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 Desc GF_Err gf_isom_svc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); /*deletes SVC config*/ GF_Err gf_isom_svc_config_del(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +/*sets avc3 entry type (inband SPS/PPS) instead of of avc1 (SPS/PPS in avcC box)*/ +GF_Err gf_isom_avc_set_inband_config(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + + +/*creates new AVC config*/ +GF_Err gf_isom_hevc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_HEVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*updates AVC config*/ +GF_Err gf_isom_hevc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_HEVCConfig *cfg); + #endif /*GPAC_DISABLE_ISOM_WRITE*/ diff --git a/include/gpac/media_tools.h b/include/gpac/media_tools.h index f860a7c..010f064 100644 --- a/include/gpac/media_tools.h +++ b/include/gpac/media_tools.h @@ -268,6 +268,7 @@ typedef struct char *file_name; char representationID[100]; char periodID[100]; + char role[100]; u32 bandwidth; } GF_DashSegmenterInput; @@ -283,13 +284,24 @@ typedef enum GF_DASH_PROFILE_UNKNOWN } GF_DashProfile; + +typedef enum +{ + GF_DASH_BSMODE_NONE = 0, + GF_DASH_BSMODE_INBAND, + GF_DASH_BSMODE_MERGED, + GF_DASH_BSMODE_SINGLE, + GF_DASH_BSMODE_SINGLE_MERGED, + +} GF_DashSwitchingMode; + GF_Err gf_dasher_segment_files(const char *mpd_name, GF_DashSegmenterInput *inputs, u32 nb_inputs, GF_DashProfile profile, const char *mpd_title, const char *mpd_source, const char *mpd_copyright, const char *mpd_moreInfoURL, const char **mpd_base_urls, u32 nb_mpd_base_urls, - Bool use_url_template, Bool single_segment, Bool single_file, Bool bitstream_switching_mode, + Bool use_url_template, Bool single_segment, Bool single_file, GF_DashSwitchingMode bitstream_switching_mode, Bool segments_start_with_rap, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, Double frag_duration_sec, s32 subsegs_per_sidx, Bool daisy_chain_sidx, Bool fragments_start_with_rap, const char *tmp_dir, - GF_Config *dash_ctx, Bool dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double subduration); + GF_Config *dash_ctx, u32 dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double subduration); /*returns time to wait until end of currently generated segments*/ u32 gf_dasher_next_update_time(GF_Config *dash_ctx, u32 mpd_update_time); @@ -339,6 +351,8 @@ enum GF_EXPORT_USE_QCP = (1<<11), /*indicates full NHML dump*/ GF_EXPORT_NHML_FULL = (1<<11), + /**/ + GF_EXPORT_SVC_LAYER = (1<<12), /*ony probes extraction format*/ GF_EXPORT_PROBE_ONLY = (1<<30), /*when set by user during export, will abort*/ diff --git a/include/gpac/modules/service.h b/include/gpac/modules/service.h index 388079f..e0b0575 100644 --- a/include/gpac/modules/service.h +++ b/include/gpac/modules/service.h @@ -94,6 +94,9 @@ typedef enum GF_NET_SERVICE_INFO, /*checks if there is an audio stream in the service - term->net only*/ GF_NET_SERVICE_HAS_AUDIO, + + /*checks if there is a forced video size - if yes, info is stored in GF_NetComPixelAR - term->net only*/ + GF_NET_SERVICE_HAS_FORCED_VIDEO_SIZE, /*instructs the service to get the migration info - term->net only*/ GF_NET_SERVICE_MIGRATION_INFO, diff --git a/include/gpac/mpeg4_odf.h b/include/gpac/mpeg4_odf.h index a31acfb..4f05173 100644 --- a/include/gpac/mpeg4_odf.h +++ b/include/gpac/mpeg4_odf.h @@ -697,6 +697,9 @@ typedef struct GF_List *IPIDataSet; GF_List *IPMPDescriptorPointers; GF_List *extensionDescriptors; + + /* 1 if this stream is referenced by type GF_ISOM_REF_BASE, 0 otherwise*/ + Bool has_ref_base; } GF_ESD; @@ -880,6 +883,36 @@ typedef struct } GF_AVCConfig; + +/*used for SPS/PPS/VPS/SEI*/ +typedef struct +{ + u8 type; + u8 array_completeness; + GF_List *nalus; +} GF_HEVCParamArray; + + +typedef struct +{ + u8 configurationVersion; + u8 profile_space; + u8 profile_idc; + u8 constraint_indicator_flags; + u8 level_idc; + u32 profile_compatibility_indications; + u8 chromaFormat; + u8 luma_bit_depth; + u8 chroma_bit_depth; + u16 avgFrameRate; + u8 constantFrameRate; + u8 numTemporalLayers; + + u8 nal_unit_size; + + GF_List *param_array; +} GF_HEVCConfig; + /************************************************************ Media Control Extensions ************************************************************/ @@ -1073,6 +1106,14 @@ GF_AVCConfig *gf_odf_avc_cfg_read(char *dsi, u32 dsi_size); /*writes GF_AVCConfig as MPEG-4 DSI*/ GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, char **outData, u32 *outSize); + +GF_HEVCConfig *gf_odf_hevc_cfg_new(); +void gf_odf_hevc_cfg_del(GF_HEVCConfig *cfg); +GF_Err gf_odf_hevc_cfg_write_bs(GF_HEVCConfig *cfg, GF_BitStream *bs); +GF_Err gf_odf_hevc_cfg_write(GF_HEVCConfig *cfg, char **outData, u32 *outSize); +GF_HEVCConfig *gf_odf_hevc_cfg_read_bs(GF_BitStream *bs); +GF_HEVCConfig *gf_odf_hevc_cfg_read(char *dsi, u32 dsi_size); + /*destroy the descriptors in a list but not the list*/ GF_Err gf_odf_desc_list_del(GF_List *descList); diff --git a/include/gpac/mpegts.h b/include/gpac/mpegts.h index 6bf26ac..f4ef599 100644 --- a/include/gpac/mpegts.h +++ b/include/gpac/mpegts.h @@ -206,6 +206,7 @@ enum GF_M2TS_SYSTEMS_MPEG4_SECTIONS = 0x13, GF_M2TS_VIDEO_H264 = 0x1B, + GF_M2TS_VIDEO_HEVC = 0x24, GF_M2TS_AUDIO_AC3 = 0x81, GF_M2TS_AUDIO_DTS = 0x8A, @@ -758,6 +759,8 @@ struct tag_m2ts_demux u32 stb_at_last_pcr; u32 nb_pck; Bool loop_demux; + const char *ts_data_chunk; + u32 ts_data_chunk_size; /* "Network" = "MobileIP", "DefaultMCastInterface" */ Bool MobileIPEnabled; diff --git a/include/gpac/setup.h b/include/gpac/setup.h index cbf8d09..59e2d87 100644 --- a/include/gpac/setup.h +++ b/include/gpac/setup.h @@ -425,6 +425,11 @@ void gf_memory_print(void); /*prints the state of current allocations*/ /*safety checks on macros*/ +#ifdef GPAC_DISABLE_ZLIB +# define GPAC_DISABLE_LOADER_BT +# define GPAC_DISABLE_SWF_IMPORT +#endif + #ifdef GPAC_DISABLE_VRML # ifndef GPAC_DISABLE_BIFS # define GPAC_DISABLE_BIFS diff --git a/include/gpac/tools.h b/include/gpac/tools.h index bd99679..fa08ffc 100644 --- a/include/gpac/tools.h +++ b/include/gpac/tools.h @@ -690,7 +690,7 @@ GF_Err gf_cleanup_dir(char* DirPathName); * \param size buffer size * \return computed CRC32 */ -u32 gf_crc_32(char *data, u32 size); +u32 gf_crc_32(const char *data, u32 size); /*!\brief run-time system info object diff --git a/modules/ffmpeg_in/ffmpeg_decode.c b/modules/ffmpeg_in/ffmpeg_decode.c index 592cee2..af5efe4 100644 --- a/modules/ffmpeg_in/ffmpeg_decode.c +++ b/modules/ffmpeg_in/ffmpeg_decode.c @@ -696,88 +696,104 @@ redecode: w = ctx->width; h = ctx->height; - if (ffd->check_h264_isma) { - /*for AVC bitstreams after ISMA decryption, in case (as we do) the decryption DRM tool - doesn't put back nalu size, do it ourselves...*/ - if (inBuffer && !inBuffer[0] && !inBuffer[1] && !inBuffer[2] && (inBuffer[3]==0x01)) { - u32 nalu_size; - char *start, *end, *bufferEnd; - - start = inBuffer; - end = inBuffer + 4; - bufferEnd = inBuffer + inBufferLength; - /* FIXME : SOUCHAY : not sure of exact behaviour, but old one was reading non-allocated memory */ - while ((end+3) < bufferEnd) { - if (!end[0] && !end[1] && !end[2] && (end[3]==0x01)) { - nalu_size = end - start - 4; - start[0] = (nalu_size>>24)&0xFF; - start[1] = (nalu_size>>16)&0xFF; - start[2] = (nalu_size>>8)&0xFF; - start[3] = (nalu_size)&0xFF; - start = end; - end = start+4; - continue; + /*we have a valid frame not yet dispatched*/ + if (ffd->had_pic) { + ffd->had_pic = 0; + gotpic = 1; + } else { + if (ffd->check_h264_isma) { + /*for AVC bitstreams after ISMA decryption, in case (as we do) the decryption DRM tool + doesn't put back nalu size, do it ourselves...*/ + if (inBuffer && !inBuffer[0] && !inBuffer[1] && !inBuffer[2] && (inBuffer[3]==0x01)) { + u32 nalu_size; + char *start, *end, *bufferEnd; + + start = inBuffer; + end = inBuffer + 4; + bufferEnd = inBuffer + inBufferLength; + /* FIXME : SOUCHAY : not sure of exact behaviour, but old one was reading non-allocated memory */ + while ((end+3) < bufferEnd) { + if (!end[0] && !end[1] && !end[2] && (end[3]==0x01)) { + nalu_size = end - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + start = end; + end = start+4; + continue; + } + end++; } - end++; + nalu_size = (inBuffer+inBufferLength) - start - 4; + start[0] = (nalu_size>>24)&0xFF; + start[1] = (nalu_size>>16)&0xFF; + start[2] = (nalu_size>>8)&0xFF; + start[3] = (nalu_size)&0xFF; + ffd->check_h264_isma = 2; + } + /*if we had ISMA E&A and lost it this is likely due to a pck loss - do NOT switch back to regular*/ + else if (ffd->check_h264_isma == 1) { + ffd->check_h264_isma = 0; } - nalu_size = (inBuffer+inBufferLength) - start - 4; - start[0] = (nalu_size>>24)&0xFF; - start[1] = (nalu_size>>16)&0xFF; - start[2] = (nalu_size>>8)&0xFF; - start[3] = (nalu_size)&0xFF; - ffd->check_h264_isma = 2; - } - /*if we had ISMA E&A and lost it this is likely due to a pck loss - do NOT switch back to regular*/ - else if (ffd->check_h264_isma == 1) { - ffd->check_h264_isma = 0; } - } -#ifdef USE_AVCODEC2 - if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { -#else - if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { -#endif - if (!ffd->check_short_header) { - return GF_NON_COMPLIANT_BITSTREAM; - } + #ifdef USE_AVCODEC2 + if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { + #else + if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { + #endif + if (!ffd->check_short_header) { + return GF_NON_COMPLIANT_BITSTREAM; + } - /*switch to H263 (ffmpeg MPEG-4 codec doesn't understand short headers)*/ - { - u32 old_codec = (*codec)->id; - ffd->check_short_header = 0; - /*OK we loose the DSI stored in the codec context, but H263 doesn't need any, and if we're - here this means the DSI was broken, so no big deal*/ - avcodec_close(ctx); - *codec = avcodec_find_decoder(CODEC_ID_H263); - if (! (*codec) || (avcodec_open(ctx, *codec)<0)) return GF_NON_COMPLIANT_BITSTREAM; -#if USE_AVCODEC2 - if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { -#else - if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { -#endif - /*nope, stay in MPEG-4*/ + /*switch to H263 (ffmpeg MPEG-4 codec doesn't understand short headers)*/ + { + u32 old_codec = (*codec)->id; + ffd->check_short_header = 0; + /*OK we loose the DSI stored in the codec context, but H263 doesn't need any, and if we're + here this means the DSI was broken, so no big deal*/ avcodec_close(ctx); - *codec = avcodec_find_decoder(old_codec); - assert(*codec); - avcodec_open(ctx, *codec); - return GF_NON_COMPLIANT_BITSTREAM; + *codec = avcodec_find_decoder(CODEC_ID_H263); + if (! (*codec) || (avcodec_open(ctx, *codec)<0)) return GF_NON_COMPLIANT_BITSTREAM; + #if USE_AVCODEC2 + if (avcodec_decode_video2(ctx, frame, &gotpic, &pkt) < 0) { + #else + if (avcodec_decode_video(ctx, frame, &gotpic, inBuffer, inBufferLength) < 0) { + #endif + /*nope, stay in MPEG-4*/ + avcodec_close(ctx); + *codec = avcodec_find_decoder(old_codec); + assert(*codec); + avcodec_open(ctx, *codec); + return GF_NON_COMPLIANT_BITSTREAM; + } } } - } - if (!gotpic && (!ctx->width || !ctx->height) ) { - ctx->width = w; - ctx->height = h; - return GF_OK; + /* + if (!gotpic && (!ctx->width || !ctx->height) ) { + ctx->width = w; + ctx->height = h; + return GF_OK; + } + */ + /*some streams use odd width/height frame values*/ + if (ffd->out_pix_fmt == GF_PIXEL_YV12) { + if (ctx->width%2) ctx->width++; + if (ctx->height%2) ctx->height++; + } } - /*some streams use odd width/height frame values*/ - if (ffd->out_pix_fmt == GF_PIXEL_YV12) { - if (ctx->width%2) ctx->width++; - if (ctx->height%2) ctx->height++; + /*we have a picture and need resize, do it*/ + if (gotpic && ffd->needs_output_resize) { + ffd->needs_output_resize=0; + ffd->had_pic = 1; + *outBufferLength = ffd->out_size; + return GF_BUFFER_TOO_SMALL; } + /*recompute outsize in case on-the-fly change*/ if ((w != ctx->width) || (h != ctx->height)) { outsize = ctx->width * ctx->height * 3; @@ -787,6 +803,17 @@ redecode: ffd->yuv_size = 3 * ctx->width * ctx->height / 2; } ffd->out_size = outsize; + + if (!ffd->no_par_update && ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { + ffd->previous_par = (ctx->sample_aspect_ratio.num<<16) | ctx->sample_aspect_ratio.den; + } + + /*we didn't get any picture: wait for a picture before resizing output buffer, otherwise we will have no + video in the output buffer*/ + if (!gotpic) { + ffd->needs_output_resize = 1; + return GF_OK; + } *outBufferLength = ffd->out_size; if (ffd->check_h264_isma) { inBuffer[0] = inBuffer[1] = inBuffer[2] = 0; @@ -798,17 +825,21 @@ redecode: *cached_sws = NULL; } #endif - if (!ffd->no_par_update && ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { - ffd->previous_par = (ctx->sample_aspect_ratio.num<<16) | ctx->sample_aspect_ratio.den; - } + ffd->had_pic = 1; return GF_BUFFER_TOO_SMALL; } /*check PAR in case on-the-fly change*/ if (!ffd->no_par_update && ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { u32 new_par = (ctx->sample_aspect_ratio.num<<16) | ctx->sample_aspect_ratio.den; - if (new_par != ffd->previous_par) { + if (ffd->previous_par && (new_par != ffd->previous_par)) { ffd->previous_par = new_par; + + if (!gotpic) { + ffd->needs_output_resize = 1; + return GF_OK; + } *outBufferLength = ffd->out_size; + ffd->had_pic = 1; return GF_BUFFER_TOO_SMALL; } } @@ -955,6 +986,9 @@ static u32 FFDEC_CanHandleStream(GF_BaseDecoder *plug, u32 StreamType, GF_ESD *e GF_AVCConfig *cfg = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); if (!cfg) return GF_CODEC_SUPPORTED; + if (esd->has_ref_base) + is_svc = 1; + /*decode all NALUs*/ count = gf_list_count(cfg->sequenceParameterSets); for (i=0; i -#ifndef FREENECT_RESOLUTION_HIGH -//#define FREENECT_MINIMAL +#if !defined(FREENECT_DEVICE_CAMERA) && defined(FREENECT_FRAME_W) +#define FREENECT_MINIMAL #endif diff --git a/modules/gpac_js/gpac_js.c b/modules/gpac_js/gpac_js.c index 2144838..4004f48 100644 --- a/modules/gpac_js/gpac_js.c +++ b/modules/gpac_js/gpac_js.c @@ -849,21 +849,21 @@ static void gjs_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, JSContext GF_JSAPIParam par; JSPropertySpec gpacEvtClassProps[] = { - {"type", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"keycode", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"mouse_x", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"mouse_y", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"picked", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"wheel", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"button", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("type", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("keycode", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("mouse_x", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("mouse_y", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("picked", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("wheel", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("button", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec gpacEvtClassFuncs[] = { SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec gpacClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec gpacClassFuncs[] = { SMJS_FUNCTION_SPEC("getOption", gpac_getOption, 3), @@ -906,7 +906,6 @@ static void gjs_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, JSContext if (!scene) return; - JS_SETUP_CLASS(gjs->gpacClass, "GPAC", JSCLASS_HAS_PRIVATE, gpac_getProperty, gpac_setProperty, JS_FinalizeStub); if (!gjs->gpac_obj) { diff --git a/modules/isom_in/isom_in.h b/modules/isom_in/isom_in.h index 18945a1..226722c 100644 --- a/modules/isom_in/isom_in.h +++ b/modules/isom_in/isom_in.h @@ -69,12 +69,15 @@ typedef struct typedef struct { - u32 track; + u32 track, track_id; + /*base track if scalable media, 0 otherwise*/ + u32 base_track; + u32 next_track; LPNETCHANNEL channel; ISOMReader *owner; u64 duration; - Bool wait_for_segment_switch, needs_codec_update; + Bool wait_for_segment_switch; /*current sample*/ GF_ISOSample *sample; GF_SLHeader current_slh; diff --git a/modules/isom_in/load.c b/modules/isom_in/load.c index 02405ab..8fe8a9d 100644 --- a/modules/isom_in/load.c +++ b/modules/isom_in/load.c @@ -86,7 +86,7 @@ void isor_declare_objects(ISOMReader *read) GF_ObjectDescriptor *od; GF_ESD *esd; const char *tag; - u32 i, count, ocr_es_id, tlen; + u32 i, count, ocr_es_id, tlen, base_track, j, track_id; ocr_es_id = 0; @@ -106,8 +106,28 @@ void isor_declare_objects(ISOMReader *read) default: continue; } + + /*we declare only the base track (i.e base_track == 0)*/ + gf_isom_get_reference(read->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track); + if (base_track) + continue; esd = gf_media_map_esd(read->mov, i+1); if (esd) { + esd->has_ref_base = 0; + track_id = gf_isom_get_track_id(read->mov, i+1); + for (j = 0; j < count; j++) + { + if (gf_isom_has_track_reference(read->mov, j+1, GF_ISOM_REF_BASE, track_id)) + { + esd->has_ref_base = 1; + break; + } + if (gf_isom_get_avc_svc_type(read->mov, j+1, 1)>=GF_ISOM_AVCTYPE_AVC_SVC) { + esd->has_ref_base = 1; + break; + } + } + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->service_ifce = read->input; od->objectDescriptorID = 0; diff --git a/modules/isom_in/read.c b/modules/isom_in/read.c index 24c688a..b737263 100644 --- a/modules/isom_in/read.c +++ b/modules/isom_in/read.c @@ -56,6 +56,7 @@ static void isor_delete_channel(ISOMReader *reader, ISOMChannel *ch) static GFINLINE Bool isor_is_local(const char *url) { if (!strnicmp(url, "file://", 7)) return 1; + if (!strnicmp(url, "gmem://", 7)) return 1; if (strstr(url, "://")) return 0; /*the rest is local (mounted on FS)*/ return 1; @@ -497,7 +498,7 @@ static GF_Descriptor *ISOR_GetServiceDesc(GF_InputService *plug, u32 expect_type GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) { - u32 ESID; + u32 ESID, base_track, count, i; ISOMChannel *ch; GF_NetworkCommand com; u32 track; @@ -561,6 +562,7 @@ GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const ch ch->channel = channel; gf_list_add(read->channels, ch); ch->track = track; + ch->track_id = gf_isom_get_track_id(read->mov, ch->track); switch (gf_isom_get_media_type(ch->owner->mov, ch->track)) { case GF_ISOM_MEDIA_OCR: ch->streamType = GF_STREAM_OCR; @@ -568,6 +570,27 @@ GF_Err ISOR_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const ch case GF_ISOM_MEDIA_SCENE: ch->streamType = GF_STREAM_SCENE; break; + case GF_ISOM_MEDIA_VISUAL: + count = gf_isom_get_track_count(ch->owner->mov); + base_track = 0; + for (i = 0; i < count; i++) + { + gf_isom_get_reference(ch->owner->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track); + if (base_track) + break; + } + ch->base_track = base_track; + ch->next_track = 0; + + /*set track to last layer (hopefully max quality)*/ + for (i = 0; i < count; i++) { + gf_isom_get_reference(ch->owner->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track); + if (base_track==ch->base_track) + ch->track = i+1; + } + /*in scalable mode add SPS/PPS in-band*/ + gf_isom_set_nalu_extract_mode(ch->owner->mov, ch->track, GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG); + break; } ch->has_edit_list = gf_isom_get_edit_segment_count(ch->owner->mov, ch->track) ? 1 : 0; @@ -698,11 +721,64 @@ static u64 check_round(ISOMChannel *ch, u64 val_ts, Double val_range, Bool make_ return val_ts; } +/*switch channel quality. Return next channel or current channel if error*/ +static +u32 gf_channel_switch_quality(ISOMChannel *ch, GF_ISOFile *the_file, Bool switch_up) +{ + u32 i, count, next_track, trackID, cur_track; + s32 ref_count; + + cur_track = ch->next_track ? ch->next_track : ch->track; + count = gf_isom_get_track_count(the_file); + trackID = gf_isom_get_track_id(the_file, cur_track); + next_track = 0; + + if (switch_up) + { + for (i = 0; i < count; i++) + { + ref_count = gf_isom_get_reference_count(the_file, i+1, GF_ISOM_REF_SCAL); + if (ref_count < 0) + return cur_track; //error + else if (ref_count == 0) + continue; + /*next track is the one that has the last reference of type GF_ISOM_REF_SCAL refers to this current track*/ + else if ((u32)ref_count == gf_isom_has_track_reference(the_file, i+1, GF_ISOM_REF_SCAL, trackID)) + { + next_track = i+1; + break; + } + } + /*this is the highest quality*/ + if (!next_track) + return cur_track; + } + else + { + if (cur_track == ch->base_track) + return cur_track; + ref_count = gf_isom_get_reference_count(the_file, cur_track, GF_ISOM_REF_SCAL); + if (ref_count < 0) + return cur_track; + gf_isom_get_reference(the_file, cur_track, GF_ISOM_REF_SCAL, ref_count, &next_track); + if (!next_track) + return cur_track; + } + + /*in scalable mode add SPS/PPS in-band*/ + gf_isom_set_nalu_extract_mode(the_file, next_track, GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG); + + return next_track; +} + + GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) { Double track_dur, media_dur; ISOMChannel *ch; ISOMReader *read; + u32 count, i; + if (!plug || !plug->priv || !com) return GF_SERVICE_ERROR; read = (ISOMReader *) plug->priv; @@ -735,6 +811,20 @@ GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) } return GF_NOT_SUPPORTED; } + + if (com->command_type == GF_NET_SERVICE_QUALITY_SWITCH) + { + count = gf_list_count(read->channels); + for (i = 0; i < count; i++) + { + ch = (ISOMChannel *)gf_list_get(read->channels, i); + if (ch->base_track) { + ch->next_track = gf_channel_switch_quality(ch, read->mov, com->switch_quality.up); + } + } + return GF_OK; + } + if (!com->base.on_channel) return GF_NOT_SUPPORTED; ch = isor_get_channel(read, com->base.on_channel); @@ -818,9 +908,9 @@ GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) } gf_odf_desc_del((GF_Descriptor *) dcd); } - } return GF_OK; } + } return GF_NOT_SUPPORTED; } diff --git a/modules/isom_in/read_ch.c b/modules/isom_in/read_ch.c index 744a69d..841b3e9 100644 --- a/modules/isom_in/read_ch.c +++ b/modules/isom_in/read_ch.c @@ -98,9 +98,24 @@ static void check_segment_switch(ISOMReader *read) ch->wait_for_segment_switch = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Track %d - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) )); if (param.url_query.next_url_init_or_switch_segment) { - ch->needs_codec_update = 1; + ch->track = gf_isom_get_track_by_id(read->mov, ch->track_id); + if (!ch->track) { + if (gf_isom_get_track_count(read->mov)==1) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n")); + ch->track = 1; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[IsoMedia] Mismatch between track IDs of different representations\n")); + } + } + + /*rewrite all upcoming SPS/PPS into the samples*/ + gf_isom_set_nalu_extract_mode(read->mov, ch->track, GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG); + /*we changed our moov structure, sample_num now starts from 0*/ ch->sample_num = 0; + } else { + /*no need to rewrite upcoming SPS/PPS into the samples*/ + gf_isom_set_nalu_extract_mode(read->mov, ch->track, GF_ISOM_NALU_EXTRACT_DEFAULT); } } } else { @@ -259,6 +274,12 @@ fetch_next: ch->sample_num++; goto fetch_next; } + if (ch->sample && ch->sample->IsRAP && ch->next_track) { + ch->track = ch->next_track; + ch->next_track = 0; + gf_isom_sample_del(&ch->sample); + goto fetch_next; + } } if (!ch->sample) { /*incomplete file - check if we're still downloading or not*/ @@ -325,62 +346,6 @@ fetch_next: ch->current_slh.isma_encrypted = 0; } } - - /*this is ugly we need a rearchitecture of the streaming part of GPAC to handle codec changes properly - fortunately in DASH we cannot switch codec on - the fly (not in the same representation)!! */ - if (ch->sample && ch->needs_codec_update) { - GF_AVCConfig *avccfg, *svccfg; - GF_AVCConfigSlot *slc; - GF_BitStream *bs; - u32 i; - ch->needs_codec_update = 0; - - GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[IsoMedia] Codec configuration changed - rewriting sample\n")); - - switch (gf_isom_get_media_subtype(ch->owner->mov, ch->track, 1)) { - case GF_ISOM_SUBTYPE_AVC_H264: - case GF_ISOM_SUBTYPE_AVC2_H264: - case GF_ISOM_SUBTYPE_SVC_H264: - avccfg = gf_isom_avc_config_get(ch->owner->mov, ch->track, 1); - svccfg = gf_isom_svc_config_get(ch->owner->mov, ch->track, 1); - - bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); - if (avccfg) { - for (i=0; isequenceParameterSets); i++) { - slc = gf_list_get(avccfg->sequenceParameterSets, i); - gf_bs_write_int(bs, slc->size, avccfg->nal_unit_size*8); - gf_bs_write_data(bs, slc->data, slc->size); - } - for (i=0; ipictureParameterSets); i++) { - slc = gf_list_get(avccfg->pictureParameterSets, i); - gf_bs_write_int(bs, slc->size, avccfg->nal_unit_size*8); - gf_bs_write_data(bs, slc->data, slc->size); - } - gf_odf_avc_cfg_del(avccfg); - } - if (svccfg) { - for (i=0; isequenceParameterSets); i++) { - slc = gf_list_get(svccfg->sequenceParameterSets, i); - gf_bs_write_int(bs, slc->size, avccfg->nal_unit_size*8); - gf_bs_write_data(bs, slc->data, slc->size); - } - for (i=0; ipictureParameterSets); i++) { - slc = gf_list_get(svccfg->pictureParameterSets, i); - gf_bs_write_int(bs, slc->size, avccfg->nal_unit_size*8); - gf_bs_write_data(bs, slc->data, slc->size); - } - gf_odf_avc_cfg_del(svccfg); - } - gf_bs_write_data(bs, ch->sample->data, ch->sample->dataLength); - gf_free(ch->sample->data); - ch->sample->data = 0; - gf_bs_get_content(bs, &ch->sample->data, &ch->sample->dataLength); - gf_bs_del(bs); - break; - default: - break; - } - } } void isor_reader_release_sample(ISOMChannel *ch) diff --git a/modules/mp3_in/mp3_in.c b/modules/mp3_in/mp3_in.c index 4269701..5db68c0 100644 --- a/modules/mp3_in/mp3_in.c +++ b/modules/mp3_in/mp3_in.c @@ -131,13 +131,35 @@ static void mp3_setup_object(MP3Reader *read) } } - -static Bool MP3_ConfigureFromFile(MP3Reader *read) +/** + * Returns TRUE if file is ready to be read, FALSE otherwise + * @param read Reader + * @param minSizeToRead How much bytes do we need to start reading at minimum + */ +static Bool MP3_ConfigureFromFile(MP3Reader *read, u32 *minSizeToRead) { + unsigned char id3v2[10]; u32 hdr, size; u64 pos; if (!read->stream) return 0; - + /* ID3VVFFFFSIZE = 13bytes + * ID3 string + * VV = Version + * F = Flags + * SIZE = 32bits size with first Most Significant bit set to 0 -> 28 bits + * Size starts AFTER this header, meaning we have to add 10 bytes + */ + pos = fread(id3v2, sizeof(unsigned char), 10, read->stream); + *minSizeToRead = 0; + if (pos == 10){ + /* Did we read an ID3v2 ? */ + if (id3v2[0] == 'I' && id3v2[1] == 'D' && id3v2[2] == '3'){ + int sz = 10 + ((id3v2[9] & 0x7f) + ((id3v2[8] & 0x7f) << 7) + ((id3v2[7] & 0x7f) << 14) + ((id3v2[6] & 0x7f) << 21)); + //printf("Size of id3v2 header = %d\n", sz); + *minSizeToRead = sz; + } + } + gf_f64_seek(read->stream, 0, SEEK_SET); hdr = gf_mp3_get_next_header(read->stream); if (!hdr) return 0; read->sample_rate = gf_mp3_sampling_rate(hdr); @@ -318,14 +340,15 @@ void MP3_NetIO(void *cbk, GF_NETIO_Parameter *param) read->stream = gf_f64_open((char *) szCache, "rb"); if (!read->stream) e = GF_SERVICE_ERROR; else { + u32 minSizeToRead = 0; /*if full file at once (in cache) parse duration*/ if (e==GF_EOS) read->is_remote = 0; e = GF_OK; /*not enough data*/ - if (!MP3_ConfigureFromFile(read)) { + if (!MP3_ConfigureFromFile(read, &minSizeToRead)) { gf_dm_sess_get_stats(read->dnload, NULL, NULL, NULL, &bytes_done, NULL, NULL); /*bad data - there's likely some ID3 around...*/ - if (bytes_done>100*1024) { + if (bytes_done>(100*1024 + minSizeToRead)) { e = GF_CORRUPTED_DATA; } else { fclose(read->stream); @@ -366,6 +389,7 @@ void mp3_download_file(GF_InputService *plug, char *url) static GF_Err MP3_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) { char szURL[2048]; + u32 minSizeToRead = 0; char *ext; GF_Err reply; MP3Reader *read = plug->priv; @@ -389,7 +413,7 @@ static GF_Err MP3_ConnectService(GF_InputService *plug, GF_ClientService *serv, read->stream = gf_f64_open(szURL, "rb"); if (!read->stream) { reply = GF_URL_ERROR; - } else if (!MP3_ConfigureFromFile(read)) { + } else if (!MP3_ConfigureFromFile(read, &minSizeToRead)) { fclose(read->stream); read->stream = NULL; reply = GF_NOT_SUPPORTED; @@ -532,7 +556,8 @@ static GF_Err MP3_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) read->done = 0; /*PLAY after complete download, estimate duration*/ if (!read->is_remote && !read->duration) { - MP3_ConfigureFromFile(read); + u32 minSizeToRead = 0; + MP3_ConfigureFromFile(read, &minSizeToRead); if (read->duration) { GF_NetworkCommand rcfg; rcfg.base.on_channel = read->ch; diff --git a/modules/mpd_in/mpd_in.c b/modules/mpd_in/mpd_in.c index 7c4bbe9..689092c 100644 --- a/modules/mpd_in/mpd_in.c +++ b/modules/mpd_in/mpd_in.c @@ -44,7 +44,11 @@ typedef struct __mpd_module Bool connection_ack_sent; Bool in_seek; + Bool memory_storage; + Bool use_max_res, immediate_switch; Double previous_start_range; + /*max width & height in all active representations*/ + u32 width, height; } GF_MPD_In; typedef struct @@ -165,12 +169,12 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) gf_dash_request_period_switch(mpdin->dash); break; } - gf_sleep(20); + gf_sleep(30); } } return GF_EOS; } - gf_sleep(16); + gf_sleep(30); } nb_segments_cached = gf_dash_group_get_num_segments_ready(mpdin->dash, group_idx, &group_done); @@ -186,7 +190,7 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) } gf_dash_group_get_next_segment_location(mpdin->dash, group_idx, ¶m->url_query.next_url, ¶m->url_query.start_range, ¶m->url_query.end_range, - ¶m->url_query.next_url_init_or_switch_segment, ¶m->url_query.switch_start_range , ¶m->url_query.switch_end_range, + NULL, ¶m->url_query.next_url_init_or_switch_segment, ¶m->url_query.switch_start_range , ¶m->url_query.switch_end_range, &src_url); { @@ -313,9 +317,11 @@ void mpdin_dash_io_delete_cache_file(GF_DASHFileIO *dashio, GF_DASHFileIOSession GF_DASHFileIOSession mpdin_dash_io_create(GF_DASHFileIO *dashio, Bool persistent, const char *url) { + GF_DownloadSession *sess; u32 flags = GF_NETIO_SESSION_NOT_THREADED; GF_MPD_In *mpdin = (GF_MPD_In *)dashio->udta; - GF_DownloadSession *sess; + if (mpdin->memory_storage) + flags |= GF_NETIO_SESSION_MEMORY_CACHE; if (persistent) flags |= GF_NETIO_SESSION_PERSISTENT; sess = gf_term_download_new(mpdin->service, url, flags, NULL, NULL); @@ -333,9 +339,9 @@ GF_Err mpdin_dash_io_setup_from_url(GF_DASHFileIO *dashio, GF_DASHFileIOSession { return gf_dm_sess_setup_from_url((GF_DownloadSession *)session, url); } -GF_Err mpdin_dash_io_set_range(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, u64 start_range, u64 end_range) +GF_Err mpdin_dash_io_set_range(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, u64 start_range, u64 end_range, Bool discontinue_cache) { - return gf_dm_sess_set_range((GF_DownloadSession *)session, start_range, end_range); + return gf_dm_sess_set_range((GF_DownloadSession *)session, start_range, end_range, discontinue_cache); } GF_Err mpdin_dash_io_init(GF_DASHFileIO *dashio, GF_DASHFileIOSession session) { @@ -404,8 +410,7 @@ GF_Err mpdin_dash_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_ /*select input services if possible*/ for (i=0; idash); i++) { - const char *mime, *init_segment; - + const char *mime, *init_segment; if (!gf_dash_is_group_selected(mpdin->dash, i)) continue; @@ -422,7 +427,14 @@ GF_Err mpdin_dash_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_ GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[MPD_IN] Unable to connect input service to %s\n", init_segment)); gf_dash_group_select(mpdin->dash, i, 0); } else { + u32 w, h; group->service_connected = 1; + w = h = 0; + gf_dash_group_get_video_info(mpdin->dash, i, &w, &h); + if (w && h && w>mpdin->width && h>mpdin->height) { + mpdin->width = w; + mpdin->height = h; + } } } } @@ -521,6 +533,19 @@ GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const c else if (opt && !strcmp(opt, "minQuality")) first_select_mode = GF_DASH_SELECT_QUALITY_LOWEST; else if (opt && !strcmp(opt, "maxQuality")) first_select_mode = GF_DASH_SELECT_QUALITY_HIGHEST; else first_select_mode = GF_DASH_SELECT_BANDWIDTH_LOWEST; + + opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "MemoryStorage"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "MemoryStorage", "no"); + mpdin->memory_storage = (opt && !strcmp(opt, "yes")) ? 1 : 0; + + opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "UseMaxResolution"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "UseMaxResolution", "yes"); + mpdin->use_max_res = (!opt || !strcmp(opt, "yes")) ? 1 : 0; + + opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "ImmediateSwitching"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "ImmediateSwitching", "no"); + mpdin->immediate_switch = (opt && !strcmp(opt, "yes")) ? 1 : 0; + mpdin->in_seek = 0; mpdin->previous_start_range = -1; @@ -535,7 +560,7 @@ GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const c /*dash thread starts at the end of gf_dash_open */ e = gf_dash_open(mpdin->dash, url); - if (!mpdin->dash) { + if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[MPD_IN] Error - cannot initialize DASH Client for %s: %s\n", url, gf_error_to_string(e) )); gf_term_on_connect(mpdin->service, NULL, e); return GF_OK; @@ -549,14 +574,17 @@ static GF_Descriptor *MPD_GetServiceDesc(GF_InputService *plug, u32 expect_type, GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv; GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MPD_IN] Received Service Description request from terminal for %s\n", sub_url)); for (i=0; idash); i++) { + GF_Descriptor *desc; GF_MPDGroup *mudta; if (!gf_dash_is_group_selected(mpdin->dash, i)) continue; mudta = gf_dash_get_group_udta(mpdin->dash, i); if (!mudta) continue; if (mudta->service_descriptor_fetched) continue; - mudta->service_descriptor_fetched = 1; - return mudta->segment_ifce->GetServiceDescriptor(mudta->segment_ifce, expect_type, sub_url); + + desc = mudta->segment_ifce->GetServiceDescriptor(mudta->segment_ifce, expect_type, sub_url); + if (desc) mudta->service_descriptor_fetched = 1; + return desc; } return NULL; } @@ -612,8 +640,13 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) } return GF_NOT_SUPPORTED; + case GF_NET_SERVICE_HAS_FORCED_VIDEO_SIZE: + com->par.width = mpdin->use_max_res ? mpdin->width : 0; + com->par.height = mpdin->use_max_res ? mpdin->height : 0; + return GF_OK; + case GF_NET_SERVICE_QUALITY_SWITCH: - gf_dash_switch_quality(mpdin->dash, com->switch_quality.up); + gf_dash_switch_quality(mpdin->dash, com->switch_quality.up, mpdin->immediate_switch); return GF_OK; } /*not supported*/ diff --git a/modules/mpegts_in/mpegts_in.c b/modules/mpegts_in/mpegts_in.c index a3e8421..bbf866e 100644 --- a/modules/mpegts_in/mpegts_in.c +++ b/modules/mpegts_in/mpegts_in.c @@ -181,6 +181,10 @@ static GF_ObjectDescriptor *MP2TS_GetOD(M2TSIn *m2ts, GF_M2TS_PES *stream, char esd->decoderConfig->streamType = GF_STREAM_VISUAL; esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_AVC; break; + case GF_M2TS_VIDEO_HEVC: + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_HEVC; + break; case GF_M2TS_AUDIO_MPEG1: esd->decoderConfig->streamType = GF_STREAM_AUDIO; esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_MPEG1; @@ -894,6 +898,9 @@ static GF_Descriptor *M2TS_GetServiceDesc(GF_InputService *plug, u32 expect_type return desc; } } + /*if we expect scene, return NULL and repost a connection ack when we get the PMT*/ + if (expect_type==GF_MEDIA_OBJECT_SCENE) + return NULL; if (m2ts->epg_requested) { GF_ObjectDescriptor *od = M2TS_GenerateEPG_OD(m2ts); m2ts->epg_requested = 0; diff --git a/modules/opensvc_dec/opensvc_dec.c b/modules/opensvc_dec/opensvc_dec.c index 0690d0e..191c4b6 100644 --- a/modules/opensvc_dec/opensvc_dec.c +++ b/modules/opensvc_dec/opensvc_dec.c @@ -182,12 +182,15 @@ static GF_Err OSVC_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capa OSVCDec *ctx = (OSVCDec*) ifcg->privateStack; switch (capability.CapCode) { case GF_CODEC_MEDIA_SWITCH_QUALITY: + if (capability.cap.valueInt) { - // set layer up (command=1) - UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 1 ); + if (ctx->CurrDqId < ctx->MaxDqId) + // set layer up (command=1) + UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 1 ); } else { - // set layer down (command=0) - UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 0 ); + if (ctx->CurrDqId > 0) + // set layer down (command=0) + UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 0 ); } return GF_OK; } @@ -207,6 +210,7 @@ static GF_Err OSVC_ProcessData(GF_MediaDecoder *ifcg, OPENSVCFRAME pic; int Layer[4]; OSVCDec *ctx = (OSVCDec*) ifcg->privateStack; + u32 curMaxDqId = ctx->MaxDqId; if (!ES_ID || (ES_ID!=ctx->ES_ID) || !ctx->codec) { *outBufferLength = 0; @@ -217,25 +221,37 @@ static GF_Err OSVC_ProcessData(GF_MediaDecoder *ifcg, return GF_BUFFER_TOO_SMALL; } + ctx->MaxDqId = GetDqIdMax(inBuffer, inBufferLength, ctx->nalu_size_length, ctx->DqIdTable, ctx->nalu_size_length ? 1 : 0); + if(!ctx->InitParseAU){ + if (ctx->MaxDqId == -1) { + //AVC stream in a h264 file + ctx->MaxDqId = 0; + } else { + //Firts time only, we parse the first AU to know the file configuration + //does not need to ba called again ever after, unless SPS or PPS changed + ParseAuPlayers(ctx->codec, inBuffer, inBufferLength, ctx->nalu_size_length, ctx->nalu_size_length ? 1 : 0); + } + ctx->InitParseAU = 1; + } +/* + if (curMaxDqId != ctx->MaxDqId) + { + if (ctx->MaxDqId == -1) + ctx->MaxDqId = 0; + else + ParseAuPlayers(ctx->codec, inBuffer, inBufferLength, ctx->nalu_size_length, ctx->nalu_size_length ? 1 : 0); + ctx->CurrDqId = ctx->MaxDqId; + } +// SetCommandLayer(Layer, ctx->MaxDqId, ctx->CurrDqId, &ctx->TemporalCom, ctx->TemporalId); +*/ + ctx->TemporalCom = 0; + SetCommandLayer(Layer, ctx->MaxDqId, ctx->MaxDqId, &ctx->TemporalCom, 0); + got_pic = 0; if (ctx->nalu_size_length) { u32 i, nalu_size = 0; u8 *ptr = inBuffer; - ctx->MaxDqId = GetDqIdMax(inBuffer, inBufferLength, ctx->nalu_size_length, ctx->DqIdTable, 1); - if(!ctx->InitParseAU){ - if (ctx->MaxDqId == -1) { - //AVC stream in a h264 file - ctx->MaxDqId = 0; - } else { - //Firts time only, we parse the first AU to know the file configuration - //does not need to ba called again ever after, unless SPS or PPS changed - ParseAuPlayers(ctx->codec, inBuffer, inBufferLength, ctx->nalu_size_length, 1); - } - ctx->InitParseAU = 1; - } - SetCommandLayer(Layer, ctx->MaxDqId, ctx->CurrDqId, &ctx->TemporalCom, ctx->TemporalId); - while (inBufferLength) { for (i=0; inalu_size_length; i++) { nalu_size = (nalu_size<<8) + ptr[i]; @@ -256,7 +272,7 @@ static GF_Err OSVC_ProcessData(GF_MediaDecoder *ifcg, } if (got_pic!=1) return GF_OK; - if ((pic.Width != ctx->width) || (pic.Height!=ctx->height)) { + if ((curMaxDqId != ctx->MaxDqId) || (pic.Width != ctx->width) || (pic.Height!=ctx->height)) { ctx->width = pic.Width; ctx->stride = pic.Width + 32; ctx->height = pic.Height; @@ -289,6 +305,9 @@ static u32 OSVC_CanHandleStream(GF_BaseDecoder *dec, u32 StreamType, GF_ESD *esd GF_AVCConfig *cfg = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); if (!cfg) return GF_CODEC_NOT_SUPPORTED; + if (esd->has_ref_base) + is_svc = 1; + /*decode all NALUs*/ count = gf_list_count(cfg->sequenceParameterSets); for (i=0; im_device->GetFriendlyName()) ); + VPASSIGN( STRING_TO_JSVAL( JS_NewStringCopyZ(c, dev->m_device->GetFriendlyName()) ) ); } else if (!strcmp(prop_name, "UUID")) { - *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, dev->m_device->GetUUID()) ); + VPASSIGN( STRING_TO_JSVAL( JS_NewStringCopyZ(c, dev->m_device->GetUUID()) ) ); } else if (!strcmp(prop_name, "PresentationURL")) { - *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, dev->m_device->m_PresentationURL) ); + VPASSIGN( STRING_TO_JSVAL( JS_NewStringCopyZ(c, dev->m_device->m_PresentationURL) ) ); } else if (!strcmp(prop_name, "ServicesCount")) { u32 count = gf_list_count(dev->m_Services); @@ -555,7 +555,7 @@ static SMJS_DECL_FUNC_PROP_GET( upnpdevice_getProperty) dev->RefreshServiceList(); count = gf_list_count(dev->m_Services); } - *vp = INT_TO_JSVAL(count); + VPASSIGN( INT_TO_JSVAL(count) ); } SMJS_FREE(c, prop_name); return JS_TRUE; @@ -663,22 +663,22 @@ static SMJS_DECL_FUNC_PROP_GET( upnp_getProperty) if (!prop_name) return JS_FALSE; if (!strcmp(prop_name, "MediaRendererEnabled")) { - *vp = BOOLEAN_TO_JSVAL( upnp->m_pMediaRenderer ? JS_TRUE : JS_FALSE ); + VPASSIGN( BOOLEAN_TO_JSVAL( upnp->m_pMediaRenderer ? JS_TRUE : JS_FALSE ) ); } else if (!strcmp(prop_name, "MediaServerEnabled")) { - *vp = BOOLEAN_TO_JSVAL( upnp->m_pMediaServer ? JS_TRUE : JS_FALSE); + VPASSIGN( BOOLEAN_TO_JSVAL( upnp->m_pMediaServer ? JS_TRUE : JS_FALSE) ); } else if (!strcmp(prop_name, "MediaControlEnabled")) { - *vp = BOOLEAN_TO_JSVAL( upnp->m_pAVCtrlPoint ? JS_TRUE : JS_FALSE); + VPASSIGN( BOOLEAN_TO_JSVAL( upnp->m_pAVCtrlPoint ? JS_TRUE : JS_FALSE) ); } else if (!strcmp(prop_name, "MediaServersCount")) { - *vp = INT_TO_JSVAL( upnp->m_pAVCtrlPoint ? gf_list_count(upnp->m_pAVCtrlPoint->m_MediaServers) : 0); + VPASSIGN( INT_TO_JSVAL( upnp->m_pAVCtrlPoint ? gf_list_count(upnp->m_pAVCtrlPoint->m_MediaServers) : 0) ); } else if (!strcmp(prop_name, "MediaRenderersCount")) { - *vp = INT_TO_JSVAL( upnp->m_pAVCtrlPoint ? gf_list_count(upnp->m_pAVCtrlPoint->m_MediaRenderers) : 0); + VPASSIGN( INT_TO_JSVAL( upnp->m_pAVCtrlPoint ? gf_list_count(upnp->m_pAVCtrlPoint->m_MediaRenderers) : 0) ); } else if (!strcmp(prop_name, "DevicesCount")) { - *vp = INT_TO_JSVAL( upnp->m_pGenericController ? gf_list_count(upnp->m_pGenericController->m_Devices) : 0); + VPASSIGN( INT_TO_JSVAL( upnp->m_pGenericController ? gf_list_count(upnp->m_pGenericController->m_Devices) : 0) ); } SMJS_FREE(c, prop_name); return JS_TRUE; @@ -699,18 +699,18 @@ static SMJS_DECL_FUNC_PROP_SET(upnp_setProperty) if (!prop_name) return JS_FALSE; if (upnp->m_pMediaRenderer ) { - if (!strcmp(prop_name, "MovieDuration") && JSVAL_IS_DOUBLE(*vp)) { + if (!strcmp(prop_name, "MovieDuration") && JSVAL_IS_DOUBLE( VPGET() )) { jsdouble d; - JS_ValueToNumber(c, *vp, &d); + JS_ValueToNumber(c, VPGET(), &d); upnp->m_pMediaRenderer->SetDuration(d, 1); } - else if (!strcmp(prop_name, "MovieTime") && JSVAL_IS_DOUBLE(*vp)) { + else if (!strcmp(prop_name, "MovieTime") && JSVAL_IS_DOUBLE( VPGET() )) { jsdouble d; - JS_ValueToNumber(c, *vp, &d); + JS_ValueToNumber(c, VPGET(), &d); upnp->m_pMediaRenderer->SetTime(d); } - else if (!strcmp(prop_name, "MovieURL") && JSVAL_IS_STRING(*vp) ) { - char *url = SMJS_CHARS(c, *vp); + else if (!strcmp(prop_name, "MovieURL") && JSVAL_IS_STRING( VPGET() ) ) { + char *url = SMJS_CHARS(c, VPGET() ); if (url) upnp->m_pMediaRenderer->SetConnected(url); SMJS_FREE(c, url); } @@ -1454,7 +1454,7 @@ Bool GF_UPnP::LoadJS(GF_TermExtJS *param) { u32 i, count; JSPropertySpec upnpClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec upnpClassFuncs[] = { SMJS_FUNCTION_SPEC("BindRenderer", upnp_bind_renderer, 0), @@ -1522,7 +1522,7 @@ Bool GF_UPnP::LoadJS(GF_TermExtJS *param) /*setup JS bindings*/ JSPropertySpec upnpDeviceClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec upnpDeviceClassFuncs[] = { SMJS_FUNCTION_SPEC("FindService", upnp_device_find_service, 0), diff --git a/modules/platinum/GPACPlatinum.h b/modules/platinum/GPACPlatinum.h index 74890ff..1264017 100644 --- a/modules/platinum/GPACPlatinum.h +++ b/modules/platinum/GPACPlatinum.h @@ -141,6 +141,15 @@ public: #ifdef GPAC_HAS_SPIDERMONKEY SMJS_DECL_FUNC_PROP_GET( upnpservice_getProperty); + +#ifdef USE_FFDEV_17 + #define VPASSIGN(__b) __vp.set( __b ) + #define VPGET() (jsval) __vp +#else + #define VPASSIGN(__b) *vp = __b + #define VPGET() *vp +#endif + #endif void format_time_string(char *str, Double dur); diff --git a/modules/platinum/GenericDevice.cpp b/modules/platinum/GenericDevice.cpp index 33e077d..ee11459 100644 --- a/modules/platinum/GenericDevice.cpp +++ b/modules/platinum/GenericDevice.cpp @@ -261,10 +261,10 @@ SMJS_DECL_FUNC_PROP_GET( upnpservice_getProperty) if (!prop_name) return JS_FALSE; if (!strcmp(prop_name, "Device")) { - *vp = OBJECT_TO_JSVAL(service->m_device->obj); + VPASSIGN( OBJECT_TO_JSVAL(service->m_device->obj) ); } else if (!strcmp(prop_name, "ModifiedStateVariablesCount")) { - *vp = INT_TO_JSVAL(service->vars ? service->vars->GetItemCount() : 0); + VPASSIGN( INT_TO_JSVAL(service->vars ? service->vars->GetItemCount() : 0) ); } SMJS_FREE(c, prop_name); diff --git a/modules/widgetman/widget.c b/modules/widgetman/widget.c index dc62a38..502dc22 100644 --- a/modules/widgetman/widget.c +++ b/modules/widgetman/widget.c @@ -386,6 +386,7 @@ SMJS_FUNC_PROP_SET( widget_setProperty) /*avoids GCC warning*/ if (!obj) obj = NULL; if (!id) id=0; + if (!vp) vp=0; return JS_TRUE; } @@ -419,7 +420,7 @@ void widget_load(GF_WidgetManager *wm, GF_SceneGraph *scene, JSContext *c, JSObj } else { JSPropertySpec widgetClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec widgetClassFuncs[] = { /*W3C*/ diff --git a/modules/widgetman/widgetman.c b/modules/widgetman/widgetman.c index 9e25943..7f602b6 100644 --- a/modules/widgetman/widgetman.c +++ b/modules/widgetman/widgetman.c @@ -3497,7 +3497,7 @@ static void widgetmanager_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, GF_JSAPIParam par; JSPropertySpec wmClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec wmClassFuncs[] = { SMJS_FUNCTION_SPEC("initialize", wm_initialize, 0), @@ -3545,7 +3545,7 @@ static void widgetmanager_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, { JSPropertySpec wmWidgetClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec wmWidgetClassFuncs[] = { SMJS_FUNCTION_SPEC("activate", wm_widget_activate, 1), diff --git a/src/Makefile b/src/Makefile index 318b782..733ce98 100644 --- a/src/Makefile +++ b/src/Makefile @@ -37,10 +37,13 @@ LIBGPAC_BIFS=bifs/arith_decoder.o bifs/bifs_codec.o bifs/bifs_node_tables.o bifs endif ## libgpac objects gathering: src/isomedia -LIBGPAC_ISOM=isomedia/avc_ext.o isomedia/box_code_3gpp.o isomedia/box_code_adobe.o isomedia/box_code_apple.o isomedia/box_code_base.o isomedia/box_code_drm.o isomedia/box_code_meta.o isomedia/box_dump.o isomedia/box_funcs.o isomedia/data_map.o isomedia/drm_sample.o isomedia/isom_intern.o isomedia/isom_read.o isomedia/isom_store.o isomedia/isom_write.o isomedia/media.o isomedia/media_odf.o isomedia/meta.o isomedia/movie_fragments.o isomedia/sample_descs.o isomedia/stbl_read.o isomedia/stbl_write.o isomedia/track.o isomedia/tx3g.o isomedia/generic_subtitle.o +LIBGPAC_ISOM=isomedia/avc_ext.o isomedia/box_code_3gpp.o isomedia/box_code_apple.o isomedia/box_code_base.o isomedia/box_code_drm.o isomedia/box_code_meta.o isomedia/box_dump.o isomedia/box_funcs.o isomedia/data_map.o isomedia/drm_sample.o isomedia/isom_intern.o isomedia/isom_read.o isomedia/isom_store.o isomedia/isom_write.o isomedia/media.o isomedia/media_odf.o isomedia/meta.o isomedia/movie_fragments.o isomedia/sample_descs.o isomedia/stbl_read.o isomedia/stbl_write.o isomedia/track.o isomedia/tx3g.o isomedia/generic_subtitle.o ifeq ($(DISABLE_ISOFF_HINT), no) LIBGPAC_ISOM+=isomedia/hint_track.o isomedia/hinting.o endif +ifeq ($(DISABLE_ISOM_ADOBE), no) +LIBGPAC_ISOM+=isomedia/box_code_adobe.o +endif ## libgpac objects gathering: src/odf LIBGPAC_ODF=odf/desc_private.o odf/descriptors.o odf/odf_code.o odf/odf_codec.o odf/odf_command.o odf/qos.o odf/slc.o diff --git a/src/compositor/audio_mixer.c b/src/compositor/audio_mixer.c index 42010d5..fa471a9 100644 --- a/src/compositor/audio_mixer.c +++ b/src/compositor/audio_mixer.c @@ -52,6 +52,8 @@ typedef struct Fixed speed; Fixed pan[6]; + + Bool muted; } MixerInput; struct __audiomix @@ -578,6 +580,7 @@ u32 gf_mixer_get_output(GF_AudioMixer *am, void *buffer, u32 buffer_size, u32 de return 0; } + single_source->muted = single_source->src->IsMuted(single_source->src->callback); /*this happens if input SR cannot be mapped to output audio hardware*/ if (single_source->src->samplerate != am->sample_rate) goto do_mix; /*note we don't check output cfg: if the number of channel is the same then the channel cfg is the @@ -590,7 +593,7 @@ single_source_mix: ptr = (char *)buffer; in_size = buffer_size; - is_muted = single_source->src->IsMuted(single_source->src->callback); + is_muted = single_source->muted; while (buffer_size) { data = single_source->src->FetchFrame(single_source->src->callback, &size, delay); @@ -631,7 +634,8 @@ do_mix: single_source = NULL; for (i=0; isources, i); - if (in->src->IsMuted(in->src->callback)) continue; + in->muted = in->src->IsMuted(in->src->callback); + if (in->muted) continue; if (in->buffer_size < nb_samples) { for (j=0; jsrc->GetConfig(in->src, 0)) { - nb_act_src = 0; - am->must_reconfig = 1; - /*if main mixer reconfig asap*/ - if (am->ar) gf_mixer_reconfig(am); - break; + if (!am->must_reconfig) { + am->must_reconfig = 1; + /*if main mixer reconfig asap*/ + if (am->ar) gf_mixer_reconfig(am); + } + //however keep the current mixer config and output some audio ... +// nb_act_src = 0; +// break; + in->muted = 1; + continue; } else if (in->speed==0) { in->out_samples_to_write = 0; } else { @@ -690,6 +699,10 @@ do_mix: /*fill*/ for (i=0; isources, i); + if (in->muted) { + in->out_samples_to_write = 0; + continue; + } if (in->out_samples_to_write>in->out_samples_written) { gf_mixer_fetch_input(am, in, delay + 8000 * i / am->bits_per_sample / am->sample_rate / am->nb_channels); if (in->out_samples_to_write>in->out_samples_written) nb_to_fill++; @@ -698,6 +711,7 @@ do_mix: /*release - this is done in 2 steps in case 2 audio object use the same source...*/ for (i=0; isources, i); + if (in->muted) continue; if (in->in_bytes_used) in->src->ReleaseFrame(in->src->callback, in->in_bytes_used-1); in->in_bytes_used = 0; } diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 04891c8..a327371 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -788,7 +788,7 @@ static void gf_sc_reset(GF_Compositor *compositor) compositor->focus_node = NULL; compositor->focus_text_type = 0; compositor->frame_number = 0; - compositor->video_memory = 0; + compositor->video_memory = compositor->was_system_memory ? 0 : 1; compositor->rotation = 0; gf_list_reset(compositor->focus_ancestors); diff --git a/src/compositor/compositor_2d.c b/src/compositor/compositor_2d.c index 9b27324..4e6de5d 100644 --- a/src/compositor/compositor_2d.c +++ b/src/compositor/compositor_2d.c @@ -783,6 +783,8 @@ Bool compositor_2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_st GF_Err compositor_2d_set_aspect_ratio(GF_Compositor *compositor) { + u32 old_vp_width, old_vp_height; + Bool changed = 0; Double ratio; GF_Event evt; GF_Err e; @@ -793,6 +795,9 @@ GF_Err compositor_2d_set_aspect_ratio(GF_Compositor *compositor) compositor->vp_x = compositor->vp_y = 0; scaleX = scaleY = FIX_ONE; + old_vp_width = compositor->vp_width; + old_vp_height = compositor->vp_height; + /*force complete clean*/ compositor->traverse_state->invalidate_all = 1; @@ -892,21 +897,32 @@ GF_Err compositor_2d_set_aspect_ratio(GF_Compositor *compositor) } #endif + if (compositor->was_system_memory != evt.setup.system_memory) changed = 1; + else if (old_vp_width != compositor->vp_width) changed=1; + else if (old_vp_height != compositor->vp_height) changed=1; + else if (compositor->was_opengl != evt.setup.opengl_mode) changed=1; + - GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor2D] Reconfiguring display size %d x %d - opengl %s - use %s memory\n", evt.setup.width, evt.setup.height, - (evt.setup.opengl_mode==2) ? "Offscreen" : (evt.setup.opengl_mode==1) ? "yes" : "no", evt.setup.system_memory ? "systems" : "video" - )); + if (changed) { + GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor2D] Reconfiguring display size %d x %d - opengl %s - use %s memory\n", evt.setup.width, evt.setup.height, + (evt.setup.opengl_mode==2) ? "Offscreen" : (evt.setup.opengl_mode==1) ? "yes" : "no", evt.setup.system_memory ? "systems" : "video" + )); - e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); - if (e) return e; + e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); + if (e) return e; - if (compositor->has_size_info) { - compositor->traverse_state->vp_size.x = INT2FIX(compositor->scene_width); - compositor->traverse_state->vp_size.y = INT2FIX(compositor->scene_height); - } else { - compositor->traverse_state->vp_size.x = INT2FIX(compositor->output_width); - compositor->traverse_state->vp_size.y = INT2FIX(compositor->output_height); + if (compositor->has_size_info) { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->scene_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->scene_height); + } else { + compositor->traverse_state->vp_size.x = INT2FIX(compositor->output_width); + compositor->traverse_state->vp_size.y = INT2FIX(compositor->output_height); + } + compositor->was_opengl = evt.setup.opengl_mode; + compositor->was_system_memory = evt.setup.system_memory; + } + /*set scale factor*/ compositor_set_ar_scale(compositor, scaleX, scaleY); return GF_OK; diff --git a/src/compositor/texturing.c b/src/compositor/texturing.c index 9c90f23..28599fa 100644 --- a/src/compositor/texturing.c +++ b/src/compositor/texturing.c @@ -177,14 +177,12 @@ void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) /*check init flag*/ if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { - /*if we had a texture this means the object has changed - delete texture and force next frame - composition (this will take care of OD reuse)*/ + /*if we had a texture this means the object has changed - delete texture and resetup. Do not skip + texture update as this may lead to an empty rendering pass (blank frame for this object), especially in DASH*/ if (txh->tx_io) { gf_sc_texture_release(txh); txh->data = NULL; txh->needs_refresh = 1; - gf_sc_invalidate(txh->compositor, NULL); - return; } if (gf_mo_is_private_media(txh->stream)) { setup_texture_object(txh, 1); @@ -195,6 +193,7 @@ void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) /*if no frame or muted don't draw*/ if (!txh->data || !size) { + GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("No output frame available\n")); /*TODO - check if this is needed */ if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) { //txh->needs_refresh = 1; diff --git a/src/export.cpp b/src/export.cpp index 1027968..e8b9e55 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -84,8 +84,10 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_prompt_get_char) ) #pragma comment (linker, EXPORT_SYMBOL(gf_prompt_set_echo_off) ) #pragma comment (linker, EXPORT_SYMBOL(gf_crc_32) ) +#ifndef GPAC_DISABLE_ZLIB #pragma comment (linker, EXPORT_SYMBOL(gf_gz_compress_payload) ) #pragma comment (linker, EXPORT_SYMBOL(gf_gz_decompress_payload) ) +#endif /* Memory */ #ifdef GPAC_MEMORY_TRACKING @@ -635,6 +637,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_decoder_config) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_reference_count) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_reference) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_reference_ID) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_has_track_reference) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_filename) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_brand_info) ) @@ -667,6 +670,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_avc_config_get) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_svc_config_get) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_3gp_config_get) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_hevc_config_get) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_meta_item_count) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_meta_item_by_id) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_meta_item_info) ) @@ -694,7 +698,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_rvc_config) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_sample_rap_roll_info) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_reset_fragment_info) ) - +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_track_original_id) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_nalu_extract_mode) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_nalu_extract_mode) ) # ifndef GPAC_DISABLE_ISOM_DUMP #pragma comment (linker, EXPORT_SYMBOL(gf_isom_dump) ) @@ -723,6 +729,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_track_creation_time) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_track_enabled) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_track_id) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_rewrite_track_dependencies) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_add_sample) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_add_sample_shadow) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_append_sample_data) ) @@ -1088,6 +1095,7 @@ #ifndef GPAC_DISABLE_AV_PARSERS #pragma comment (linker, EXPORT_SYMBOL(gf_avc_get_sps_info) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_avc_get_pps_info) ) #pragma comment (linker, EXPORT_SYMBOL(gf_avc_get_profile_name) ) #endif /*GPAC_DISABLE_AV_PARSERS*/ @@ -1731,6 +1739,16 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_segment_switch_forced) ) #pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_current_segment_start_time) ) #pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_get_presentation_time_offset) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_get_video_info) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_get_representation_info) ) + +#pragma comment (linker, EXPORT_SYMBOL(gf_media_hevc_read_vps) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_media_hevc_read_sps) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_media_hevc_read_pps) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_odf_hevc_cfg_read) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_odf_hevc_cfg_del) ) + +#pragma comment (linker, EXPORT_SYMBOL(gf_media_nalu_next_start_code) ) #endif diff --git a/src/ietf/rtp_streamer.c b/src/ietf/rtp_streamer.c index 4d638a0..4324bb4 100644 --- a/src/ietf/rtp_streamer.c +++ b/src/ietf/rtp_streamer.c @@ -342,6 +342,8 @@ GF_RTPStreamer *gf_rtp_streamer_new_extended(u32 streamType, u32 oti, u32 timeSc break; case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: case GF_ISOM_SUBTYPE_SVC_H264: { required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ diff --git a/src/isomedia/avc_ext.c b/src/isomedia/avc_ext.c index e88e74d..970b83d 100644 --- a/src/isomedia/avc_ext.c +++ b/src/isomedia/avc_ext.c @@ -32,6 +32,369 @@ #ifndef GPAC_DISABLE_ISOM + +Bool gf_isom_is_nalu_based_entry(GF_MediaBox *mdia, GF_SampleEntryBox *_entry) +{ + GF_MPEGVisualSampleEntryBox *entry; + if (mdia->handler->handlerType != GF_ISOM_MEDIA_VISUAL) return 0; + entry = (GF_MPEGVisualSampleEntryBox*)_entry; + if (!entry) return 0; + if (entry->avc_config || entry->svc_config || entry->hevc_config) return 1; + return 0; +} + + +static void rewrite_nalus_list(GF_List *nalus, GF_BitStream *bs, Bool rewrite_start_codes, u32 nal_unit_size_field) +{ + u32 i, count = gf_list_count(nalus); + for (i=0; isize, 8*nal_unit_size_field); + gf_bs_write_data(bs, sl->data, sl->size); + } +} + +static void merge_nalus_list(GF_List *src, GF_List *dst) +{ + u32 i, count = gf_list_count(src); + for (i=0; iavc_config) { + merge_nalus_list(entry->avc_config->config->sequenceParameterSets, sps); + merge_nalus_list(entry->avc_config->config->sequenceParameterSetExtensions, sps); + merge_nalus_list(entry->avc_config->config->pictureParameterSets, pps); + } + if (entry->svc_config) { + merge_nalus_list(entry->svc_config->config->sequenceParameterSets, sps); + merge_nalus_list(entry->svc_config->config->pictureParameterSets, pps); + } +} + + +/* Rewrite mode: + * mode = 0: playback + * mode = 1: streaming + */ +GF_Err gf_isom_nalu_sample_rewrite(GF_MediaBox *mdia, GF_ISOSample *sample, u32 sampleNumber, GF_MPEGVisualSampleEntryBox *entry) +{ + Bool is_hevc = 0; + GF_Err e = GF_OK; + GF_ISOSample *ref_samp; + GF_BitStream *src_bs, *ref_bs, *dst_bs; + u64 offset; + u32 ref_nalu_size, data_offset, data_length, copy_size, nal_size, max_size, di, nal_unit_size_field, cur_extract_mode, extractor_mode; + Bool rewrite_ps, rewrite_start_codes; + u8 ref_track_ID, ref_track_num; + s8 sample_offset, nal_type; + u32 nal_hdr; + char *buffer; + GF_ISOFile *file = mdia->mediaTrack->moov->mov; + + src_bs = ref_bs = dst_bs = NULL; + ref_samp = NULL; + buffer = NULL; + rewrite_ps = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG) ? 1 : 0; + if (! sample->IsRAP) rewrite_ps = 0; + rewrite_start_codes = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG) ? 1 : 0; + extractor_mode = mdia->mediaTrack->extractor_mode&0x0000FFFF; + + if (extractor_mode == GF_ISOM_NALU_EXTRACT_INSPECT) { + if (!rewrite_ps && !rewrite_start_codes) + return GF_OK; + } + + if (!entry) return GF_BAD_PARAM; + nal_unit_size_field = 0; + /*if svc rewrire*/ + if (entry->svc_config && entry->svc_config->config) nal_unit_size_field = entry->svc_config->config->nal_unit_size; + /*if mvc rewrire*/ + + /*otherwise do nothing*/ + else if (!rewrite_ps && !rewrite_start_codes) { + return GF_OK; + } + + if (!nal_unit_size_field) { + if (entry->avc_config) nal_unit_size_field = entry->avc_config->config->nal_unit_size; + else if (entry->hevc_config) { + nal_unit_size_field = entry->hevc_config->config->nal_unit_size; + is_hevc = 1; + } + } + if (!nal_unit_size_field) return GF_ISOM_INVALID_FILE; + + dst_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + src_bs = gf_bs_new(sample->data, sample->dataLength, GF_BITSTREAM_READ); + max_size = 4096; + + /*rewrite start code with NALU delim*/ + if (rewrite_start_codes) { + gf_bs_write_int(dst_bs, 1, 32); + if (is_hevc) { + gf_bs_write_int(dst_bs, 0, 1); + gf_bs_write_int(dst_bs, GF_HEVC_NALU_ACCESS_UNIT, 6); + gf_bs_write_int(dst_bs, 0, 9); + /*pic-type - by default we signal all slice types possible*/ + gf_bs_write_int(dst_bs, 2, 3); + gf_bs_write_int(dst_bs, 0, 5); + } else { + gf_bs_write_int(dst_bs, (sample->data[0] & 0x60) | GF_AVC_NALU_ACCESS_UNIT, 8); + gf_bs_write_int(dst_bs, 0xF0 , 8); /*7 "all supported NALUs" (=111) + rbsp trailing (10000)*/; + } + } + + if (rewrite_ps) { + if (is_hevc) { + u32 i, count; + count = gf_list_count(entry->hevc_config->config->param_array); + for (i=0; ihevc_config->config->param_array, i); + rewrite_nalus_list(ar->nalus, dst_bs, rewrite_start_codes, nal_unit_size_field); + } + + /*little optimization if we are not asked to start codes: copy over the sample*/ + if (!rewrite_start_codes) { + gf_bs_write_data(dst_bs, sample->data, sample->dataLength); + gf_free(sample->data); + sample->data = NULL; + gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); + gf_bs_del(src_bs); + gf_bs_del(dst_bs); + return GF_OK; + } + } else { + + /*this is an SVC track: get all SPS/PPS from this track down to the base layer and rewrite them*/ + if (mdia->mediaTrack->has_base_layer) { + u32 j; + GF_List *nalu_sps = gf_list_new(); + GF_List *nalu_pps = gf_list_new(); + GF_TrackReferenceTypeBox *dpnd = NULL; + Track_FindRef(mdia->mediaTrack, GF_ISOM_REF_SCAL, &dpnd); + +#if 0 + /*get all upper layers with SCAL reference to this track*/ + for (j = 0; j < gf_isom_get_track_count(file); j++) { + if (gf_isom_has_track_reference(file, j+1, GF_ISOM_REF_SCAL, mdia->mediaTrack->Header->trackID)) { + u32 tkID; + GF_TrackBox *base_track; + GF_MPEGVisualSampleEntryBox *base_entry; + gf_isom_get_reference_ID(file, j+1, GF_ISOM_REF_SCAL, 1, &tkID); + + base_track = GetTrackbyID(mdia->mediaTrack->moov, tkID); + base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; + if (base_entry) + merge_nalus(base_entry, nalu_sps, nalu_pps); + } + } + +#endif + + merge_nalus(entry, nalu_sps, nalu_pps); + if (dpnd) { + for (j=0; jtrackIDCount; j++) { + GF_TrackBox *base_track = GetTrackbyID(mdia->mediaTrack->moov, dpnd->trackIDs[j]); + GF_MPEGVisualSampleEntryBox *base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; + if (base_entry) + merge_nalus(base_entry, nalu_sps, nalu_pps); + } + } + + //rewrite nalus + rewrite_nalus_list(nalu_sps, dst_bs, rewrite_start_codes, nal_unit_size_field); + rewrite_nalus_list(nalu_pps, dst_bs, rewrite_start_codes, nal_unit_size_field); + + gf_list_del(nalu_sps); + gf_list_del(nalu_pps); + } else { + + if (entry->avc_config) { + rewrite_nalus_list(entry->avc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); + rewrite_nalus_list(entry->avc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); + rewrite_nalus_list(entry->avc_config->config->sequenceParameterSetExtensions, dst_bs, rewrite_start_codes, nal_unit_size_field); + } + + /*add svc config */ + if (entry->svc_config) { + rewrite_nalus_list(entry->svc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); + rewrite_nalus_list(entry->svc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); + } + + /*little optimization if we are not asked to rewrite extractors or start codes: copy over the sample*/ + if (!entry->svc_config && !rewrite_start_codes) { + gf_bs_write_data(dst_bs, sample->data, sample->dataLength); + gf_free(sample->data); + sample->data = NULL; + gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); + gf_bs_del(src_bs); + gf_bs_del(dst_bs); + return GF_OK; + } + + } + } + } + + buffer = (char *)gf_malloc(sizeof(char)*max_size); + + while (gf_bs_available(src_bs)) + { + nal_size = gf_bs_read_int(src_bs, 8*nal_unit_size_field); + if (nal_size>max_size) { + buffer = (char*) gf_realloc(buffer, sizeof(char)*nal_size); + max_size = nal_size; + } + if (is_hevc) { + nal_hdr = gf_bs_read_u16(src_bs); + nal_type = (nal_hdr&0x7E00) >> 9; + } else { + nal_hdr = gf_bs_read_u8(src_bs); + nal_type = nal_hdr & 0x1F; + } + + if (is_hevc) { + /*we already wrote this stuff*/ + if (nal_type==GF_HEVC_NALU_ACCESS_UNIT) + continue; + + /*rewrite nal*/ + gf_bs_read_data(src_bs, buffer, nal_size-2); + if (rewrite_start_codes) + gf_bs_write_u32(dst_bs, 1); + else + gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); + + gf_bs_write_u16(dst_bs, nal_hdr); + gf_bs_write_data(dst_bs, buffer, nal_size-2); + + continue; + } + + /*we already wrote this stuff*/ + if (nal_type==GF_AVC_NALU_ACCESS_UNIT) + continue; + + //extractor + if (nal_type == 31) { + switch (extractor_mode) { + case 0: + gf_bs_read_int(src_bs, 24); //3 bytes of NALUHeader in extractor + ref_track_ID = gf_bs_read_u8(src_bs); + sample_offset = (s8) gf_bs_read_int(src_bs, 8); + data_offset = gf_bs_read_u32(src_bs); + data_length = gf_bs_read_u32(src_bs); + + ref_track_num = gf_isom_get_track_by_id(file, ref_track_ID); + if (!ref_track_num) { + e = GF_BAD_PARAM; + goto exit; + } + cur_extract_mode = gf_isom_get_nalu_extract_mode(file, ref_track_num); + gf_isom_set_nalu_extract_mode(file, ref_track_num, GF_ISOM_NALU_EXTRACT_INSPECT); + ref_samp = gf_isom_get_sample(file, ref_track_num, sampleNumber+sample_offset, &di); + if (!ref_samp) { + e = GF_IO_ERR; + goto exit; + } + ref_bs = gf_bs_new(ref_samp->data, ref_samp->dataLength, GF_BITSTREAM_READ); + offset = 0; + while (gf_bs_available(ref_bs)) { + if (gf_bs_get_position(ref_bs) < data_offset) { + ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); + offset += ref_nalu_size + nal_unit_size_field; + if ((offset > data_offset) || (offset >= gf_bs_get_size(ref_bs))) { + e = GF_BAD_PARAM; + goto exit; + } + + e = gf_bs_seek(ref_bs, offset); + if (e) + goto exit; + continue; + } + ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); + copy_size = data_length ? data_length : ref_nalu_size; + assert(copy_size <= ref_nalu_size); + nal_hdr = gf_bs_read_u8(ref_bs); //rewrite NAL type + if ((copy_size-1)>max_size) { + buffer = (char*)gf_realloc(buffer, sizeof(char)*(copy_size-1)); + max_size = copy_size-1; + } + gf_bs_read_data(ref_bs, buffer, copy_size-1); + + if (rewrite_start_codes) + gf_bs_write_u32(dst_bs, 1); + else + gf_bs_write_int(dst_bs, copy_size, 8*nal_unit_size_field); + + gf_bs_write_u8(dst_bs, nal_hdr); + gf_bs_write_data(dst_bs, buffer, copy_size-1); + } + + gf_isom_sample_del(&ref_samp); + ref_samp = NULL; + gf_bs_del(ref_bs); + ref_bs = NULL; + gf_isom_set_nalu_extract_mode(file, ref_track_num, cur_extract_mode); + break; + default: + //skip to end of this NALU + gf_bs_skip_bytes(src_bs, nal_size-1); + continue; + } + } else { + gf_bs_read_data(src_bs, buffer, nal_size-1); + if (rewrite_start_codes) + gf_bs_write_u32(dst_bs, 1); + else + gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); + + gf_bs_write_u8(dst_bs, nal_hdr); + gf_bs_write_data(dst_bs, buffer, nal_size-1); + } + } + /*done*/ + gf_free(sample->data); + sample->data = NULL; + gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); + +exit: + if (ref_samp) gf_isom_sample_del(&ref_samp); + if (src_bs) gf_bs_del(src_bs); + if (ref_bs) gf_bs_del(ref_bs); + if (dst_bs) gf_bs_del(dst_bs); + if (buffer) gf_free(buffer); + return e; +} + +GF_HEVCConfig *HEVC_DuplicateConfig(GF_HEVCConfig *cfg) +{ + char *data; + u32 data_size; + GF_HEVCConfig *new_cfg; + GF_BitStream *bs; + + if (!cfg) return NULL; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_odf_hevc_cfg_write_bs(cfg, bs); + + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + + new_cfg = gf_odf_hevc_cfg_read_bs(bs); + gf_bs_del(bs); + gf_free(data); + return new_cfg; +} + static GF_AVCConfig *AVC_DuplicateConfig(GF_AVCConfig *cfg) { u32 i, count; @@ -128,14 +491,41 @@ void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc) gf_odf_avc_cfg_write(avcc, &avc->emul_esd->decoderConfig->decoderSpecificInfo->data, &avc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); gf_odf_avc_cfg_del(avcc); } - } else { + } else if (avc->svc_config) { svcc = AVC_DuplicateConfig(avc->svc_config->config); gf_odf_avc_cfg_write(svcc, &avc->emul_esd->decoderConfig->decoderSpecificInfo->data, &avc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); gf_odf_avc_cfg_del(svcc); } } -GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) +void HEVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *hevc) +{ + if (hevc->emul_esd) gf_odf_desc_del((GF_Descriptor *)hevc->emul_esd); + hevc->emul_esd = gf_odf_desc_esd_new(2); + hevc->emul_esd->decoderConfig->streamType = GF_STREAM_VISUAL; + hevc->emul_esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_HEVC; + if (hevc->bitrate) { + hevc->emul_esd->decoderConfig->bufferSizeDB = hevc->bitrate->bufferSizeDB; + hevc->emul_esd->decoderConfig->avgBitrate = hevc->bitrate->avgBitrate; + hevc->emul_esd->decoderConfig->maxBitrate = hevc->bitrate->maxBitrate; + } + if (hevc->descr) { + u32 i=0; + GF_Descriptor *desc,*clone; + i=0; + while ((desc = (GF_Descriptor *)gf_list_enum(hevc->descr->descriptors, &i))) { + clone = NULL; + gf_odf_desc_copy(desc, &clone); + if (gf_odf_desc_add_desc((GF_Descriptor *)hevc->emul_esd, clone) != GF_OK) + gf_odf_desc_del(clone); + } + } + if (hevc->hevc_config && hevc->hevc_config->config) { + gf_odf_hevc_cfg_write(hevc->hevc_config->config, &hevc->emul_esd->decoderConfig->decoderSpecificInfo->data, &hevc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); + } +} + +GF_Err AVC_HEVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) { if (!avc->bitrate) avc->bitrate = (GF_MPEG4BitRateBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BTRT); if (avc->descr) gf_isom_box_del((GF_Box *) avc->descr); @@ -178,14 +568,27 @@ GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) /*update GF_AVCConfig*/ if (!avc->svc_config) { - if (!avc->avc_config) avc->avc_config = (GF_AVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); - if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { - if (avc->avc_config->config) gf_odf_avc_cfg_del(avc->avc_config->config); - avc->avc_config->config = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_HEVC) { + if (!avc->hevc_config) avc->hevc_config = (GF_HEVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + if (avc->hevc_config->config) gf_odf_hevc_cfg_del(avc->hevc_config->config); + avc->hevc_config->config = gf_odf_hevc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + } + } else { + if (!avc->avc_config) avc->avc_config = (GF_AVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + if (avc->avc_config->config) gf_odf_avc_cfg_del(avc->avc_config->config); + avc->avc_config->config = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + } } - gf_odf_desc_del((GF_Descriptor *)esd); + + } + gf_odf_desc_del((GF_Descriptor *)esd); + if (avc->hevc_config) { + HEVC_RewriteESDescriptor(avc); + } else { + AVC_RewriteESDescriptor(avc); } - AVC_RewriteESDescriptor(avc); return GF_OK; } @@ -234,12 +637,14 @@ static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); - if (!trak || !trak->Media || !cfg || !DescriptionIndex) return GF_BAD_PARAM; + if (!trak || !trak->Media || !DescriptionIndex) return GF_BAD_PARAM; entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return GF_BAD_PARAM; switch (entry->type) { case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: break; default: @@ -249,6 +654,7 @@ static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber switch (op_type) { /*AVCC replacement*/ case 0: + if (!cfg) return GF_BAD_PARAM; if (!entry->avc_config) entry->avc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); if (entry->avc_config->config) gf_odf_avc_cfg_del(entry->avc_config->config); entry->avc_config->config = AVC_DuplicateConfig(cfg); @@ -256,6 +662,7 @@ static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber break; /*SVCC replacement*/ case 1: + if (!cfg) return GF_BAD_PARAM; if (!entry->svc_config) entry->svc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC); if (entry->svc_config->config) gf_odf_avc_cfg_del(entry->svc_config->config); entry->svc_config->config = AVC_DuplicateConfig(cfg); @@ -263,6 +670,7 @@ static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber break; /*SVCC replacement and AVC removal*/ case 2: + if (!cfg) return GF_BAD_PARAM; if (entry->avc_config) { gf_isom_box_del((GF_Box*)entry->avc_config); entry->avc_config = NULL; @@ -272,11 +680,45 @@ static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber entry->svc_config->config = AVC_DuplicateConfig(cfg); entry->type = GF_ISOM_BOX_TYPE_SVC1; break; + /*AVCC removal and switch to avc3*/ + case 3: + if (!entry->avc_config || !entry->avc_config->config) + return GF_BAD_PARAM; + + if (entry->svc_config) { + gf_isom_box_del((GF_Box*)entry->svc_config); + entry->svc_config = NULL; + } + + while (gf_list_count(entry->avc_config->config->sequenceParameterSets)) { + GF_AVCConfigSlot *sl = gf_list_get(entry->avc_config->config->sequenceParameterSets, 0); + gf_list_rem(entry->avc_config->config->sequenceParameterSets, 0); + if (sl->data) gf_free(sl->data); + gf_free(sl); + } + + while (gf_list_count(entry->avc_config->config->pictureParameterSets)) { + GF_AVCConfigSlot *sl = gf_list_get(entry->avc_config->config->pictureParameterSets, 0); + gf_list_rem(entry->avc_config->config->pictureParameterSets, 0); + if (sl->data) gf_free(sl->data); + gf_free(sl); + } + + if (entry->type == GF_ISOM_BOX_TYPE_AVC1) + entry->type = GF_ISOM_BOX_TYPE_AVC3; + else if (entry->type == GF_ISOM_BOX_TYPE_AVC2) + entry->type = GF_ISOM_BOX_TYPE_AVC4; + break; } AVC_RewriteESDescriptor(entry); return GF_OK; } +GF_Err gf_isom_avc_set_inband_config(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + return gf_isom_avc_config_update_ex(the_file, trackNumber, DescriptionIndex, NULL, 3); +} + GF_EXPORT GF_Err gf_isom_avc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg) { @@ -303,6 +745,8 @@ GF_Err gf_isom_svc_config_del(GF_ISOFile *the_file, u32 trackNumber, u32 Descrip switch (entry->type) { case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: break; default: @@ -334,7 +778,11 @@ GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) switch (entry->type) { case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: break; default: return GF_OK; @@ -380,6 +828,79 @@ GF_Err gf_isom_svc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfi return e; } +GF_Err gf_isom_hevc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_HEVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + //create a new entry + entry = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1); + if (!entry) return GF_OUT_OF_MEM; + entry->hevc_config = (GF_HEVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC); + entry->hevc_config->config = HEVC_DuplicateConfig(cfg); + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->other_boxes, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->other_boxes); + HEVC_RewriteESDescriptor(entry); + return e; +} + +GF_Err gf_isom_hevc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_HEVCConfig *cfg) +{ + u32 i, array_incomplete; + GF_TrackBox *trak; + GF_Err e; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg || !DescriptionIndex) return GF_BAD_PARAM; + entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: + break; + default: + return GF_BAD_PARAM; + } + + if (!entry->hevc_config) entry->hevc_config = (GF_HEVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC); + if (entry->hevc_config->config) gf_odf_hevc_cfg_del(entry->hevc_config->config); + entry->hevc_config->config = HEVC_DuplicateConfig(cfg); + + array_incomplete = 0; + for (i=0; ihevc_config->config->param_array); i++) { + GF_HEVCParamArray *ar = gf_list_get(entry->hevc_config->config->param_array, i); + if (!ar->array_completeness) { + array_incomplete = 1; + break; + } + } + entry->type = array_incomplete ? GF_ISOM_BOX_TYPE_HEV1 : GF_ISOM_BOX_TYPE_HVC1; + + HEVC_RewriteESDescriptor(entry); + return GF_OK; +} + #endif /*GPAC_DISABLE_ISOM_WRITE*/ GF_EXPORT @@ -389,13 +910,37 @@ GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 GF_MPEGVisualSampleEntryBox *entry; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !DescriptionIndex) return NULL; + if (gf_isom_get_avc_svc_type(the_file, trackNumber, DescriptionIndex)==GF_ISOM_AVCTYPE_NONE) + return NULL; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return NULL; - //if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return NULL; + if (!entry->avc_config) return NULL; return AVC_DuplicateConfig(entry->avc_config->config); } +GF_EXPORT +GF_HEVCConfig *gf_isom_hevc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_MPEGVisualSampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !DescriptionIndex) return NULL; + + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); + if (!entry) return NULL; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: + break; + default: + return NULL; + } + if (!entry->hevc_config) return NULL; + return HEVC_DuplicateConfig(entry->hevc_config->config); +} + GF_EXPORT GF_AVCConfig *gf_isom_svc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) { @@ -403,12 +948,15 @@ GF_AVCConfig *gf_isom_svc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 GF_MPEGVisualSampleEntryBox *entry; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !DescriptionIndex) return NULL; + if (gf_isom_get_avc_svc_type(the_file, trackNumber, DescriptionIndex)==GF_ISOM_AVCTYPE_NONE) + return NULL; entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return NULL; if (!entry->svc_config) return NULL; return AVC_DuplicateConfig(entry->svc_config->config); } + GF_EXPORT u32 gf_isom_get_avc_svc_type(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) { @@ -416,11 +964,14 @@ u32 gf_isom_get_avc_svc_type(GF_ISOFile *the_file, u32 trackNumber, u32 Descript GF_MPEGVisualSampleEntryBox *entry; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !DescriptionIndex) return GF_ISOM_AVCTYPE_NONE; + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_VISUAL) return GF_ISOM_AVCTYPE_NONE; entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->other_boxes, DescriptionIndex-1); if (!entry) return GF_ISOM_AVCTYPE_NONE; switch (entry->type) { case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: break; default: @@ -604,7 +1155,7 @@ GF_Err avcc_Read(GF_Box *s, GF_BitStream *bs) AVCState avc; s32 idx, vui_flag_pos; GF_AVCConfigSlot *sl = gf_list_get(ptr->config->sequenceParameterSets, 0); - idx = AVC_ReadSeqInfo(sl->data+1, sl->size-1, &avc, 0, &vui_flag_pos); + idx = gf_media_avc_read_sps(sl->data+1, sl->size-1, &avc, 0, &vui_flag_pos); if (idx>=0) { ptr->config->chroma_format = avc.sps[idx].chroma_format; ptr->config->luma_bit_depth = 8 + avc.sps[idx].luma_bit_depth_m8; @@ -755,4 +1306,75 @@ GF_Err avcc_Size(GF_Box *s) #endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void hvcc_del(GF_Box *s) +{ + GF_HEVCConfigurationBox *ptr = (GF_HEVCConfigurationBox*)s; + if (ptr->config) gf_odf_hevc_cfg_del(ptr->config); + gf_free(ptr); +} + +GF_Err hvcc_Read(GF_Box *s, GF_BitStream *bs) +{ + u64 pos; + GF_HEVCConfigurationBox *ptr = (GF_HEVCConfigurationBox *)s; + + if (ptr->config) gf_odf_hevc_cfg_del(ptr->config); + + pos = gf_bs_get_position(bs); + ptr->config = gf_odf_hevc_cfg_read_bs(bs); + pos = gf_bs_get_position(bs) - pos ; + if (pos < ptr->size) + ptr->size -= (u32) pos; + + return GF_OK; +} +GF_Box *hvcc_New() +{ + GF_HEVCConfigurationBox *tmp = (GF_HEVCConfigurationBox *) gf_malloc(sizeof(GF_HEVCConfigurationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HEVCConfigurationBox)); + tmp->type = GF_ISOM_BOX_TYPE_HVCC; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err hvcc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HEVCConfigurationBox *ptr = (GF_HEVCConfigurationBox *) s; + if (!s) return GF_BAD_PARAM; + if (!ptr->config) return GF_OK; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + return gf_odf_hevc_cfg_write_bs(ptr->config, bs); +} +GF_Err hvcc_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count, j, subcount; + GF_HEVCConfigurationBox *ptr = (GF_HEVCConfigurationBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + if (!ptr->config) { + ptr->size = 0; + return e; + } + ptr->size += 16; + + count = gf_list_count(ptr->config->param_array); + for (i=0; iconfig->param_array, i); + ptr->size += 3; + subcount = gf_list_count(ar->nalus); + for (j=0; jsize += 2 + ((GF_AVCConfigSlot *)gf_list_get(ar->nalus, j))->size; + } + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + #endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/isomedia/box_code_adobe.c b/src/isomedia/box_code_adobe.c index 32069d8..37b9820 100644 --- a/src/isomedia/box_code_adobe.c +++ b/src/isomedia/box_code_adobe.c @@ -25,10 +25,10 @@ * */ -#ifndef GPAC_DISABLE_ISOM_ADOBE - #include +#ifndef GPAC_DISABLE_ISOM_ADOBE + #ifndef GPAC_DISABLE_ISOM #ifndef GPAC_DISABLE_ISOM_FRAGMENTS diff --git a/src/isomedia/box_code_base.c b/src/isomedia/box_code_base.c index f0eec99..98e049c 100644 --- a/src/isomedia/box_code_base.c +++ b/src/isomedia/box_code_base.c @@ -3504,6 +3504,7 @@ void mp4v_del(GF_Box *s) if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); if (ptr->avc_config) gf_isom_box_del((GF_Box *) ptr->avc_config); if (ptr->svc_config) gf_isom_box_del((GF_Box *) ptr->svc_config); + if (ptr->hevc_config) gf_isom_box_del((GF_Box *) ptr->hevc_config); if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); if (ptr->descr) gf_isom_box_del((GF_Box *) ptr->descr); if (ptr->ipod_ext) gf_isom_box_del((GF_Box *)ptr->ipod_ext); @@ -3531,6 +3532,10 @@ GF_Err mp4v_AddBox(GF_Box *s, GF_Box *a) if (ptr->avc_config) return GF_ISOM_INVALID_FILE; ptr->avc_config = (GF_AVCConfigurationBox *)a; break; + case GF_ISOM_BOX_TYPE_HVCC: + if (ptr->hevc_config) return GF_ISOM_INVALID_FILE; + ptr->hevc_config = (GF_HEVCConfigurationBox *)a; + break; case GF_ISOM_BOX_TYPE_SVCC: if (ptr->svc_config) return GF_ISOM_INVALID_FILE; ptr->svc_config = (GF_AVCConfigurationBox *)a; @@ -3571,6 +3576,8 @@ GF_Err mp4v_Read(GF_Box *s, GF_BitStream *bs) if (e) return e; /*this is an AVC sample desc*/ if (mp4v->avc_config || mp4v->svc_config) AVC_RewriteESDescriptor(mp4v); + /*this is an HEVC sample desc*/ + if (mp4v->hevc_config ) HEVC_RewriteESDescriptor(mp4v); return GF_OK; } @@ -3599,11 +3606,32 @@ GF_Box *avc2_New() return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC2); } +GF_Box *avc3_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC3); +} + +GF_Box *avc4_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC4); +} + GF_Box *svc1_New() { return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_SVC1); } +GF_Box *hvc1_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_HVC1); +} + +GF_Box *hev1_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_HEV1); +} + + GF_Box *encv_New() { return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_ENCV); @@ -3636,6 +3664,10 @@ GF_Err mp4v_Write(GF_Box *s, GF_BitStream *bs) e = gf_isom_box_write((GF_Box *) ptr->avc_config, bs); if (e) return e; } + if (ptr->hevc_config && ptr->hevc_config->config) { + e = gf_isom_box_write((GF_Box *) ptr->hevc_config, bs); + if (e) return e; + } if (ptr->ipod_ext) { e = gf_isom_box_write((GF_Box *) ptr->ipod_ext, bs); if (e) return e; @@ -3674,19 +3706,28 @@ GF_Err mp4v_Size(GF_Box *s) if (e) return e; ptr->size += ptr->esd->size; } else { - if (!ptr->avc_config && !ptr->svc_config) + if (!ptr->avc_config && !ptr->svc_config && !ptr->hevc_config) { return GF_ISOM_INVALID_FILE; + } + if (ptr->hevc_config && ptr->hevc_config->config) { + e = gf_isom_box_size((GF_Box *)ptr->hevc_config); + if (e) return e; + ptr->size += ptr->hevc_config->size; + } + if (ptr->avc_config && ptr->avc_config->config) { e = gf_isom_box_size((GF_Box *) ptr->avc_config); if (e) return e; ptr->size += ptr->avc_config->size; } + if (ptr->svc_config && ptr->svc_config->config) { e = gf_isom_box_size((GF_Box *) ptr->svc_config); if (e) return e; ptr->size += ptr->svc_config->size; } + if (ptr->ipod_ext) { e = gf_isom_box_size((GF_Box *) ptr->ipod_ext); if (e) return e; @@ -4902,7 +4943,11 @@ GF_Err stsd_AddBox(GF_SampleDescriptionBox *ptr, GF_Box *a) case GF_ISOM_BOX_TYPE_RTP_STSD: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: case GF_ISOM_BOX_TYPE_TX3G: case GF_ISOM_BOX_TYPE_TEXT: case GF_ISOM_BOX_TYPE_ENCT: @@ -6104,10 +6149,14 @@ static void gf_isom_check_sample_desc(GF_TrackBox *trak) case GF_ISOM_SUBTYPE_3GP_H263: case GF_ISOM_BOX_TYPE_GHNT: case GF_ISOM_BOX_TYPE_RTP_STSD: - case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_METX: case GF_ISOM_BOX_TYPE_METT: + case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_TX3G: case GF_ISOM_BOX_TYPE_TEXT: @@ -6473,6 +6522,11 @@ GF_Err trex_Read(GF_Box *s, GF_BitStream *bs) ptr->def_sample_duration = gf_bs_read_u32(bs); ptr->def_sample_size = gf_bs_read_u32(bs); ptr->def_sample_flags = gf_bs_read_u32(bs); + + if (!ptr->def_sample_desc_index) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] TREX with default sample description set to 0, likely broken ! Fixing to 1\n" )); + ptr->def_sample_desc_index = 1; + } return GF_OK; } @@ -8183,7 +8237,7 @@ static void sgpd_del_entry(u32 grouping_type, void *entry) { GF_DefaultSampleGroupDescriptionEntry *ptr = (GF_DefaultSampleGroupDescriptionEntry *)entry; if (ptr->data) gf_free(ptr->data); - gf_free(ptr->data); + gf_free(ptr); } } diff --git a/src/isomedia/box_dump.c b/src/isomedia/box_dump.c index 9d72e92..d6560a0 100644 --- a/src/isomedia/box_dump.c +++ b/src/isomedia/box_dump.c @@ -305,13 +305,19 @@ GF_Err gf_box_dump(void *ptr, FILE * trace) case GF_ISOM_BOX_TYPE_AVCC: case GF_ISOM_BOX_TYPE_SVCC: return avcc_dump(a, trace); + case GF_ISOM_BOX_TYPE_HVCC: + return hvcc_dump(a, trace); case GF_ISOM_BOX_TYPE_BTRT: return btrt_dump(a, trace); case GF_ISOM_BOX_TYPE_M4DS: return m4ds_dump(a, trace); case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: return mp4v_dump(a, trace); case GF_ISOM_BOX_TYPE_PASP: return pasp_dump(a, trace); @@ -962,6 +968,7 @@ GF_Err mp4v_dump(GF_Box *a, FILE * trace) if (p->esd) { gf_box_dump(p->esd, trace); } else { + if (p->hevc_config) gf_box_dump(p->hevc_config, trace); if (p->avc_config) gf_box_dump(p->avc_config, trace); if (p->ipod_ext) gf_box_dump(p->ipod_ext, trace); if (p->descr) gf_box_dump(p->descr, trace); @@ -1680,6 +1687,43 @@ GF_Err avcc_dump(GF_Box *a, FILE * trace) return GF_OK; } +GF_Err hvcc_dump(GF_Box *a, FILE * trace) +{ + u32 i, count; + GF_HEVCConfigurationBox *p = (GF_HEVCConfigurationBox *) a; + + fprintf(trace, "\n"); + + fprintf(trace, "config->nal_unit_size, p->config->configurationVersion, p->config->profile_space, p->config->profile_idc, p->config->constraint_indicator_flags); + fprintf(trace, "chroma_format=\"%d\" luma_bit_depth=\"%d\" chroma_bit_depth=\"%d\" avgFrameRate=\"%d\" constantFrameRate=\"%d\" numTemporalLayers=\"%d\"", + p->config->chromaFormat, p->config->luma_bit_depth, p->config->chroma_bit_depth, p->config->avgFrameRate, p->config->constantFrameRate, p->config->numTemporalLayers); + + fprintf(trace, ">\n"); + + count = gf_list_count(p->config->param_array); + for (i=0; iconfig->param_array, i); + fprintf(trace, "\n", ar->type, ar->array_completeness); + nalucount = gf_list_count(ar->nalus); + for (j=0; jnalus, j); + fprintf(trace, "size); + DumpData(trace, c->data, c->size); + fprintf(trace, "\"/>\n"); + } + fprintf(trace, "\n"); + } + + fprintf(trace, "\n"); + + DumpBox(a, trace); + gf_box_dump_done(NULL, a, trace); + fprintf(trace, "\n"); + return GF_OK; +} + GF_Err m4ds_dump(GF_Box *a, FILE * trace) { u32 i; @@ -2299,6 +2343,19 @@ GF_Err mehd_dump(GF_Box *a, FILE * trace) return GF_OK; } +void sample_flags_dump(const char *name, u32 sample_flags, FILE * trace) +{ + fprintf(trace, "<%s", name); + fprintf(trace, " IsLeading=\"%d\"", GF_ISOM_GET_FRAG_LEAD(sample_flags) ); + fprintf(trace, " SampleDependsOn=\"%d\"", GF_ISOM_GET_FRAG_DEPENDS(sample_flags) ); + fprintf(trace, " SampleIsDependedOn=\"%d\"", GF_ISOM_GET_FRAG_DEPENDED(sample_flags) ); + fprintf(trace, " SampleHasRedundancy=\"%d\"", GF_ISOM_GET_FRAG_REDUNDANT(sample_flags) ); + fprintf(trace, " SamplePadding=\"%d\"", GF_ISOM_GET_FRAG_PAD(sample_flags) ); + fprintf(trace, " SampleSync=\"%d\"", GF_ISOM_GET_FRAG_SYNC(sample_flags)); + fprintf(trace, " SampleDegradationPriority=\"%d\"", GF_ISOM_GET_FRAG_DEG(sample_flags)); + fprintf(trace, "/>\n"); +} + GF_Err trex_dump(GF_Box *a, FILE * trace) { GF_TrackExtendsBox *p; @@ -2307,10 +2364,8 @@ GF_Err trex_dump(GF_Box *a, FILE * trace) fprintf(trace, "trackID); fprintf(trace, " SampleDescriptionIndex=\"%d\" SampleDuration=\"%d\" SampleSize=\"%d\"", p->def_sample_desc_index, p->def_sample_duration, p->def_sample_size); - fprintf(trace, " SamplePadding=\"%d\" SampleSync=\"%d\" SampleDegradationPriority=\"%d\"", - GF_ISOM_GET_FRAG_PAD(p->def_sample_flags), GF_ISOM_GET_FRAG_SYNC(p->def_sample_flags), GF_ISOM_GET_FRAG_DEG(p->def_sample_flags)); - fprintf(trace, ">\n"); + sample_flags_dump("DefaultSampleFlags", p->def_sample_flags, trace); DumpBox(a, trace); gf_full_box_dump(a, trace); gf_box_dump_done("TrackExtendsBox", a, trace); @@ -2402,10 +2457,11 @@ GF_Err trun_dump(GF_Box *a, FILE * trace) if (p->flags & GF_ISOM_TRUN_DATA_OFFSET) fprintf(trace, " DataOffset=\"%d\"", p->data_offset); + fprintf(trace, ">\n"); + if (p->flags & GF_ISOM_TRUN_FIRST_FLAG) { - fprintf(trace, " FirstSamplePadding=\"%d\" FirstSampleSync=\"%d\" FirstSampleDegradationPriority=\"%d\"", GF_ISOM_GET_FRAG_PAD(p->first_sample_flags), GF_ISOM_GET_FRAG_SYNC(p->first_sample_flags), GF_ISOM_GET_FRAG_DEG(p->first_sample_flags)); + sample_flags_dump("FirstSampleFlags", p->first_sample_flags, trace); } - fprintf(trace, ">\n"); DumpBox(a, trace); gf_full_box_dump(a, trace); diff --git a/src/isomedia/box_funcs.c b/src/isomedia/box_funcs.c index ac7c774..1daaf79 100644 --- a/src/isomedia/box_funcs.c +++ b/src/isomedia/box_funcs.c @@ -501,12 +501,18 @@ GF_Box *gf_isom_box_new(u32 boxType) a = avcc_New(); if (a) a->type = boxType; return a; + case GF_ISOM_BOX_TYPE_HVCC: + return hvcc_New(); case GF_ISOM_BOX_TYPE_BTRT: return btrt_New(); case GF_ISOM_BOX_TYPE_M4DS: return m4ds_New(); case GF_ISOM_BOX_TYPE_AVC1: return avc1_New(); case GF_ISOM_BOX_TYPE_AVC2: return avc2_New(); + case GF_ISOM_BOX_TYPE_AVC3: return avc3_New(); + case GF_ISOM_BOX_TYPE_AVC4: return avc4_New(); case GF_ISOM_BOX_TYPE_SVC1: return svc1_New(); + case GF_ISOM_BOX_TYPE_HVC1: return hvc1_New(); + case GF_ISOM_BOX_TYPE_HEV1: return hev1_New(); /*3GPP streaming text*/ case GF_ISOM_BOX_TYPE_FTAB: return ftab_New(); @@ -795,8 +801,13 @@ void gf_isom_box_del(GF_Box *a) case GF_ISOM_BOX_TYPE_M4DS: m4ds_del(a); return; case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: mp4v_del(a); return; + case GF_ISOM_BOX_TYPE_HVCC: hvcc_del(a); return; /*3GPP streaming text*/ case GF_ISOM_BOX_TYPE_FTAB: ftab_del(a); return; @@ -1066,8 +1077,14 @@ GF_Err gf_isom_box_read(GF_Box *a, GF_BitStream *bs) case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Read(a, bs); case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: return mp4v_Read(a, bs); + case GF_ISOM_BOX_TYPE_HVCC: + return hvcc_Read(a, bs); /*3GPP streaming text*/ case GF_ISOM_BOX_TYPE_FTAB: return ftab_Read(a, bs); @@ -1319,11 +1336,17 @@ GF_Err gf_isom_box_write_listing(GF_Box *a, GF_BitStream *bs) case GF_ISOM_BOX_TYPE_AVCC: case GF_ISOM_BOX_TYPE_SVCC: return avcc_Write(a, bs); + case GF_ISOM_BOX_TYPE_HVCC: + return hvcc_Write(a, bs); case GF_ISOM_BOX_TYPE_BTRT: return btrt_Write(a, bs); case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Write(a, bs); case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: return mp4v_Write(a, bs); /*3GPP streaming text*/ @@ -1584,11 +1607,17 @@ static GF_Err gf_isom_box_size_listing(GF_Box *a) case GF_ISOM_BOX_TYPE_AVCC: case GF_ISOM_BOX_TYPE_SVCC: return avcc_Size(a); + case GF_ISOM_BOX_TYPE_HVCC: + return hvcc_Size(a); case GF_ISOM_BOX_TYPE_BTRT: return btrt_Size(a); case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Size(a); case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: return mp4v_Size(a); /*3GPP streaming text*/ diff --git a/src/isomedia/data_map.c b/src/isomedia/data_map.c index 68bc03c..298c3cb 100644 --- a/src/isomedia/data_map.c +++ b/src/isomedia/data_map.c @@ -130,6 +130,10 @@ GF_Err gf_isom_datamap_new(const char *location, const char *parentPath, u8 mode #else return GF_NOT_SUPPORTED; #endif + } else if (!strncmp(location, "gmem://", 7)) { + *outDataMap = gf_isom_fdm_new(location, GF_ISOM_DATA_MAP_READ); + if (! (*outDataMap)) return GF_IO_ERR; + return GF_OK; } extern_file = !gf_url_is_local(location); @@ -340,6 +344,18 @@ GF_DataMap *gf_isom_fdm_new(const char *sPath, u8 mode) bs_mode = GF_BITSTREAM_READ; } #endif + if (!strncmp(sPath, "gmem://", 7)) { + u32 size; + void *mem_address; + if (sscanf(sPath, "gmem://%d@%p", &size, &mem_address) != 2) + return NULL; + tmp->bs = gf_bs_new((const char *)mem_address, size, GF_BITSTREAM_READ); + if (!tmp->bs) { + gf_free(tmp); + return NULL; + } + return (GF_DataMap *)tmp; + } switch (mode) { case GF_ISOM_DATA_MAP_READ: @@ -426,7 +442,7 @@ u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u6 //rewind to original (if seek fails, return 0 cause this means: //1- no support for seek on the platform //2- corrupted file for the OS - fflush(ptr->stream); + if (ptr->stream) fflush(ptr->stream); gf_bs_seek(ptr->bs, ptr->curPos); } ptr->last_acces_was_read = 1; @@ -472,7 +488,7 @@ GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize) } ptr->curPos = gf_bs_get_position(ptr->bs); //flush the stream !! - fflush(ptr->stream); + if (ptr->stream) fflush(ptr->stream); return GF_OK; } diff --git a/src/isomedia/drm_sample.c b/src/isomedia/drm_sample.c index f94ed33..1bf6093 100644 --- a/src/isomedia/drm_sample.c +++ b/src/isomedia/drm_sample.c @@ -337,6 +337,7 @@ GF_Err gf_isom_remove_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, gf_isom_box_array_del(sea->protections); sea->protections = gf_list_new(); if (sea->type == GF_4CC('2','6','4','b')) sea->type = GF_ISOM_BOX_TYPE_AVC1; + if (sea->type == GF_4CC('2','6','5','b')) sea->type = GF_ISOM_BOX_TYPE_HVC1; return GF_OK; } @@ -403,10 +404,17 @@ GF_Err gf_isom_set_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u3 /*special case for AVC1*/ case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: original_format = GF_4CC('2','6','4','b'); sea->type = GF_ISOM_BOX_TYPE_ENCV; break; + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: + original_format = GF_4CC('2','6','5','b'); + sea->type = GF_ISOM_BOX_TYPE_ENCV; + break; case GF_ISOM_BOX_TYPE_MP4S: case GF_ISOM_BOX_TYPE_LSR1: original_format = sea->type; @@ -467,8 +475,12 @@ GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 des case GF_ISOM_BOX_TYPE_MP4V: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_D263: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: original_format = sea->type; sea->type = GF_ISOM_BOX_TYPE_ENCV; break; diff --git a/src/isomedia/isom_intern.c b/src/isomedia/isom_intern.c index 6d54c1f..30bf769 100644 --- a/src/isomedia/isom_intern.c +++ b/src/isomedia/isom_intern.c @@ -481,6 +481,20 @@ GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID) return NULL; } +GF_TrackBox *gf_isom_get_track_from_original_id(GF_MovieBox *moov, u32 originalID, u32 originalFile) +{ + u32 i, count; + GF_TrackBox *trak; + if (!moov || !originalID) return NULL; + + count = gf_list_count(moov->trackList); + for (i = 0; itrackList, i); + if ((trak->originalFile == originalFile) && (trak->originalID == originalID)) return trak; + } + return NULL; +} + GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *movie, u32 trackNumber) { GF_TrackBox *trak; diff --git a/src/isomedia/isom_read.c b/src/isomedia/isom_read.c index 8638353..5297372 100644 --- a/src/isomedia/isom_read.c +++ b/src/isomedia/isom_read.c @@ -92,17 +92,28 @@ void gf_isom_sample_del(GF_ISOSample **samp) GF_EXPORT Bool gf_isom_probe_file(const char *fileName) { - unsigned char data[4]; - u32 type; - FILE *f = gf_f64_open(fileName, "rb"); - if (!f) return 0; - type = 0; - if (fread(data, 1, 4, f) == 4) { + u32 type = 0; + + if (!strncmp(fileName, "gmem://", 7)) { + u32 size; + u8 *mem_address; + if (sscanf(fileName, "gmem://%d@%p", &size, &mem_address) != 2) { + return GF_URL_ERROR; + } + if (size>8) + type = GF_4CC(mem_address[4], mem_address[5], mem_address[6], mem_address[7]); + } else { + unsigned char data[4]; + FILE *f = gf_f64_open(fileName, "rb"); + if (!f) return 0; + type = 0; if (fread(data, 1, 4, f) == 4) { - type = GF_4CC(data[0], data[1], data[2], data[3]); + if (fread(data, 1, 4, f) == 4) { + type = GF_4CC(data[0], data[1], data[2], data[3]); + } } + fclose(f); } - fclose(f); switch (type) { case GF_ISOM_BOX_TYPE_MOOV: case GF_ISOM_BOX_TYPE_MDAT: @@ -405,6 +416,16 @@ u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID) return 0; } +GF_EXPORT +u32 gf_isom_get_track_original_id(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!movie) return 0; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + return trak->originalID; +} + //return the timescale of the movie, 0 if error GF_EXPORT Bool gf_isom_has_movie(GF_ISOFile *file) @@ -568,7 +589,7 @@ GF_Err gf_isom_get_media_language(GF_ISOFile *the_file, u32 trackNumber, char *t //Return the number of track references of a track for a given ReferenceType -//return -1 if error +//return 0 if error GF_EXPORT s32 gf_isom_get_reference_count(GF_ISOFile *movie, u32 trackNumber, u32 referenceType) { @@ -622,10 +643,35 @@ GF_Err gf_isom_get_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceTy return GF_OK; } -//Return the number of track references of a track for a given ReferenceType +//Return the referenced track ID for a track and a given ReferenceType and Index +//return -1 if error, 0 if the reference is a NULL one, or the trackNumber +GF_EXPORT +GF_Err gf_isom_get_reference_ID(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrackID) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + trak = gf_isom_get_track_from_file(movie, trackNumber); + + *refTrackID = 0; + if (!trak || !trak->References) return GF_BAD_PARAM; + + dpnd = NULL; + e = Track_FindRef(trak, referenceType, &dpnd); + if (e) return e; + if (!dpnd) return GF_BAD_PARAM; + + if (referenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM; + + *refTrackID = dpnd->trackIDs[referenceIndex-1]; + + return GF_OK; +} + +//Return referenceIndex if the given track has a reference to the given TreckID of a given ReferenceType //return -1 if error GF_EXPORT -Bool gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 refTrackID) +u32 gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 refTrackID) { u32 i; GF_TrackBox *trak; @@ -638,7 +684,7 @@ Bool gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referen if ( (movie->LastError = Track_FindRef(trak, referenceType, &dpnd)) ) return 0; if (!dpnd) return 0; for (i=0; itrackIDCount; i++) { - if (dpnd->trackIDs[i]==refTrackID) return 1; + if (dpnd->trackIDs[i]==refTrackID) return i+1; } return 0; } @@ -2162,8 +2208,12 @@ GF_Err gf_isom_get_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDes case GF_ISOM_SUBTYPE_3GP_H263: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_GNRV: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: *Width = ((GF_VisualSampleEntryBox*)entry)->Width; *Height = ((GF_VisualSampleEntryBox*)entry)->Height; return GF_OK; @@ -2238,8 +2288,12 @@ GF_Err gf_isom_get_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 St case GF_ISOM_SUBTYPE_3GP_H263: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_GNRV: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: *hSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->hSpacing : 0; *vSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->vSpacing : 0; return GF_OK; @@ -2417,8 +2471,12 @@ u32 gf_isom_guess_specification(GF_ISOFile *file) case GF_ISOM_SUBTYPE_3GP_EVRC: nb_evrc++; break; case GF_ISOM_SUBTYPE_3GP_QCELP: nb_qcelp++; break; case GF_ISOM_SUBTYPE_3GP_SMV: nb_smv++; break; - case GF_ISOM_SUBTYPE_AVC_H264: nb_avc++; break; - case GF_ISOM_SUBTYPE_AVC2_H264: nb_avc++; break; + case GF_ISOM_SUBTYPE_AVC_H264: + case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: + nb_avc++; + break; case GF_ISOM_SUBTYPE_SVC_H264: nb_avc++; break; case GF_ISOM_SUBTYPE_MPEG4: case GF_ISOM_SUBTYPE_MPEG4_CRYP: @@ -2681,8 +2739,12 @@ GF_Err gf_isom_get_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptio case GF_ISOM_BOX_TYPE_MP4V: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: break; default: return GF_BAD_PARAM; @@ -2845,4 +2907,30 @@ u32 gf_isom_get_fragments_count(GF_ISOFile *movie, Bool segments_only) #endif +GF_EXPORT +GF_Err gf_isom_set_nalu_extract_mode(GF_ISOFile *the_file, u32 trackNumber, u32 nalu_extract_mode) +{ + GF_TrackReferenceTypeBox *dpnd; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + trak->extractor_mode = nalu_extract_mode; + + if (!trak->References) return GF_OK; + + /*get base*/ + dpnd = NULL; + trak->has_base_layer = 0; + Track_FindRef(trak, GF_ISOM_REF_SCAL, &dpnd); + if (dpnd) trak->has_base_layer = 1; + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_nalu_extract_mode(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->extractor_mode; +} + #endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/isomedia/isom_write.c b/src/isomedia/isom_write.c index f94d4df..587f9c0 100644 --- a/src/isomedia/isom_write.c +++ b/src/isomedia/isom_write.c @@ -1171,7 +1171,11 @@ GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDes case GF_ISOM_SUBTYPE_3GP_H263: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: ((GF_VisualSampleEntryBox*)entry)->Width = Width; ((GF_VisualSampleEntryBox*)entry)->Height = Height; trak->Header->width = Width<<16; @@ -1215,7 +1219,11 @@ GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 St case GF_ISOM_SUBTYPE_3GP_H263: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: break; default: return GF_BAD_PARAM; @@ -2451,6 +2459,7 @@ GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *de GF_TrackBox *trak, *new_tk; GF_BitStream *bs; char *data; + const char *buffer; u32 data_size; Double ts_scale; GF_Err e; @@ -2510,6 +2519,12 @@ GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *de moov_AddBox((GF_Box*)dest_file->moov, (GF_Box *)new_tk); + /*set originalID*/ + new_tk->originalID = trak->Header->trackID; + /*set originalFile*/ + buffer = gf_isom_get_filename(orig_file); + new_tk->originalFile = gf_crc_32(buffer, sizeof(buffer)); + /*rewrite edit list segmentDuration to new movie timescale*/ ts_scale = dest_file->moov->mvhd->timeScale; ts_scale /= orig_file->moov->mvhd->timeScale; @@ -2974,6 +2989,36 @@ GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, u32 trackID) return GF_OK; } +/*force to rewrite all dependencies when the trackID of referenced track changes*/ +GF_EXPORT +GF_Err gf_isom_rewrite_track_dependencies(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackReferenceTypeBox *ref; + GF_TrackBox *trak, *a_trak; + u32 i, k; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) + return GF_BAD_PARAM; + if (!trak->References) + return GF_OK; + + i=0; + while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->other_boxes, &i))) { + for (k=0; k < ref->trackIDCount; k++) { + a_trak = gf_isom_get_track_from_original_id(movie->moov, ref->trackIDs[k], trak->originalFile); + if (a_trak) { + ref->trackIDs[k] = a_trak->Header->trackID; + } else { + a_trak = gf_isom_get_track_from_id(movie->moov, ref->trackIDs[k]); + /*we should have a track with no original ID (not imported) - should we rewrite the dependency ?*/ + if (! a_trak || a_trak->originalID) return GF_BAD_PARAM; + } + } + } + + return GF_OK; +} GF_EXPORT GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset) @@ -3259,21 +3304,25 @@ Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index break; case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: { GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1; GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2; data1 = data2 = NULL; bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); - a = (GF_Box *)avc1->avc_config; + a = avc1->hevc_config ? (GF_Box *) avc1->hevc_config : (GF_Box *) avc1->avc_config; gf_isom_box_size(a); gf_isom_box_write(a, bs); gf_bs_get_content(bs, &data1, &data1_size); gf_bs_del(bs); bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); - a = (GF_Box *)avc2->avc_config; + a = avc2->hevc_config ? (GF_Box *) avc2->hevc_config : (GF_Box *) avc2->avc_config; gf_isom_box_size(a); gf_isom_box_write(a, bs); gf_bs_get_content(bs, &data2, &data2_size); @@ -4100,8 +4149,12 @@ GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptio case GF_ISOM_BOX_TYPE_MP4V: case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: break; default: return GF_BAD_PARAM; @@ -4410,7 +4463,6 @@ GF_Err gf_isom_set_composition_offset_mode(GF_ISOFile *file, u32 track, Bool use } } - #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/ diff --git a/src/isomedia/media.c b/src/isomedia/media.c index 24a6d20..1aa4762 100644 --- a/src/isomedia/media.c +++ b/src/isomedia/media.c @@ -173,7 +173,11 @@ GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **out_esd, Bo break; case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: esd = ((GF_MPEGVisualSampleEntryBox*) entry)->emul_esd; break; case GF_ISOM_BOX_TYPE_MP4A: @@ -373,6 +377,10 @@ GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, e = Media_RewriteODFrame(mdia, *samp); if (e) return e; } + else if (gf_isom_is_nalu_based_entry(mdia, entry)) { + e = gf_isom_nalu_sample_rewrite(mdia, *samp, sampleNumber, (GF_MPEGVisualSampleEntryBox *)entry); + if (e) return e; + } else if (mdia->mediaTrack->moov->mov->convert_streaming_text && ((mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) || (mdia->handler->handlerType == GF_ISOM_MEDIA_SUBT)) ) { diff --git a/src/isomedia/movie_fragments.c b/src/isomedia/movie_fragments.c index 02c44c0..bf45801 100644 --- a/src/isomedia/movie_fragments.c +++ b/src/isomedia/movie_fragments.c @@ -883,7 +883,7 @@ GF_Err gf_isom_allocate_sidx(GF_ISOFile *movie, s32 subsegs_per_sidx, Bool daisy if (e) return e; if (start_range) *start_range = (u32) movie->root_sidx_offset; - if (end_range) *end_range = (u32) gf_bs_get_position(bs); + if (end_range) *end_range = (u32) gf_bs_get_position(bs)-1; return GF_OK; } diff --git a/src/isomedia/stbl_write.c b/src/isomedia/stbl_write.c index aee50e5..90d75df 100644 --- a/src/isomedia/stbl_write.c +++ b/src/isomedia/stbl_write.c @@ -78,7 +78,10 @@ GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 Last //we need to split the entry if (ent->sampleCount == 1) { //use this one and adjust... - ent->sampleDelta = (u32) (DTS - stts->w_LastDTS); + if (stts->w_LastDTS) + ent->sampleDelta += (u32) (DTS - stts->w_LastDTS); + else + ent->sampleDelta = (u32) DTS; ent->sampleCount ++; stts->w_currentSampleNum ++; stts->w_LastDTS = DTS; diff --git a/src/isomedia/track.c b/src/isomedia/track.c index b8b02de..507b7e9 100644 --- a/src/isomedia/track.c +++ b/src/isomedia/track.c @@ -197,7 +197,7 @@ default_sync: } else { esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) { -#ifndef GPAC_DISABLE_CORE_TOOLS +#if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB) gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength); gf_free(rvc_cfg_data); #endif @@ -877,8 +877,12 @@ GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, break; case GF_ISOM_BOX_TYPE_AVC1: case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_AVC3: + case GF_ISOM_BOX_TYPE_AVC4: case GF_ISOM_BOX_TYPE_SVC1: - e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd); + case GF_ISOM_BOX_TYPE_HVC1: + case GF_ISOM_BOX_TYPE_HEV1: + e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd); if (e) return e; break; case GF_ISOM_BOX_TYPE_LSR1: @@ -887,7 +891,7 @@ GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, break; default: - gf_odf_desc_del((GF_Descriptor *) esd); + //gf_odf_desc_del((GF_Descriptor *) esd); break; } } else { @@ -905,7 +909,12 @@ GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_AVC) { entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1); if (!entry_v) return GF_OUT_OF_MEM; - e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd); + e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd); + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_HEVC) { + entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1); + if (!entry_v) return GF_OUT_OF_MEM; + e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd); + } else { entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V); if (!entry_v) return GF_OUT_OF_MEM; diff --git a/src/media_tools/av_parsers.c b/src/media_tools/av_parsers.c index d54f1d6..74bc079 100644 --- a/src/media_tools/av_parsers.c +++ b/src/media_tools/av_parsers.c @@ -1461,7 +1461,7 @@ static u8 avc_golomb_bits[256] = { 0 }; -static u32 avc_get_ue(GF_BitStream *bs) +static u32 bs_get_ue(GF_BitStream *bs) { u8 coded; u32 bits = 0, read = 0; @@ -1478,14 +1478,14 @@ static u32 avc_get_ue(GF_BitStream *bs) return gf_bs_read_int(bs, bits + 1) - 1; } -static s32 avc_get_se(GF_BitStream *bs) +static s32 bs_get_se(GF_BitStream *bs) { - u32 v = avc_get_ue(bs); + u32 v = bs_get_ue(bs); if ((v & 0x1) == 0) return (s32) (0 - (v>>1)); return (v + 1) >> 1; } -u32 AVC_IsStartCode(GF_BitStream *bs) +u32 gf_media_nalu_is_start_code(GF_BitStream *bs) { u8 s1, s2, s3, s4; Bool is_sc = 0; @@ -1506,7 +1506,7 @@ u32 AVC_IsStartCode(GF_BitStream *bs) /*read that amount of data at each IO access rather than fetching byte by byte...*/ #define AVC_CACHE_SIZE 4096 -u32 AVC_NextStartCode(GF_BitStream *bs) +u32 gf_media_nalu_next_start_code_bs(GF_BitStream *bs) { u32 v, bpos; char avc_cache[AVC_CACHE_SIZE]; @@ -1539,7 +1539,37 @@ u32 AVC_NextStartCode(GF_BitStream *bs) return (u32) (end-start); } -Bool AVC_SliceIsIntra(AVCState *avc) +u32 gf_media_nalu_next_start_code(u8 *data, u32 data_len, u32 *sc_size) +{ + u32 v, bpos; + u32 end; + + bpos = 0; + end = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) data_len) + break; + + v = ( (v<<8) & 0xFFFFFF00) | ((u32) data[bpos]); + bpos++; + if (v == 0x00000001) { + end = bpos-4; + *sc_size = 4; + return end; + } + else if ( (v & 0x00FFFFFF) == 0x00000001) { + end = bpos-3; + *sc_size = 3; + return end; + } + } + if (!end) end = data_len; + return (u32) (end); +} + +Bool gf_media_avc_slice_is_intra(AVCState *avc) { switch (avc->s_info.slice_type) { case GF_AVC_TYPE_I: @@ -1552,7 +1582,7 @@ Bool AVC_SliceIsIntra(AVCState *avc) } } -Bool AVC_SliceIsIDR(AVCState *avc) +Bool gf_media_avc_slice_is_IDR(AVCState *avc) { if (avc->sei.recovery_point.valid) { @@ -1561,7 +1591,7 @@ Bool AVC_SliceIsIDR(AVCState *avc) } if (avc->s_info.nal_unit_type != GF_AVC_NALU_IDR_SLICE) return 0; - return AVC_SliceIsIntra(avc); + return gf_media_avc_slice_is_intra(avc); } static const struct { u32 w, h; } avc_sar[14] = @@ -1578,14 +1608,16 @@ static void avc_parse_hrd_parameters(GF_BitStream *bs, AVC_HRD *hrd) { int i, cpb_cnt_minus1; - cpb_cnt_minus1 = avc_get_ue(bs); /*cpb_cnt_minus1*/ + cpb_cnt_minus1 = bs_get_ue(bs); /*cpb_cnt_minus1*/ + if (cpb_cnt_minus1 > 31) + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[avc-h264] invalid cpb_cnt_minus1 value: %d (expected in [0;31])\n", cpb_cnt_minus1)); gf_bs_read_int(bs, 4); /*bit_rate_scale*/ gf_bs_read_int(bs, 4); /*cpb_size_scale*/ /*for( SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ ) {*/ for (i=0; i<=cpb_cnt_minus1; i++) { - avc_get_ue(bs); /*bit_rate_value_minus1[ SchedSelIdx ]*/ - avc_get_ue(bs); /*cpb_size_value_minus1[ SchedSelIdx ]*/ + bs_get_ue(bs); /*bit_rate_value_minus1[ SchedSelIdx ]*/ + bs_get_ue(bs); /*cpb_size_value_minus1[ SchedSelIdx ]*/ gf_bs_read_int(bs, 1); /*cbr_flag[ SchedSelIdx ]*/ } gf_bs_read_int(bs, 5); /*initial_cpb_removal_delay_length_minus1*/ @@ -1733,7 +1765,7 @@ static u32 avc_remove_emulation_bytes(const unsigned char *buffer_src, unsigned return nal_size-emulation_bytes_count; } -s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos) +s32 gf_media_avc_read_sps(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos) { AVC_SPS *sps; u32 ChromaArrayType = 0; @@ -1753,6 +1785,8 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, } if (vui_flag_pos) *vui_flag_pos = 0; + /*nal hdr*/ gf_bs_read_int(bs, 8); + profile_idc = gf_bs_read_int(bs, 8); pcomp = gf_bs_read_int(bs, 8); @@ -1765,7 +1799,7 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, /*SubsetSps is used to be sure that AVC SPS are not going to be scratched by subset SPS. According to the SVC standard, subset SPS can have the same sps_id than its base layer, but it does not refer to the same SPS. */ - sps_id = avc_get_ue(bs) + GF_SVC_SSPS_ID_SHIFT * subseq_sps; + sps_id = bs_get_ue(bs) + GF_SVC_SSPS_ID_SHIFT * subseq_sps; if (sps_id >=32) { sps_id = -1; goto exit; @@ -1789,7 +1823,7 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, case 86: case 118: case 128: - chroma_format_idc = avc_get_ue(bs); + chroma_format_idc = bs_get_ue(bs); ChromaArrayType = chroma_format_idc; if (chroma_format_idc == 3) { u8 separate_colour_plane_flag = gf_bs_read_int(bs, 1); @@ -1800,8 +1834,8 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, */ if (separate_colour_plane_flag) ChromaArrayType = 0; } - luma_bd = avc_get_ue(bs); - chroma_bd = avc_get_ue(bs); + luma_bd = bs_get_ue(bs); + chroma_bd = bs_get_ue(bs); /*qpprime_y_zero_transform_bypass_flag = */ gf_bs_read_int(bs, 1); /*seq_scaling_matrix_present_flag*/ if (gf_bs_read_int(bs, 1)) { @@ -1812,7 +1846,7 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 sl = k<6 ? 16 : 64; for (z=0; zprofile_idc = profile_idc; sps->level_idc = level_idc; sps->prof_compat = pcomp; - sps->log2_max_frame_num = avc_get_ue(bs) + 4; - sps->poc_type = avc_get_ue(bs); + sps->log2_max_frame_num = bs_get_ue(bs) + 4; + sps->poc_type = bs_get_ue(bs); sps->chroma_format = chroma_format_idc; sps->luma_bit_depth_m8 = luma_bd; sps->chroma_bit_depth_m8 = chroma_bd; if (sps->poc_type == 0) { - sps->log2_max_poc_lsb = avc_get_ue(bs) + 4; + sps->log2_max_poc_lsb = bs_get_ue(bs) + 4; } else if(sps->poc_type == 1) { sps->delta_pic_order_always_zero_flag = gf_bs_read_int(bs, 1); - sps->offset_for_non_ref_pic = avc_get_se(bs); - sps->offset_for_top_to_bottom_field = avc_get_se(bs); - sps->poc_cycle_length = avc_get_ue(bs); - for(i=0; ipoc_cycle_length; i++) sps->offset_for_ref_frame[i] = avc_get_se(bs); + sps->offset_for_non_ref_pic = bs_get_se(bs); + sps->offset_for_top_to_bottom_field = bs_get_se(bs); + sps->poc_cycle_length = bs_get_ue(bs); + for(i=0; ipoc_cycle_length; i++) sps->offset_for_ref_frame[i] = bs_get_se(bs); } if (sps->poc_type > 2) { sps_id = -1; goto exit; } - avc_get_ue(bs); /*ref_frame_count*/ + bs_get_ue(bs); /*ref_frame_count*/ gf_bs_read_int(bs, 1); /*gaps_in_frame_num_allowed_flag*/ - mb_width = avc_get_ue(bs) + 1; - mb_height= avc_get_ue(bs) + 1; + mb_width = bs_get_ue(bs) + 1; + mb_height= bs_get_ue(bs) + 1; sps->frame_mbs_only_flag = gf_bs_read_int(bs, 1); @@ -1860,10 +1894,10 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, gf_bs_read_int(bs, 1); /*direct_8x8_inference_flag*/ cl = cr = ct = cb = 0; if (gf_bs_read_int(bs, 1)) /*crop*/ { - cl = avc_get_ue(bs); /*crop_left*/ - cr = avc_get_ue(bs); /*crop_right*/ - ct = avc_get_ue(bs); /*crop_top*/ - cb = avc_get_ue(bs); /*crop_bottom*/ + cl = bs_get_ue(bs); /*crop_left*/ + cr = bs_get_ue(bs); /*crop_right*/ + ct = bs_get_ue(bs); /*crop_top*/ + cb = bs_get_ue(bs); /*crop_bottom*/ sps->width = 16*mb_width - 2*(cl + cr); sps->height -= (2-sps->frame_mbs_only_flag)*2*(ct + cb); @@ -1899,8 +1933,8 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, } if (gf_bs_read_int(bs, 1)) { /* chroma_location_info_present_flag */ - avc_get_ue(bs); /* chroma_sample_location_type_top_field */ - avc_get_ue(bs); /* chroma_sample_location_type_bottom_field */ + bs_get_ue(bs); /* chroma_sample_location_type_top_field */ + bs_get_ue(bs); /* chroma_sample_location_type_bottom_field */ } sps->vui.timing_info_present_flag = gf_bs_read_int(bs, 1); @@ -1943,10 +1977,10 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, /*seq_ref_layer_chroma_phase_x_plus1_flag*/gf_bs_read_int(bs, 1); /*seq_ref_layer_chroma_phase_y_plus1*/gf_bs_read_int(bs, 2); } - /*seq_scaled_ref_layer_left_offset*/ avc_get_se(bs); - /*seq_scaled_ref_layer_top_offset*/avc_get_se(bs); - /*seq_scaled_ref_layer_right_offset*/avc_get_se(bs); - /*seq_scaled_ref_layer_bottom_offset*/avc_get_se(bs); + /*seq_scaled_ref_layer_left_offset*/ bs_get_se(bs); + /*seq_scaled_ref_layer_top_offset*/bs_get_se(bs); + /*seq_scaled_ref_layer_right_offset*/bs_get_se(bs); + /*seq_scaled_ref_layer_bottom_offset*/bs_get_se(bs); } if (/*seq_tcoeff_level_prediction_flag*/gf_bs_read_int(bs, 1)) { /*adaptive_tcoeff_level_prediction_flag*/ gf_bs_read_int(bs, 1); @@ -1956,7 +1990,7 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, /*svc_vui_parameters_present*/ if (gf_bs_read_int(bs, 1)) { u32 i, vui_ext_num_entries_minus1; - vui_ext_num_entries_minus1 = avc_get_ue(bs); + vui_ext_num_entries_minus1 = bs_get_ue(bs); for (i=0; i <= vui_ext_num_entries_minus1; i++) { u8 vui_ext_nal_hrd_parameters_present_flag, vui_ext_vcl_hrd_parameters_present_flag, vui_ext_timing_info_present_flag; @@ -2001,7 +2035,7 @@ exit: return sps_id; } -s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) +s32 gf_media_avc_read_pps(char *pps_data, u32 pps_size, AVCState *avc) { GF_BitStream *bs; char *pps_data_without_emulation_bytes = NULL; @@ -2017,7 +2051,10 @@ s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) pps_id = -1; goto exit; } - pps_id = avc_get_ue(bs); + /*nal hdr*/gf_bs_read_u8(bs); + + + pps_id = bs_get_ue(bs); if (pps_id>=255) { pps_id = -1; goto exit; @@ -2025,7 +2062,7 @@ s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) pps = &avc->pps[pps_id]; if (!pps->status) pps->status = 1; - pps->sps_id = avc_get_ue(bs); + pps->sps_id = bs_get_ue(bs); if (pps->sps_id >= 32) { pps->sps_id = 0; pps_id = -1; @@ -2039,19 +2076,19 @@ s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) avc->sps_active_idx = pps->sps_id; /*set active sps*/ /*pps->cabac = */gf_bs_read_int(bs, 1); pps->pic_order_present= gf_bs_read_int(bs, 1); - pps->slice_group_count= avc_get_ue(bs) + 1; - if (pps->slice_group_count > 1 ) /*pps->mb_slice_group_map_type = */avc_get_ue(bs); - /*pps->ref_count[0]= */avc_get_ue(bs) /*+ 1*/; - /*pps->ref_count[1]= */avc_get_ue(bs) /*+ 1*/; + pps->slice_group_count= bs_get_ue(bs) + 1; + if (pps->slice_group_count > 1 ) /*pps->mb_slice_group_map_type = */bs_get_ue(bs); + /*pps->ref_count[0]= */bs_get_ue(bs) /*+ 1*/; + /*pps->ref_count[1]= */bs_get_ue(bs) /*+ 1*/; /* if ((pps->ref_count[0] > 32) || (pps->ref_count[1] > 32)) goto exit; */ /*pps->weighted_pred = */gf_bs_read_int(bs, 1); /*pps->weighted_bipred_idc = */gf_bs_read_int(bs, 2); - /*pps->init_qp = */avc_get_se(bs) /*+ 26*/; - /*pps->init_qs= */avc_get_se(bs) /*+ 26*/; - /*pps->chroma_qp_index_offset = */avc_get_se(bs); + /*pps->init_qp = */bs_get_se(bs) /*+ 26*/; + /*pps->init_qs= */bs_get_se(bs) /*+ 26*/; + /*pps->chroma_qp_index_offset = */bs_get_se(bs); /*pps->deblocking_filter_parameters_present = */gf_bs_read_int(bs, 1); /*pps->constrained_intra_pred = */gf_bs_read_int(bs, 1); pps->redundant_pic_cnt_present = gf_bs_read_int(bs, 1); @@ -2062,7 +2099,7 @@ exit: return pps_id; } -s32 AVC_ReadSeqParamSetExtId(char *spse_data, u32 spse_size) +s32 gf_media_avc_read_sps_ext(char *spse_data, u32 spse_size) { GF_BitStream *bs; char *spse_data_without_emulation_bytes = NULL; @@ -2074,9 +2111,9 @@ s32 AVC_ReadSeqParamSetExtId(char *spse_data, u32 spse_size) spse_data_without_emulation_bytes_size = avc_remove_emulation_bytes(spse_data, spse_data_without_emulation_bytes, spse_size); bs = gf_bs_new(spse_data_without_emulation_bytes, spse_data_without_emulation_bytes_size, GF_BITSTREAM_READ); - sps_id = -1; - if (bs) - sps_id = avc_get_ue(bs); + /*nal header*/gf_bs_read_u8(bs); + + sps_id = bs_get_ue(bs); gf_bs_del(bs); gf_free(spse_data_without_emulation_bytes); @@ -2104,11 +2141,11 @@ static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, Bool svc_idr_flag, A s32 pps_id; /*s->current_picture.reference= h->nal_ref_idc != 0;*/ - /*first_mb_in_slice = */avc_get_ue(bs); - si->slice_type = avc_get_ue(bs); + /*first_mb_in_slice = */bs_get_ue(bs); + si->slice_type = bs_get_ue(bs); if (si->slice_type > 9) return -1; - pps_id = avc_get_ue(bs); + pps_id = bs_get_ue(bs); if (pps_id>255) return -1; si->pps = &avc->pps[pps_id]; if (!si->pps->slice_group_count) return -2; @@ -2125,20 +2162,20 @@ static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, Bool svc_idr_flag, A si->bottom_field_flag = gf_bs_read_int(bs, 1); } if ((si->nal_unit_type==GF_AVC_NALU_IDR_SLICE) || svc_idr_flag) - si->idr_pic_id = avc_get_ue(bs); + si->idr_pic_id = bs_get_ue(bs); if (si->sps->poc_type==0) { si->poc_lsb = gf_bs_read_int(bs, si->sps->log2_max_poc_lsb); if (si->pps->pic_order_present && !si->field_pic_flag) { - si->delta_poc_bottom = avc_get_se(bs); + si->delta_poc_bottom = bs_get_se(bs); } } else if ((si->sps->poc_type==1) && !si->sps->delta_pic_order_always_zero_flag) { - si->delta_poc[0] = avc_get_se(bs); + si->delta_poc[0] = bs_get_se(bs); if ((si->pps->pic_order_present==1) && !si->field_pic_flag) - si->delta_poc[1] = avc_get_se(bs); + si->delta_poc[1] = bs_get_se(bs); } if (si->pps->redundant_pic_cnt_present) { - si->redundant_pic_cnt = avc_get_ue(bs); + si->redundant_pic_cnt = bs_get_ue(bs); } return 0; } @@ -2149,11 +2186,11 @@ static s32 svc_parse_slice(GF_BitStream *bs, AVCState *avc, AVCSliceInfo *si) s32 pps_id; /*s->current_picture.reference= h->nal_ref_idc != 0;*/ - /*first_mb_in_slice = */avc_get_ue(bs); - si->slice_type = avc_get_ue(bs); + /*first_mb_in_slice = */bs_get_ue(bs); + si->slice_type = bs_get_ue(bs); if (si->slice_type > 9) return -1; - pps_id = avc_get_ue(bs); + pps_id = bs_get_ue(bs); if (pps_id>255) return -1; si->pps = &avc->pps[pps_id]; @@ -2174,20 +2211,20 @@ static s32 svc_parse_slice(GF_BitStream *bs, AVCState *avc, AVCSliceInfo *si) if (si->field_pic_flag) si->bottom_field_flag = gf_bs_read_int(bs, 1); } if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE || si ->NalHeader.idr_pic_flag) - si->idr_pic_id = avc_get_ue(bs); + si->idr_pic_id = bs_get_ue(bs); if (si->sps->poc_type==0) { si->poc_lsb = gf_bs_read_int(bs, si->sps->log2_max_poc_lsb); if (si->pps->pic_order_present && !si->field_pic_flag) { - si->delta_poc_bottom = avc_get_se(bs); + si->delta_poc_bottom = bs_get_se(bs); } } else if ((si->sps->poc_type==1) && !si->sps->delta_pic_order_always_zero_flag) { - si->delta_poc[0] = avc_get_se(bs); + si->delta_poc[0] = bs_get_se(bs); if ((si->pps->pic_order_present==1) && !si->field_pic_flag) - si->delta_poc[1] = avc_get_se(bs); + si->delta_poc[1] = bs_get_se(bs); } if (si->pps->redundant_pic_cnt_present) { - si->redundant_pic_cnt = avc_get_ue(bs); + si->redundant_pic_cnt = bs_get_ue(bs); } return 0; } @@ -2197,7 +2234,7 @@ static s32 avc_parse_recovery_point_sei(GF_BitStream *bs, AVCState *avc) { AVCSeiRecoveryPoint *rp = &avc->sei.recovery_point; - rp->frame_cnt = avc_get_ue(bs); + rp->frame_cnt = bs_get_ue(bs); rp->exact_match_flag = gf_bs_read_int(bs, 1); rp->broken_link_flag = gf_bs_read_int(bs, 1); rp->changing_slice_group_idc = gf_bs_read_int(bs, 2); @@ -2379,7 +2416,7 @@ static void avc_compute_poc(AVCSliceInfo *si) si->poc = field_poc[1]; } -s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) +s32 gf_media_avc_parse_nalu(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) { u8 idr_flag; s32 slice, ret; @@ -2483,6 +2520,7 @@ s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) break; case GF_AVC_NALU_SEQ_PARAM: case GF_AVC_NALU_PIC_PARAM: + case GF_AVC_NALU_SVC_SUBSEQ_PARAM: return 0; default: if (avc->s_info.nal_unit_type <= GF_AVC_NALU_IDR_SLICE) ret = 1; @@ -2511,7 +2549,7 @@ s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) return ret; } -u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc) +u32 gf_media_avc_reformat_sei(char *buffer, u32 nal_size, AVCState *avc) { u32 ptype, psize, hdr, written, var; u64 start; @@ -2671,7 +2709,7 @@ static u8 avc_get_sar_idx(u32 w, u32 h) return 0xFF; } -GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) +GF_Err gf_media_avc_change_par(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) { GF_BitStream *orig, *mod; AVCState avc; @@ -2687,7 +2725,7 @@ GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) while ((slc = (GF_AVCConfigSlot *)gf_list_enum(avcc->sequenceParameterSets, &i))) { char *no_emulation_buf = NULL; u32 no_emulation_buf_size = 0, emulation_bytes = 0; - idx = AVC_ReadSeqInfo(slc->data+1/*skip NALU type*/, slc->size-1, &avc, 0, &bit_offset); + idx = gf_media_avc_read_sps(slc->data, slc->size, &avc, 0, &bit_offset); if (idx<0) { if ( orig ) gf_bs_del(orig); @@ -2704,14 +2742,15 @@ GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) mod = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); /*copy over till vui flag*/ - while (bit_offset) { + assert(bit_offset>=8); + while (bit_offset-8/*bit_offset doesn't take care of the first byte (NALU type)*/) { flag = gf_bs_read_int(orig, 1); gf_bs_write_int(mod, flag, 1); bit_offset--; } /*check VUI*/ flag = gf_bs_read_int(orig, 1); - gf_bs_write_int(mod, 1, 1); + gf_bs_write_int(mod, 1, 1); /*vui_parameters_present_flag*/ if (flag) { /*aspect_ratio_info_present_flag*/ if (gf_bs_read_int(orig, 1)) { @@ -2778,7 +2817,7 @@ GF_Err gf_avc_get_sps_info(char *sps_data, u32 sps_size, u32 *sps_id, u32 *width memset(&avc, 0, sizeof(AVCState)); avc.sps_active_idx = -1; - idx = AVC_ReadSeqInfo(sps_data+1/*skip NALU type*/, sps_size-1, &avc, 0, NULL); + idx = gf_media_avc_read_sps(sps_data, sps_size, &avc, 0, NULL); if (idx<0) { return GF_NON_COMPLIANT_BITSTREAM; } @@ -2792,11 +2831,634 @@ GF_Err gf_avc_get_sps_info(char *sps_data, u32 sps_size, u32 *sps_id, u32 *width return GF_OK; } -#endif /*GPAC_DISABLE_AV_PARSERS*/ +GF_EXPORT +GF_Err gf_avc_get_pps_info(char *pps_data, u32 pps_size, u32 *pps_id, u32 *sps_id) +{ + GF_BitStream *bs; + char *pps_data_without_emulation_bytes = NULL; + u32 pps_data_without_emulation_bytes_size = 0; + GF_Err e = GF_OK; + + /*PPS still contains emulation bytes*/ + pps_data_without_emulation_bytes = gf_malloc(pps_size*sizeof(char)); + pps_data_without_emulation_bytes_size = avc_remove_emulation_bytes(pps_data, pps_data_without_emulation_bytes, pps_size); + bs = gf_bs_new(pps_data_without_emulation_bytes, pps_data_without_emulation_bytes_size, GF_BITSTREAM_READ); + if (!bs) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + *pps_id = bs_get_ue(bs); + *sps_id = bs_get_ue(bs); +exit: + gf_bs_del(bs); + gf_free(pps_data_without_emulation_bytes); + return e; +} -#ifndef GPAC_DISABLE_AV_PARSERS +/********** +HEVC parsing +**********/ + +Bool gf_media_hevc_slice_is_intra(HEVCState *hevc) +{ + switch (hevc->s_info.nal_unit_type) { + case GF_HEVC_NALU_SLICE_BLA_W_LP: + case GF_HEVC_NALU_SLICE_BLA_W_DLP: + case GF_HEVC_NALU_SLICE_BLA_N_LP: + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + case GF_HEVC_NALU_SLICE_CRA: + return 1; + default: + return 0; + } +} + +Bool gf_media_hevc_slice_is_IDR(HEVCState *hevc) +{ + if (hevc->sei.recovery_point.valid) + { + hevc->sei.recovery_point.valid = 0; + return 1; + } + switch (hevc->s_info.nal_unit_type) { + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + return 1; + default: + return 0; + } +} + +static s32 hevc_parse_slice_segment(GF_BitStream *bs, HEVCState *hevc, HEVCSliceInfo *si) +{ + HEVC_PPS *pps; + HEVC_SPS *sps; + s32 pps_id; + Bool dependent_slice_segment_flag = 0; + Bool first_slice_segment_in_pic_flag = gf_bs_read_int(bs, 1); + Bool RapPicFlag = 0; + Bool IDRPicFlag = 0; + + switch (si->nal_unit_type) { + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + IDRPicFlag = 1; + RapPicFlag = 1; + break; + case GF_HEVC_NALU_SLICE_BLA_W_LP: + case GF_HEVC_NALU_SLICE_BLA_W_DLP: + case GF_HEVC_NALU_SLICE_BLA_N_LP: + case GF_HEVC_NALU_SLICE_CRA: + RapPicFlag = 1; + break; + } + + if (RapPicFlag) { + /*Bool no_output_of_prior_pics_flag = */gf_bs_read_int(bs, 1); + } + pps_id = bs_get_ue(bs); + if (pps_id>=64) return -1; + + pps = &hevc->pps[pps_id]; + sps = &hevc->sps[pps->sps_id]; + si->sps = sps; + si->pps = pps; + + if (!first_slice_segment_in_pic_flag && pps->dependent_slice_segments_enabled_flag) { + dependent_slice_segment_flag = gf_bs_read_int(bs, 1); + } + + { + if (!first_slice_segment_in_pic_flag) { + /*slice_segment_address = */gf_bs_read_int(bs, sps->bitsSliceSegmentAddress); + } + } + + if( !dependent_slice_segment_flag ) { + gf_bs_read_int(bs, pps->num_extra_slice_header_bits); + + si->slice_type = bs_get_ue(bs); + + if(pps->output_flag_present_flag) + /*pic_output_flag = */gf_bs_read_int(bs, 1); + + if (sps->separate_colour_plane_flag == 1) + /*colour_plane_id = */gf_bs_read_int(bs, 2); + + if (IDRPicFlag) { + si->poc_lsb = 0; + } else { + si->poc_lsb = gf_bs_read_int(bs, sps->log2_max_pic_order_cnt_lsb); + } + } + return 0; +} + +static void hevc_compute_poc(HEVCSliceInfo *si) +{ + u32 max_poc_lsb = 1 << (si->sps->log2_max_pic_order_cnt_lsb); + + /* frame_num_offset */ + switch (si->nal_unit_type) { + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + case GF_HEVC_NALU_SLICE_CRA: + si->poc_lsb_prev = 0; + si->poc_msb_prev = 0; + break; + } + + if ((si->poc_lsb < si->poc_lsb_prev) && (si->poc_lsb_prev - si->poc_lsb >= max_poc_lsb / 2) ) + si->poc_msb = si->poc_msb_prev + max_poc_lsb; + else if ((si->poc_lsb > si->poc_lsb_prev) && (si->poc_lsb - si->poc_lsb_prev > max_poc_lsb / 2)) + si->poc_msb = si->poc_msb_prev - max_poc_lsb; + else + si->poc_msb = si->poc_msb_prev; + + switch (si->nal_unit_type) { + case GF_HEVC_NALU_SLICE_BLA_W_LP: + case GF_HEVC_NALU_SLICE_BLA_W_DLP: + case GF_HEVC_NALU_SLICE_BLA_N_LP: + si->poc_msb = 0; + break; + } + si->poc = si->poc_msb + si->poc_lsb; +} + +void profile_tier_level(GF_BitStream *bs, Bool ProfilePresentFlag, u8 MaxNumSubLayersMinus1, HEVC_ProfileTierLevel *ptl) +{ + u32 i; + if (ProfilePresentFlag) { + ptl->profile_space = gf_bs_read_int(bs, 2); + ptl->tier_flag = gf_bs_read_int(bs, 1); + ptl->profile_idc = gf_bs_read_int(bs, 5); + + ptl->profile_compatibility_flag = gf_bs_read_int(bs, 32); + + /*general_reserved_zero_16bits = */gf_bs_read_int(bs, 16); + } + ptl->level_idc = gf_bs_read_int(bs, 8); + for (i=0; isub_ptl[i].profile_present_flag = gf_bs_read_int(bs, 1); + ptl->sub_ptl[i].level_present_flag = gf_bs_read_int(bs, 1); + if (ProfilePresentFlag && ptl->sub_ptl[i].profile_present_flag) { + ptl->sub_ptl[i].profile_space = gf_bs_read_int(bs, 2); + ptl->sub_ptl[i].tier_flag = gf_bs_read_int(bs, 1); + ptl->sub_ptl[i].profile_idc = gf_bs_read_int(bs, 5); + ptl->sub_ptl[i].profile_compatibility_flag = gf_bs_read_int(bs, 32); + /*sub_layer_reserved_zero_16bits*/gf_bs_read_int(bs, 16); + } + if (ptl->sub_ptl[i].level_present_flag) + ptl->sub_ptl[i].level_idc = gf_bs_read_int(bs, 8); + } +} + +void bit_rate_pic_rate_info(GF_BitStream *bs, u8 level_low, u8 level_high, HEVC_VPS *vps) +{ + u8 i; + for (i=level_low; i<=level_high; i++) { + Bool bit_rate_info_present_flag = gf_bs_read_int(bs, 1); + Bool pic_rate_info_present_flag = gf_bs_read_int(bs, 1); + if (bit_rate_info_present_flag) { + vps->rates[i].avg_bit_rate = gf_bs_read_int(bs, 16); + vps->rates[i].max_bit_rate = gf_bs_read_int(bs, 16); + } + if (pic_rate_info_present_flag) { + vps->rates[i].constand_pic_rate_idc = gf_bs_read_int(bs, 2); + vps->rates[i].avg_pic_rate = gf_bs_read_int(bs, 16); + } + } +} + +s32 gf_media_hevc_read_vps(char *data, u32 size, HEVCState *hevc) +{ + GF_BitStream *bs; + char *data_without_emulation_bytes = NULL; + u32 data_without_emulation_bytes_size = 0; + s32 vps_id = -1; + HEVC_VPS *vps; + + /*still contains emulation bytes*/ + data_without_emulation_bytes = gf_malloc(size*sizeof(char)); + data_without_emulation_bytes_size = avc_remove_emulation_bytes(data, data_without_emulation_bytes, size); + bs = gf_bs_new(data_without_emulation_bytes, data_without_emulation_bytes_size, GF_BITSTREAM_READ); + if (!bs) goto exit; + + gf_bs_read_u16(bs); + + vps_id = gf_bs_read_int(bs, 4); + + if (vps_id>=16) goto exit; + + vps = &hevc->vps[vps_id]; + if (!vps->state) { + vps->id = vps_id; + vps->state = 1; + } + + /*vps_temporal_id_nesting_flag = */ gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 6); + vps->max_sub_layer = gf_bs_read_int(bs, 3) + 1; + gf_bs_read_int(bs, 16); + profile_tier_level(bs, 1, vps->max_sub_layer-1, &vps->ptl); + bit_rate_pic_rate_info(bs, 0, vps->max_sub_layer-1, vps); + + + //and we don't care about the rest for now +exit: + gf_bs_del(bs); + gf_free(data_without_emulation_bytes); + return vps_id; +} + + +static Bool parse_short_term_ref_pic_set(GF_BitStream *bs, HEVC_SPS *sps, u32 idx_rps) +{ + u32 i; + Bool inter_ref_pic_set_prediction_flag = 0; + if (idx_rps != 0) + inter_ref_pic_set_prediction_flag = gf_bs_read_int(bs, 1); + + if (inter_ref_pic_set_prediction_flag ) { + HEVC_ReferencePictureSets *ref_ps, *rps; + u32 delta_idx_minus1 = 0; + u32 ref_idx; + u32 delta_rps_sign; + u32 abs_delta_rps_minus1, nb_ref_pics; + s32 deltaRPS; + u32 k = 0, k0 = 0, k1 = 0; + if (idx_rps == sps->num_short_term_ref_pic_sets) + delta_idx_minus1 = bs_get_ue(bs); + + assert(delta_idx_minus1 <= idx_rps - 1); + ref_idx = idx_rps - 1 - delta_idx_minus1; + delta_rps_sign = gf_bs_read_int(bs, 1); + abs_delta_rps_minus1 = bs_get_ue(bs); + deltaRPS = (1 - (delta_rps_sign<<1)) * (abs_delta_rps_minus1 + 1); + + rps = &sps->rps[idx_rps]; + ref_ps = &sps->rps[ref_idx]; + nb_ref_pics = ref_ps->num_negative_pics + ref_ps->num_positive_pics; + for (i=0; i<=nb_ref_pics; i++) { + s32 ref_idc; + s32 used_by_curr_pic_flag = gf_bs_read_int(bs, 1); + ref_idc = used_by_curr_pic_flag ? 1 : 0; + if ( !used_by_curr_pic_flag ) { + used_by_curr_pic_flag = gf_bs_read_int(bs, 1); + ref_idc = used_by_curr_pic_flag << 1; + } + if ((ref_idc==1) || (ref_idc== 2)) { + s32 deltaPOC = deltaRPS; + if (i < nb_ref_pics) + deltaPOC += ref_ps->delta_poc[i]; + + rps->delta_poc[k] = deltaPOC; + + if (deltaPOC < 0) k0++; + else k1++; + + k++; + } + } + rps->num_negative_pics = k0; + rps->num_positive_pics = k1; + } else { + s32 prev = 0, poc = 0; + sps->rps[idx_rps].num_negative_pics = bs_get_ue(bs); + sps->rps[idx_rps].num_positive_pics = bs_get_ue(bs); + for (i=0; irps[idx_rps].num_negative_pics; i++) { + u32 delta_poc_s0_minus1 = bs_get_ue(bs); + poc = prev - delta_poc_s0_minus1 - 1; + prev = poc; + sps->rps[idx_rps].delta_poc[i] = poc; + /*used_by_curr_pic_s1_flag[ i ] = */gf_bs_read_int(bs, 1); + } + for (i=0; irps[idx_rps].num_positive_pics; i++) { + u32 delta_poc_s1_minus1 = bs_get_ue(bs); + poc = prev + delta_poc_s1_minus1 + 1; + prev = poc; + sps->rps[idx_rps].delta_poc[i] = poc; + /*used_by_curr_pic_s1_flag[ i ] = */gf_bs_read_int(bs, 1); + } + } + return 1; +} + +s32 gf_media_hevc_read_sps(char *data, u32 size, HEVCState *hevc) +{ + GF_BitStream *bs; + char *data_without_emulation_bytes = NULL; + u32 data_without_emulation_bytes_size = 0; + s32 vps_id, sps_id = -1; + u8 max_sub_layers_minus1; + u32 i, nb_CTUs, depth; + u32 log2_diff_max_min_luma_coding_block_size; + u32 log2_min_transform_block_size, log2_min_luma_coding_block_size; + + Bool sps_sub_layer_ordering_info_present_flag; + HEVC_SPS *sps; + HEVC_ProfileTierLevel ptl; + + /*still contains emulation bytes*/ + data_without_emulation_bytes = gf_malloc(size*sizeof(char)); + data_without_emulation_bytes_size = avc_remove_emulation_bytes(data, data_without_emulation_bytes, size); + bs = gf_bs_new(data_without_emulation_bytes, data_without_emulation_bytes_size, GF_BITSTREAM_READ); + if (!bs) goto exit; + + gf_bs_read_u16(bs); + + vps_id = gf_bs_read_int(bs, 4); + if (vps_id>=16) goto exit; + + max_sub_layers_minus1 = gf_bs_read_int(bs, 3); + /*temporal_id_nesting_flag = */gf_bs_read_int(bs, 1); + memset(&ptl, 0, sizeof(ptl)); + profile_tier_level(bs, 1, max_sub_layers_minus1, &ptl); + + sps_id = bs_get_ue(bs); + if (sps_id>=16) goto exit; + sps = &hevc->sps[sps_id]; + if (!sps->state) { + sps->state = 1; + sps->id = sps_id; + sps->vps_id = vps_id; + } + sps->ptl = ptl; + + sps->chroma_format_idc = bs_get_ue(bs); + if (sps->chroma_format_idc==3) + sps->separate_colour_plane_flag = gf_bs_read_int(bs, 1); + sps->width = bs_get_ue(bs); + sps->height = bs_get_ue(bs); + if (gf_bs_read_int(bs, 1)) { + sps->cw_left = bs_get_ue(bs); + sps->cw_right = bs_get_ue(bs); + sps->cw_top = bs_get_ue(bs); + sps->cw_bottom = bs_get_ue(bs); + } + sps->bit_depth_luma = 8 + bs_get_ue(bs); + sps->bit_depth_chroma = 8 + bs_get_ue(bs); + + sps->log2_max_pic_order_cnt_lsb = 4 + bs_get_ue(bs); + + sps_sub_layer_ordering_info_present_flag = gf_bs_read_int(bs, 1); + for(i= sps_sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1; i<=max_sub_layers_minus1; i++) { + /*max_dec_pic_buffering = */ bs_get_ue(bs); + /*num_reorder_pics = */ bs_get_ue(bs); + /*max_latency_increase = */ bs_get_ue(bs); + } + + log2_min_luma_coding_block_size = 3 + bs_get_ue(bs); + log2_diff_max_min_luma_coding_block_size = bs_get_ue(bs); + sps->max_CU_width = ( 1<<(log2_min_luma_coding_block_size + log2_diff_max_min_luma_coding_block_size) ); + sps->max_CU_height = ( 1<<(log2_min_luma_coding_block_size + log2_diff_max_min_luma_coding_block_size) ); + + log2_min_transform_block_size = 2 + bs_get_ue(bs); + /*log2_max_transform_block_size = log2_min_transform_block_size + */bs_get_ue(bs); + + depth = 0; + /*u32 max_transform_hierarchy_depth_inter = */bs_get_ue(bs); + /*u32 max_transform_hierarchy_depth_intra = */bs_get_ue(bs); + while( (u32) ( sps->max_CU_width >> log2_diff_max_min_luma_coding_block_size ) > (u32) ( 1 << ( log2_min_transform_block_size + depth ) ) ) + { + depth++; + } + sps->max_CU_depth = log2_diff_max_min_luma_coding_block_size + depth; + + nb_CTUs = ((sps->width + sps->max_CU_width -1) / sps->max_CU_width) * ((sps->height + sps->max_CU_height-1) / sps->max_CU_height); + sps->bitsSliceSegmentAddress = 0; + while (nb_CTUs > (u32) (1 << sps->bitsSliceSegmentAddress)) { + sps->bitsSliceSegmentAddress++; + } + + if (/*scaling_list_enable_flag = */ gf_bs_read_int(bs, 1)) { + if (/*sps_scaling_list_data_present_flag=*/gf_bs_read_int(bs, 1) ) { + //scaling_list_data( ) + } + } + /*asymmetric_motion_partitions_enabled_flag= */ gf_bs_read_int(bs, 1); + /*sample_adaptive_offset_enabled_flag= */ gf_bs_read_int(bs, 1); + if (/*pcm_enabled_flag= */ gf_bs_read_int(bs, 1) ) { + /*pcm_sample_bit_depth_luma_minus1=*/gf_bs_read_int(bs, 4); + /*pcm_sample_bit_depth_chroma_minus1=*/gf_bs_read_int(bs, 4); + /*log2_min_pcm_luma_coding_block_size_minus3= */ bs_get_ue(bs); + /*log2_diff_max_min_pcm_luma_coding_block_size = */ bs_get_ue(bs); + /*pcm_loop_filter_disable_flag=*/gf_bs_read_int(bs, 1); + } + sps->num_short_term_ref_pic_sets = bs_get_ue(bs); + for (i=0;inum_short_term_ref_pic_sets; i++) { + Bool ret = parse_short_term_ref_pic_set(bs, sps, i); + /*cannot parse short_term_ref_pic_set, skip VUI parsing*/ + if (!ret) goto exit; + } + if (/*long_term_ref_pics_present_flag */ gf_bs_read_int(bs, 1) ) { + sps->num_long_term_ref_pic_sps = bs_get_ue(bs); + for (i=0; inum_long_term_ref_pic_sps; i++) { + /*lt_ref_pic_poc_lsb_sps=*/gf_bs_read_int(bs, sps->log2_max_pic_order_cnt_lsb); + /*used_by_curr_pic_lt_sps_flag*/gf_bs_read_int(bs, 1); + } + } + /*sps_temporal_mvp_enable_flag*/gf_bs_read_int(bs, 1); + /*strong_intra_smoothing_enable_flag*/gf_bs_read_int(bs, 1); + if (/*vui_parameters_present_flag*/gf_bs_read_int(bs, 1)) { + //vui param + } + if (/*sps_extension_flag*/gf_bs_read_int(bs, 1)) { + while (gf_bs_available(bs)) { + } + } + +exit: + gf_bs_del(bs); + gf_free(data_without_emulation_bytes); + return sps_id; +} + +s32 gf_media_hevc_read_pps(char *data, u32 size, HEVCState *hevc) +{ + u32 i; + GF_BitStream *bs; + char *data_without_emulation_bytes = NULL; + u32 data_without_emulation_bytes_size = 0; + s32 pps_id = -1; + HEVC_PPS *pps; + + /*still contains emulation bytes*/ + data_without_emulation_bytes = gf_malloc(size*sizeof(char)); + data_without_emulation_bytes_size = avc_remove_emulation_bytes(data, data_without_emulation_bytes, size); + bs = gf_bs_new(data_without_emulation_bytes, data_without_emulation_bytes_size, GF_BITSTREAM_READ); + if (!bs) goto exit; + + gf_bs_read_u16(bs); + + pps_id = bs_get_ue(bs); + + if (pps_id>=64) goto exit; + pps = &hevc->pps[pps_id]; + + if (!pps->state) { + pps->id = pps_id; + pps->state = 1; + } + pps->sps_id = bs_get_ue(bs); + hevc->sps_active_idx = pps->sps_id; /*set active sps*/ + pps->dependent_slice_segments_enabled_flag = gf_bs_read_int(bs, 1); + + /*sign_data_hiding_flag = */gf_bs_read_int(bs, 1); + /*cabac_init_present_flag = */gf_bs_read_int(bs, 1); + /*num_ref_idx_l0_default_active_minus1 = */bs_get_ue(bs); + /*num_ref_idx_l1_default_active_minus1 = */bs_get_ue(bs); + /*pic_init_qp_minus26 = */bs_get_se(bs); + /*constrained_intra_pred_flag = */gf_bs_read_int(bs, 1); + /*transform_skip_enabled_flag = */gf_bs_read_int(bs, 1); + if (/*cu_qp_delta_enabled_flag = */gf_bs_read_int(bs, 1) ) + /*diff_cu_qp_delta_depth = */bs_get_ue(bs); + + /*pic_cb_qp_offset = */bs_get_se(bs); + /*pic_cr_qp_offset = */bs_get_se(bs); + /*pic_slice_chroma_qp_offsets_present_flag = */gf_bs_read_int(bs, 1); + /*weighted_pred_flag = */gf_bs_read_int(bs, 1); + /*weighted_bipred_flag = */gf_bs_read_int(bs, 1); + pps->output_flag_present_flag = gf_bs_read_int(bs, 1); + /*transquant_bypass_enable_flag = */gf_bs_read_int(bs, 1); + pps->tiles_enabled_flag = gf_bs_read_int(bs, 1); + /*entropy_coding_sync_enabled_flag = */gf_bs_read_int(bs, 1); + if (pps->tiles_enabled_flag) { + u32 num_tile_columns_minus1 = bs_get_ue(bs); + u32 num_tile_rows_minus1 = bs_get_ue(bs); + pps->uniform_spacing_flag = gf_bs_read_int(bs, 1); + if (!pps->uniform_spacing_flag ) { + for( i = 0; i < num_tile_columns_minus1; i++ ) { + /*column_width_minus1[ i ] = */bs_get_ue(bs); + } + for( i = 0; i < num_tile_rows_minus1; i++ ) { + /*row_height_minus1[ i ] = */bs_get_ue(bs); + } + } + /*loop_filter_across_tiles_enabled_flag = */gf_bs_read_int(bs, 1); + } + /*loop_filter_across_slices_enabled_flag = */gf_bs_read_int(bs, 1); + if( /*deblocking_filter_control_present_flag = */gf_bs_read_int(bs, 1) ) { + /*deblocking_filter_override_enabled_flag= */gf_bs_read_int(bs, 1); + if (/*pic_disable_deblocking_filter_flag= */gf_bs_read_int(bs, 1) ) { + /*beta_offset_div2 = */bs_get_se(bs); + /*tc_offset_div2 = */bs_get_se(bs); + } + } + if (/*pic_scaling_list_data_present_flag = */gf_bs_read_int(bs, 1) ) { + //scaling_list_data( ) + assert(0 && "not implemented"); + } + /*lists_modification_present_flag = */gf_bs_read_int(bs, 1); + /*log2_parallel_merge_level_minus2 = */bs_get_ue(bs); + pps->num_extra_slice_header_bits = gf_bs_read_int(bs, 3); + pps->slice_segment_header_extension_present_flag = gf_bs_read_int(bs, 1); + if ( /*pps_extension_flag= */gf_bs_read_int(bs, 1) ) { + while (gf_bs_available(bs) ) { + //pps_extension_data_flag u(1) + } + } + +exit: + gf_bs_del(bs); + gf_free(data_without_emulation_bytes); + return pps_id; +} + + +s32 gf_media_hevc_parse_nalu(GF_BitStream *bs, HEVCState *hevc, u8 *nal_unit_type, u8 *temporal_id) +{ + u32 reserved; + s32 slice, ret; + HEVCSliceInfo n_state; + + slice = 0; + memcpy(&n_state, &hevc->s_info, sizeof(HEVCSliceInfo)); + + reserved = gf_bs_read_int(bs, 1); + if (reserved) return -1; + + *nal_unit_type = n_state.nal_unit_type = gf_bs_read_int(bs, 6); + reserved = gf_bs_read_int(bs, 6); +// if (reserved) return -1; + + *temporal_id = n_state.temporal_id = gf_bs_read_int(bs, 3); + + ret = 0; + switch (n_state.nal_unit_type) { + case GF_HEVC_NALU_ACCESS_UNIT: + case GF_HEVC_NALU_END_OF_SEQ: + case GF_HEVC_NALU_END_OF_STREAM: + ret = 1; + break; + + + /*slice_layer_rbsp*/ +// case GF_HEVC_NALU_SLICE_STSA_N: +// case GF_HEVC_NALU_SLICE_STSA_R: + case GF_HEVC_NALU_SLICE_RADL_N: +// case GF_HEVC_NALU_SLICE_RADL_R: + case GF_HEVC_NALU_SLICE_RASL_N: +// case GF_HEVC_NALU_SLICE_RASL_R: + break; + + /*slice_segment_layer_rbsp*/ + case GF_HEVC_NALU_SLICE_TRAIL_N: + case GF_HEVC_NALU_SLICE_TRAIL_R: + case GF_HEVC_NALU_SLICE_TSA_N: + case GF_HEVC_NALU_SLICE_TSA_R: + case GF_HEVC_NALU_SLICE_STSA_N: + case GF_HEVC_NALU_SLICE_STSA_R: + + case GF_HEVC_NALU_SLICE_BLA_W_LP: + case GF_HEVC_NALU_SLICE_BLA_W_DLP: + case GF_HEVC_NALU_SLICE_BLA_N_LP: + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + case GF_HEVC_NALU_SLICE_CRA: + case GF_HEVC_NALU_SLICE_RADL_R: + case GF_HEVC_NALU_SLICE_RASL_R: + slice = 1; + /* slice - read the info and compare.*/ + ret = hevc_parse_slice_segment(bs, hevc, &n_state); + if (ret<0) return ret; + + hevc_compute_poc(&n_state); + + ret = 0; + + if (hevc->s_info.poc != n_state.poc) { + ret=1; + break; + } + break; + case GF_HEVC_NALU_SEQ_PARAM: + case GF_HEVC_NALU_PIC_PARAM: + case GF_HEVC_NALU_VID_PARAM: + return 0; + default: + break; + } + + /* save _prev values */ + if (ret && hevc->s_info.sps) { + n_state.frame_num_offset_prev = hevc->s_info.frame_num_offset; + n_state.frame_num_prev = hevc->s_info.frame_num; + + n_state.poc_lsb_prev = hevc->s_info.poc_lsb; + n_state.poc_msb_prev = hevc->s_info.poc_msb; + + } + if (slice) hevc_compute_poc(&n_state); + memcpy(&hevc->s_info, &n_state, sizeof(HEVCSliceInfo)); + return ret; +} + static u32 AC3_FindSyncCode(u8 *buf, u32 buflen) { diff --git a/src/media_tools/dash_client.c b/src/media_tools/dash_client.c index 3336f9e..9b30691 100644 --- a/src/media_tools/dash_client.c +++ b/src/media_tools/dash_client.c @@ -28,8 +28,13 @@ #include #include #include +#include #include +#ifndef _WIN32_WCE +#include +#endif + #ifndef GPAC_DISABLE_DASH_CLIENT /*set to 1 if you want MPD to use SegmentTemplate if possible instead of SegmentList*/ @@ -60,6 +65,7 @@ struct __dash_client u32 max_cache_duration; u32 auto_switch_count; Bool keep_files, disable_switching, allow_local_mpd_update; + Bool is_m3u8; GF_DASHInitialSelectionMode first_select_mode; @@ -135,6 +141,8 @@ struct __dash_group u32 prev_active_rep_index; + Bool timeline_setup; + GF_DASHGroupSelection selection; @@ -148,6 +156,7 @@ struct __dash_group u32 nb_segments_in_rep; Double segment_duration; + Bool was_segment_base; /*local file playback, do not delete them*/ Bool local_files; /*next segment to download for this group*/ @@ -207,6 +216,64 @@ Bool gf_dash_check_mpd_root_type(const char *local_url) return 0; } +static void gf_dash_group_timeline_setup(GF_MPD *mpd, GF_DASH_Group *group) +{ + u32 sec, frac; +#ifndef _WIN32_WCE + time_t gtime; + struct tm *_t; +#endif + u64 current_time; + + if (mpd->type==GF_MPD_TYPE_STATIC) + return; + + /*M3U8 does not use NTP sync */ + if (group->dash->is_m3u8) + return; + + gf_net_get_ntp(&sec, &frac); + +#ifndef _WIN32_WCE + gtime = sec - GF_NTP_SEC_1900_TO_1970; + _t = gmtime(>ime); + current_time = mktime(_t); +#else + current_time = sec - GF_NTP_SEC_1900_TO_1970; +#endif + if (current_time < mpd->availabilityStartTime) current_time = 0; + else current_time -= mpd->availabilityStartTime; + + if (current_time < group->period->start) current_time = 0; + else current_time -= group->period->start; + +#if 0 + { + s32 diff = (s32) current_time - (s32) (mpd->media_presentation_duration/1000); + if (ABS(diff)>10) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Broken UTC timing in client or server - got Media URL is not set in segment list\n")); + + } + current_time = mpd->media_presentation_duration/1000; + } +#endif + + frac = mpd->time_shift_buffer_depth/1000; + if (current_time < frac) current_time = 0; + else current_time -= frac; + + + if (group->segment_duration) { + Double nb_seg = (Double) current_time; + nb_seg /= group->segment_duration; + frac = (u32) nb_seg; + group->download_segment_index = frac; + group->nb_segments_in_rep = frac + 10; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Segment duration unknown - cannot estimate current startNumber\n")); + } +} + void gf_dash_group_check_switch(GF_DASHFileIO *dash_io, GF_DASH_Group *group, Bool download_active) { u32 download_rate; @@ -279,7 +346,7 @@ static Bool gf_dash_is_m3u8_mime(const char * mime) { * Parameters are identical to the ones of gf_term_download_new. * \see gf_term_download_new() */ -GF_Err gf_dash_download_resource(GF_DASHFileIO *dash_io, GF_DASHFileIOSession *sess, const char *url, u64 start_range, u64 end_range, Bool persistent, GF_DASH_Group *group) +GF_Err gf_dash_download_resource(GF_DASHFileIO *dash_io, GF_DASHFileIOSession *sess, const char *url, u64 start_range, u64 end_range, u32 persistent_mode, GF_DASH_Group *group) { Bool had_sess = 0; Bool retry = 1; @@ -288,7 +355,7 @@ GF_Err gf_dash_download_resource(GF_DASHFileIO *dash_io, GF_DASHFileIOSession *s GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading %s...\n", url)); if (! *sess) { - *sess = dash_io->create(dash_io, persistent, url); + *sess = dash_io->create(dash_io, persistent_mode ? 1 : 0, url); if (!(*sess)){ assert(0); GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot try to download %s... OUT of memory ?\n", url)); @@ -296,22 +363,24 @@ GF_Err gf_dash_download_resource(GF_DASHFileIO *dash_io, GF_DASHFileIOSession *s } } else { had_sess = 1; - e = dash_io->setup_from_url(dash_io, *sess, url); - if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot resetup session for url %s: %s\n", url, gf_error_to_string(e) )); - return e; + if (persistent_mode!=2) { + e = dash_io->setup_from_url(dash_io, *sess, url); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot resetup session for url %s: %s\n", url, gf_error_to_string(e) )); + return e; + } } } retry: if (end_range) { - e = dash_io->set_range(dash_io, *sess, start_range, end_range); + e = dash_io->set_range(dash_io, *sess, start_range, end_range, (persistent_mode==2) ? 0 : 1); if (e) { if (had_sess) { dash_io->del(dash_io, *sess); *sess = NULL; - return gf_dash_download_resource(dash_io, sess, url, start_range, end_range, persistent, group); + return gf_dash_download_resource(dash_io, sess, url, start_range, end_range, persistent_mode ? 1 : 0, group); } GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot setup byte-range download for %s: %s\n", url, gf_error_to_string(e) )); return e; @@ -411,13 +480,6 @@ static void gf_dash_get_segment_duration(GF_MPD_Representation *rep, GF_MPD_Adap *nb_segments = timescale = 0; duration = 0; - /*single segment*/ - if (rep->segment_base || set->segment_base || period->segment_base) { - *max_seg_duration = mpd->media_presentation_duration; - *max_seg_duration /= 1000; - *nb_segments = 1; - return; - } if (rep->segment_list || set->segment_list || period->segment_list) { GF_List *segments = NULL; if (period->segment_list) { @@ -454,6 +516,14 @@ static void gf_dash_get_segment_duration(GF_MPD_Representation *rep, GF_MPD_Adap return; } + /*single segment*/ + if (rep->segment_base || set->segment_base || period->segment_base) { + *max_seg_duration = mpd->media_presentation_duration; + *max_seg_duration /= 1000; + *nb_segments = 1; + return; + } + single_segment = 1; if (period->segment_template) { single_segment = 0; @@ -657,6 +727,10 @@ static void gf_dash_resolve_duration(GF_MPD_Representation *rep, GF_MPD_Adaptati GF_MPD_SegmentTimelineEntry *ent = gf_list_get(segment_timeline->entries, 0); if (ent) *out_duration = ent->duration; } + else if (rep->segment_list) { + GF_MPD_SegmentURL *url = gf_list_get(rep->segment_list->segment_URLs, 0); + if (url && url->duration) *out_duration = url->duration; + } } static GF_Err gf_dash_merge_segment_timeline(GF_MPD_SegmentList *old_list, GF_MPD_SegmentTemplate *old_template, GF_MPD_SegmentList *new_list, GF_MPD_SegmentTemplate *new_template, Double min_start_time) @@ -935,6 +1009,7 @@ static GF_Err gf_dash_update_manifest(GF_DashClient *dash) for (group_idx=0; group_idxgroups); group_idx++) { GF_MPD_AdaptationSet *set, *new_set; GF_DASH_Group *group = gf_list_get(dash->groups, group_idx); + if (group->selection != GF_DASH_GROUP_SELECTED) continue; set = group->adaptation_set; new_set = gf_list_get(new_period->adaptation_sets, group_idx); @@ -1075,6 +1150,10 @@ static GF_Err gf_dash_update_manifest(GF_DashClient *dash) } } + if (new_mpd->availabilityStartTime != dash->mpd->availabilityStartTime) { + gf_dash_group_timeline_setup(new_mpd, group); + } + /*update number of segments in active rep*/ gf_dash_get_segment_duration(gf_list_get(group->adaptation_set->representations, group->active_rep_index), group->adaptation_set, group->period, new_mpd, &group->nb_segments_in_rep, NULL); @@ -1210,7 +1289,8 @@ typedef enum } GF_DASHURLResolveType; -GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Group *group, char *mpd_url, GF_DASHURLResolveType resolve_type, u32 item_index, char **out_url, u64 *out_range_start, u64 *out_range_end, u64 *segment_duration) + +GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Group *group, const char *mpd_url, GF_DASHURLResolveType resolve_type, u32 item_index, char **out_url, u64 *out_range_start, u64 *out_range_end, u64 *segment_duration, Bool *is_in_base_url) { GF_MPD_BaseURL *url_child; GF_MPD_SegmentTimeline *timeline = NULL; @@ -1225,6 +1305,15 @@ GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Grou *out_range_start = *out_range_end = 0; *out_url = NULL; + + if (!group->timeline_setup) { + gf_dash_group_timeline_setup(mpd, group); + group->timeline_setup = 1; + } + + + + /*resolve base URLs from document base (download location) to representation (media)*/ url = gf_strdup(mpd_url); @@ -1260,8 +1349,10 @@ GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Grou *segment_duration = (u32) ((Double) (*segment_duration) * 1000.0 / timescale); /*single URL*/ - if (rep->segment_base || set->segment_base || period->segment_base) { +// if (rep->segment_base || set->segment_base || period->segment_base) { + if (!rep->segment_list && !set->segment_list && !period->segment_list && !rep->segment_template && !set->segment_template && !period->segment_template) { GF_MPD_URL *res_url; + GF_MPD_SegmentBase *base_seg = NULL; if (item_index>0) return GF_EOS; switch (resolve_type) { case GF_DASH_RESOLVE_URL_MEDIA: @@ -1271,29 +1362,32 @@ GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Grou case GF_DASH_RESOLVE_URL_INIT: case GF_DASH_RESOLVE_URL_INDEX: res_url = NULL; - if (resolve_type == GF_DASH_RESOLVE_URL_INDEX) { - if (period->segment_base) res_url = period->segment_base->representation_index; - if (set->segment_base) res_url = set->segment_base->representation_index; - if (rep->segment_base) res_url = rep->segment_base->representation_index; - } else { - if (period->segment_base) res_url = period->segment_base->initialization_segment; - if (set->segment_base) res_url = set->segment_base->initialization_segment; - if (rep->segment_base) res_url = rep->segment_base->initialization_segment; - } - /*no initialization segment / index*/ - if (!res_url) { - gf_free(url); - return GF_OK; + base_seg = rep->segment_base; + if (!base_seg) base_seg = set->segment_base; + if (!base_seg) base_seg = period->segment_base; + + if (base_seg) { + if (resolve_type == GF_DASH_RESOLVE_URL_INDEX) { + res_url = base_seg->representation_index; + } else { + res_url = base_seg->initialization_segment; + } } - if (res_url->sourceURL) { + if (is_in_base_url) *is_in_base_url = 0; + /*no initialization segment / index, use base URL*/ + if (res_url && res_url->sourceURL) { *out_url = gf_url_concatenate(url, res_url->sourceURL); gf_free(url); } else { *out_url = url; + if (is_in_base_url) *is_in_base_url = 1; } - if (res_url->byte_range) { + if (res_url && res_url->byte_range) { *out_range_start = res_url->byte_range->start_range; *out_range_end = res_url->byte_range->end_range; + } else if (base_seg && base_seg->index_range && (resolve_type == GF_DASH_RESOLVE_URL_INDEX)) { + *out_range_start = base_seg->index_range->start_range; + *out_range_end = base_seg->index_range->end_range; } return GF_OK; default: @@ -1374,6 +1468,9 @@ GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Grou *out_range_start = segment->media_range->start_range; *out_range_end = segment->media_range->end_range; } + if (segment->duration) { + *segment_duration = (u32) ((Double) (segment->duration) * 1000.0 / timescale); + } return GF_OK; case GF_DASH_RESOLVE_URL_INDEX: if (item_index >= segment_count) { @@ -1574,7 +1671,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * } start_range = end_range = 0; - e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration); + e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL); if (e) { gf_mx_v(dash->dl_mutex); GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to resolve initialization URL: %s\n", gf_error_to_string(e) )); @@ -1583,7 +1680,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * /*no error and no init segment, go for media segment*/ if (!base_init_url) { - e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration); + e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL); if (e) { gf_mx_v(dash->dl_mutex); GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to resolve media URL: %s\n", gf_error_to_string(e) )); @@ -1594,7 +1691,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * group->dont_delete_first_segment = 1; } - if (!strstr(base_init_url, "://") || !strnicmp(base_init_url, "file://", 7) || !strnicmp(base_init_url, "views://", 8)) { + if (!strstr(base_init_url, "://") || !strnicmp(base_init_url, "file://", 7) || !strnicmp(base_init_url, "gmem://", 7) || !strnicmp(base_init_url, "views://", 8)) { assert(!group->nb_cached_segments); group->cached[0].cache = gf_strdup(base_init_url); group->cached[0].url = gf_strdup(base_init_url); @@ -1603,7 +1700,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * group->nb_cached_segments = 1; /*do not erase local files*/ - group->local_files = 1; + group->local_files = group->was_segment_base ? 0 : 1; group->download_segment_index += nb_segment_read; group->segment_local_url = group->cached[0].cache; group->local_url_start_range = start_range; @@ -1623,11 +1720,11 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * if (a_rep==rep) continue; if (a_rep->playback.disabled) continue; - e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur); + e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur, NULL); if (!e && a_base_init_url) { a_rep->playback.cached_init_segment_url = a_base_init_url; - rep->playback.init_start_range = a_start; - rep->playback.init_end_range =a_end ; + a_rep->playback.init_start_range = a_start; + a_rep->playback.init_end_range =a_end ; } else if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot solve initialization segment for representation: %s - discarding representation\n", gf_error_to_string(e) )); a_rep->playback.disabled = 1; @@ -1658,7 +1755,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * gf_free(base_init_url); - e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index + 1, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration); + e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index + 1, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL); if (!e) { gf_mx_v(dash->dl_mutex); return e; @@ -1761,7 +1858,7 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * if (a_rep==rep) continue; if (a_rep->playback.disabled) continue; - e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur); + e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur, NULL); if (!e && a_base_init_url) { e = gf_dash_download_resource(dash->dash_io, &(group->segment_download), a_base_init_url, a_start, a_end, 1, group); if (e) { @@ -1769,8 +1866,8 @@ static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group * a_rep->playback.disabled = 1; } else { a_rep->playback.cached_init_segment_url = gf_strdup( dash->dash_io->get_cache_name(dash->dash_io, group->segment_download) ); - rep->playback.init_start_range = 0; - rep->playback.init_end_range = 0; + a_rep->playback.init_start_range = 0; + a_rep->playback.init_end_range = 0; } gf_free(a_base_init_url); } else if (e) { @@ -1882,8 +1979,14 @@ GF_Err gf_dash_setup_groups(GF_DashClient *dash) for (j=0; jrepresentations); j++) { Double dur; u32 nb_seg; - gf_dash_get_segment_duration(gf_list_get(set->representations, j), set, period, dash->mpd, &nb_seg, &dur); + GF_MPD_Representation *rep = gf_list_get(set->representations, j); + gf_dash_get_segment_duration(rep, set, period, dash->mpd, &nb_seg, &dur); if (dur>seg_dur) seg_dur = dur; + + if (rep->width>set->max_width) { + set->max_width = rep->width; + set->max_height = rep->height; + } } if (!seg_dur) { @@ -1918,10 +2021,278 @@ GF_Err gf_dash_setup_groups(GF_DashClient *dash) return GF_OK; } +static GF_Err gf_dash_load_sidx(GF_BitStream *bs, GF_MPD_Representation *rep, Bool seperate_index, u64 sidx_offset) +{ +#ifdef GPAC_DISABLE_ISOM + return GF_NOT_SUPPORTED; +#else + u64 anchor_position, prev_pos; + GF_SegmentIndexBox *sidx = NULL; + u32 i, size, type; + GF_Err e; + u64 offset; + + prev_pos = gf_bs_get_position(bs); + gf_bs_seek(bs, sidx_offset); + size = gf_bs_read_u32(bs); + type = gf_bs_read_u32(bs); + if (type != GF_4CC('s','i','d','x')) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error parsing SIDX: type is %s (box start offset "LLD")\n", gf_4cc_to_str(type), gf_bs_get_position(bs)-8 )); + return GF_ISOM_INVALID_FILE; + } + + gf_bs_seek(bs, sidx_offset); + + anchor_position = sidx_offset + size; + if (seperate_index) + anchor_position = 0; + + e = gf_isom_parse_box((GF_Box **) &sidx, bs); + if (e) return e; + + GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Loading SIDX - %d entries - Earliest Presentation Time "LLD"\n", sidx->nb_refs, sidx->earliest_presentation_time)); + + offset = sidx->first_offset + anchor_position; + rep->segment_list->timescale = sidx->timescale; + for (i=0; inb_refs; i++) { + GF_MPD_SegmentURL *seg; + if (sidx->refs[i].reference_type) { + e = gf_dash_load_sidx(bs, rep, seperate_index, offset); + if (e) { + break; + } + } else { + GF_SAFEALLOC(seg, GF_MPD_SegmentURL); + GF_SAFEALLOC(seg->media_range, GF_MPD_ByteRange); + GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Found media segment size %d - duration %d - start with SAP: %d - SAP type %d - SAP Deltat Time %d\n", + sidx->refs[i].reference_size, sidx->refs[i].subsegment_duration, sidx->refs[i].starts_with_SAP, sidx->refs[i].SAP_type, sidx->refs[i].SAP_delta_time)); + + seg->media_range->start_range = offset; + offset += sidx->refs[i].reference_size; + seg->media_range->end_range = offset - 1; + seg->duration = sidx->refs[i].subsegment_duration; + gf_list_add(rep->segment_list->segment_URLs, seg); + } + } + gf_isom_box_del((GF_Box*)sidx); + gf_bs_seek(bs, prev_pos); + return e; +#endif +} + +static GF_Err gf_dash_load_representation_sidx(GF_DASH_Group *group, GF_MPD_Representation *rep, const char *cache_name, Bool seperate_index, Bool needs_mov_range) +{ + GF_Err e; + GF_BitStream *bs; + FILE *f=NULL; + if (!strncmp(cache_name, "gmem://", 7)) { + u32 size; + u8 *mem_address; + if (sscanf(cache_name, "gmem://%d@%p", &size, &mem_address) != 2) { + return GF_IO_ERR; + } + bs = gf_bs_new(mem_address, size, GF_BITSTREAM_READ); + } else { + FILE *f = gf_f64_open(cache_name, "rb"); + if (!f) return GF_IO_ERR; + bs = gf_bs_from_file(f, GF_BITSTREAM_READ); + } + e = GF_OK; + while (gf_bs_available(bs)) { + u32 size = gf_bs_read_u32(bs); + u32 type = gf_bs_read_u32(bs); + if (type != GF_4CC('s','i','d','x')) { + gf_bs_skip_bytes(bs, size-8); + + if (needs_mov_range && (type==GF_4CC('m','o','o','v') )) { + GF_SAFEALLOC(rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange); + rep->segment_list->initialization_segment->byte_range->end_range = gf_bs_get_position(bs); + } + continue; + } + gf_bs_seek(bs, gf_bs_get_position(bs)-8); + e = gf_dash_load_sidx(bs, rep, seperate_index, gf_bs_get_position(bs) ); + + /*we could also parse the sub sidx*/ + break; + } + gf_bs_del(bs); + if (f) fclose(f); + return e; +} + +static GF_Err dash_load_box_type(const char *cache_name, u32 offset, u32 *box_type, u32 *box_size) +{ + *box_type = *box_size = 0; + if (!strncmp(cache_name, "gmem://", 7)) { + u32 size; + u8 *mem_address; + if (sscanf(cache_name, "gmem://%d@%p", &size, &mem_address) != 2) { + return GF_IO_ERR; + } + if (offset+8 > size) + return GF_IO_ERR; + mem_address+=offset; + *box_size = GF_4CC(mem_address[0], mem_address[1], mem_address[2], mem_address[3]); + *box_type = GF_4CC(mem_address[4], mem_address[5], mem_address[6], mem_address[7]); + } else { + unsigned char data[4]; + FILE *f = gf_f64_open(cache_name, "rb"); + if (!f) return GF_IO_ERR; + if (gf_f64_seek(f, offset, SEEK_SET)) + return GF_IO_ERR; + if (fread(data, 1, 4, f) == 4) { + *box_size = GF_4CC(data[0], data[1], data[2], data[3]); + if (fread(data, 1, 4, f) == 4) { + *box_type = GF_4CC(data[0], data[1], data[2], data[3]); + } + } + fclose(f); + } + return GF_OK; +} + +static GF_Err gf_dash_setup_single_index_mode(GF_DASH_Group *group) +{ + u32 i; + GF_Err e; + char *init_url = NULL; + char *index_url = NULL; + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, 0); + + if (rep->segment_template || group->adaptation_set->segment_template || group->period->segment_template) return GF_OK; + if (rep->segment_list || group->adaptation_set->segment_list || group->period->segment_list) return GF_OK; + + /*OK we are in single-file mode, download all required indexes & co*/ + for (i=0; iadaptation_set->representations); i++) { + char *sidx_file = NULL; + u64 duration, index_start_range, index_end_range, init_start_range, init_end_range; + Bool index_in_base, init_in_base; + Bool init_needs_byte_range = 0; + Bool has_seen_sidx = 0; + Bool is_isom = 1; + rep = gf_list_get(group->adaptation_set->representations, i); + + index_in_base = init_in_base = 0; + e = gf_dash_resolve_url(group->dash->mpd, rep, group, group->dash->base_url, GF_DASH_RESOLVE_URL_INIT, 0, &init_url, &init_start_range, &init_end_range, &duration, &init_in_base); + if (e) goto exit; + + e = gf_dash_resolve_url(group->dash->mpd, rep, group, group->dash->base_url, GF_DASH_RESOLVE_URL_INDEX, 0, &index_url, &index_start_range, &index_end_range, &duration, &index_in_base); + if (e) goto exit; + + + if (is_isom && (init_in_base || index_in_base)) { + if (!strstr(init_url, "://") || (!strnicmp(init_url, "file://", 7) || !strnicmp(init_url, "views://", 7)) ) { + GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList); + rep->segment_list->segment_URLs =gf_list_new(); + + if (init_in_base) { + GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL); + rep->segment_list->initialization_segment->sourceURL = gf_strdup(init_url); + /*we don't want to load the entire movie */ + init_needs_byte_range = 1; + } + if (index_in_base) { + sidx_file = (char *)init_url; + } + } + /*we need to download the init segement, at least partially*/ + else { + u32 offset = 0; + u32 box_type=0; + u32 box_size=0; + const char *cache_name; + + GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Downloading init segment and SIDX for representation %s\n", init_url)); + + /*download first 8 bytes and check if we do have a box starting there*/ + e = gf_dash_download_resource(group->dash->dash_io, &(group->segment_download), init_url, offset, 7, 1, group); + if (e) goto exit; + cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, group->segment_download); + e = dash_load_box_type(cache_name, offset, &box_type, &box_size); + offset=8; + while (box_type) { + /*we got the moov , stop here */ + if (!index_in_base && (box_type==GF_4CC('m','o','o','v'))) { + e = gf_dash_download_resource(group->dash->dash_io, &(group->segment_download), init_url, offset, offset+box_size-8, 2, group); + break; + } else { + e = gf_dash_download_resource(group->dash->dash_io, &(group->segment_download), init_url, offset, offset+box_size-1, 2, group); + offset += box_size; + /*we need to refresh the cache name because of our memory astorage thing ...*/ + cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, group->segment_download); + e = dash_load_box_type(cache_name, offset-8, &box_type, &box_size); + + if (box_type==GF_4CC('s','i','d','x')) + has_seen_sidx = 1; + else if (has_seen_sidx) + break; + + + } + } + if (e<0) goto exit; + + if (box_type==0) { + e = GF_ISOM_INVALID_FILE; + goto exit; + } + GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Done downloading init segment and SIDX\n")); + + GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList); + rep->segment_list->segment_URLs =gf_list_new(); + + cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, group->segment_download); + if (init_in_base) { + GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL); + rep->segment_list->initialization_segment->sourceURL = gf_strdup(cache_name); + } + if (index_in_base) { + sidx_file = (char *)cache_name; + } + } + } + /*we have index url, download it*/ + if (! index_in_base) { + e = gf_dash_download_resource(group->dash->dash_io, &(group->segment_download), index_url, index_start_range, index_end_range, 1, group); + if (e) goto exit; + sidx_file = (char *)group->dash->dash_io->get_cache_name(group->dash->dash_io, group->segment_download); + } + + /*load sidx*/ + e = gf_dash_load_representation_sidx(group, rep, sidx_file, !index_in_base, init_needs_byte_range); + if (e) { + rep->playback.disabled = 1; + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to load segment index for this representation - disabling\n")); + } + + /*reset all seg based stuff*/ + if (rep->segment_base) { + gf_mpd_segment_base_free(rep->segment_base); + rep->segment_base = NULL; + } + + gf_free(index_url); + index_url = NULL; + gf_free(init_url); + init_url = NULL; + } + if (group->adaptation_set->segment_base) { + gf_mpd_segment_base_free(group->adaptation_set->segment_base); + group->adaptation_set->segment_base = NULL; + } + group->was_segment_base = 1; + +exit: + if (init_url) gf_free(init_url); + if (index_url) gf_free(index_url); + return e; +} + static GF_Err gf_dash_setup_period(GF_DashClient *dash) { + GF_MPD_Period *period; u32 rep_i, group_i, nb_groups_ok; - /*setup all groups*/ gf_dash_setup_groups(dash); @@ -1930,6 +2301,7 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) GF_MPD_Representation *rep_sel; u32 active_rep, nb_rep; const char *mime_type; + u32 nb_rep_ok = 0; GF_DASH_Group *group = gf_list_get(dash->groups, group_i); nb_rep = gf_list_count(group->adaptation_set->representations); @@ -1943,6 +2315,9 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) continue; } + /*translate from single-indexed file to SegmentList*/ + gf_dash_setup_single_index_mode(group); + /* Select the appropriate representation in the given period */ active_rep = 0; for (rep_i = 0; rep_i < nb_rep; rep_i++) { @@ -1952,7 +2327,8 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) if (rep_i) { Bool ok; char *sep; - if ( !rep->codecs || !rep_sel->codecs ) continue; + if ( !rep->codecs || !rep_sel->codecs) continue; + sep = strchr(rep_sel->codecs, '.'); if (sep) sep[0] = 0; ok = !strnicmp(rep->codecs, rep_sel->codecs, strlen(rep_sel->codecs) ); @@ -1986,10 +2362,11 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) } break; case GF_DASH_SELECT_QUALITY_HIGHEST: + /*fallthrough if quality is not indicated*/ if (rep->quality_ranking > rep_sel->quality_ranking) { active_rep = rep_i; break; - }/*fallthrough if quality is not indicated*/ + } case GF_DASH_SELECT_BANDWIDTH_HIGHEST: if (rep->bandwidth > rep_sel->bandwidth) { active_rep = rep_i; @@ -1999,8 +2376,20 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) break; } } + for (rep_i = 0; rep_i < nb_rep; rep_i++) { + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_i); + if (!rep->playback.disabled) + nb_rep_ok++; + } + + if (! nb_rep_ok) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No valid representation in this group - disabling\n")); + group->selection = GF_DASH_GROUP_NOT_SELECTABLE; + continue; + } rep_sel = gf_list_get(group->adaptation_set->representations, active_rep); + gf_dash_set_group_representation(group, rep_sel); if (dash->playback_start_range>=0) @@ -2026,6 +2415,14 @@ static GF_Err gf_dash_setup_period(GF_DashClient *dash) group->selection = GF_DASH_GROUP_NOT_SELECTED; nb_groups_ok++; } + + period = gf_list_get(dash->mpd->periods, dash->active_period_index); + + if (period->segment_base) { + gf_mpd_segment_base_free(period->segment_base); + period->segment_base = NULL; + } + if (!nb_groups_ok) { GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No AdaptationSet could be selected in the MPD - Cannot play\n")); return GF_NON_COMPLIANT_BITSTREAM; @@ -2059,6 +2456,14 @@ restart_period: dash->in_period_setup = 1; + /*setup period*/ + e = gf_dash_setup_period(dash); + if (e) { + dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_PERIOD_SETUP_ERROR, e); + ret = 1; + goto exit; + } + dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SELECT_GROUPS, GF_OK); e = GF_OK; @@ -2108,9 +2513,8 @@ restart_period: /*wait until next segment is needed*/ while (!dash->mpd_stop_request) { u32 timer = gf_sys_clock() - dash->last_update_time; - Bool shouldParsePlaylist = dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period); - if (shouldParsePlaylist) { + if (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period)) { GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Time to update the playlist (%u ms ellapsed since last refresh and min reoad rate is %u)\n", timer, dash->mpd->minimum_update_period)); e = gf_dash_update_manifest(dash); group_count = gf_list_count(dash->groups); @@ -2140,13 +2544,12 @@ restart_period: if (dash->request_period_switch == 1) dash->active_period_index++; - gf_dash_setup_period(dash); dash->request_period_switch = 0; goto restart_period; } - gf_sleep(16); + gf_sleep(30); } } @@ -2178,8 +2581,16 @@ restart_period: we need to check if a new playlist is ready */ if (group->nb_segments_in_rep && (group->download_segment_index>=group->nb_segments_in_rep)) { u32 timer = gf_sys_clock() - dash->last_update_time; + Bool update_playlist = 0; /* update of the playlist, only if indicated */ - if (dash->mpd->minimum_update_period && timer > dash->mpd->minimum_update_period) { + if (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period)) { + update_playlist = 1; + } + /* if media_presentation_duration is 0 and we are in live, force a refresh (not in the spec but safety check*/ + else if ((dash->mpd->type==GF_MPD_TYPE_DYNAMIC) && !dash->mpd->media_presentation_duration) { + update_playlist = 1; + } + if (update_playlist) { GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Last segment in current playlist downloaded, checking updates after %u ms\n", timer)); e = gf_dash_update_manifest(dash); if (e) { @@ -2192,8 +2603,8 @@ restart_period: } /* Now that the playlist is up to date, we can check again */ if (group->download_segment_index >= group->nb_segments_in_rep) { - if (dash->mpd->minimum_update_period) { - /* if there is a specified update period, we redo the whole process */ + /* if there is a specified update period, we redo the whole process */ + if (dash->mpd->minimum_update_period ) { continue; } else { /* if not, we are really at the end of the playlist, we can quit */ @@ -2205,10 +2616,16 @@ restart_period: } gf_mx_p(dash->dl_mutex); - /*todo for live - check we don't attempt to request segments before their availabilityStartTime !*/ + if (group->force_switch_bandwidth && !dash->auto_switch_count) { + gf_dash_switch_group_representation(dash, group); + /*restart*/ + i--; + gf_mx_v(dash->dl_mutex); + continue; + } /* At this stage, there are some segments left to be downloaded */ - e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index, &new_base_seg_url, &start_range, &end_range, &group->current_downloaded_segment_duration); + e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_DASH_RESOLVE_URL_MEDIA, group->download_segment_index, &new_base_seg_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL); gf_mx_v(dash->dl_mutex); if (e) { /*do something!!*/ @@ -2221,7 +2638,7 @@ restart_period: } /*local file*/ - if (!strstr(new_base_seg_url, "://") || !strnicmp(new_base_seg_url, "file://", 7)) { + if (!strstr(new_base_seg_url, "://") || (!strnicmp(new_base_seg_url, "file://", 7) || !strnicmp(new_base_seg_url, "gmem://", 7)) ) { resource_name = local_file_name = (char *) new_base_seg_url; e = GF_OK; /*do not erase local files*/ @@ -2565,7 +2982,6 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) GF_Err e; GF_MPD_Period *period; GF_DOMParser *mpd_parser; - Bool is_m3u8 = 0; Bool is_local = 0; if (!dash || !manifest_url) return GF_BAD_PARAM; @@ -2592,7 +3008,7 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) local_url = manifest_url + 7; is_local = 1; if (strstr(manifest_url, ".m3u8")) { - is_m3u8 = 1; + dash->is_m3u8 = 1; } } else if (strstr(manifest_url, "://")) { const char *reloc_url, *mtype; @@ -2612,7 +3028,7 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) reloc_url = dash->dash_io->get_url(dash->dash_io, dash->mpd_dnload); /* Some servers, for instance http://tv.freebox.fr, serve m3u8 as text/plain */ if (gf_dash_is_m3u8_mime(mime) || strstr(reloc_url, ".m3u8") || strstr(reloc_url, ".M3U8")) { - is_m3u8 = 1; + dash->is_m3u8 = 1; } else if (!gf_dash_is_dash_mime(mime) && !strstr(reloc_url, ".mpd") && !strstr(reloc_url, ".MPD")) { GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] mime '%s' for '%s' should be m3u8 or mpd\n", mime, reloc_url)); dash->dash_io->del(dash->dash_io, dash->mpd_dnload); @@ -2631,7 +3047,7 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) local_url = manifest_url; is_local = 1; if (strstr(manifest_url, ".m3u8")) - is_m3u8 = 1; + dash->is_m3u8 = 1; } if (is_local) { @@ -2640,7 +3056,7 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) fclose(f); } - if (is_m3u8) { + if (dash->is_m3u8) { if (is_local) { char *sep; strcpy(local_path, local_url); @@ -2707,11 +3123,8 @@ GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url) goto exit; } - e = gf_dash_setup_period(dash); - if (e) goto exit; - e = gf_th_run(dash->dash_thread, dash_main_thread_proc, dash); - gf_sleep(0); + return e; exit: dash->dash_io->del(dash->dash_io, dash->mpd_dnload); @@ -2754,7 +3167,7 @@ GF_DashClient *gf_dash_new(GF_DASHFileIO *dash_io, u32 max_cache_duration_sec, u dash->dash_thread = gf_th_new("MPD Segment Downloader Thread"); dash->dl_mutex = gf_mx_new("MPD Segment Downloader Mutex"); - dash->mimeTypeForM3U8Segments = gf_strdup( M3U8_UNKOWN_MIME_TYPE ); + dash->mimeTypeForM3U8Segments = gf_strdup( "video/mp2t" ); dash->max_cache_duration = max_cache_duration_sec; @@ -2823,46 +3236,67 @@ void gf_dash_get_info(GF_DashClient *dash, const char **title, const char **sour } GF_EXPORT -void gf_dash_switch_quality(GF_DashClient *dash, Bool switch_up) +void gf_dash_switch_quality(GF_DashClient *dash, Bool switch_up, Bool immediate_switch) { u32 i; for (i=0; igroups); i++) { - Bool do_switch = 0; + u32 switch_to_rep_idx = 0; + u32 bandwidth, quality, k; + GF_MPD_Representation *rep, *active_rep; GF_DASH_Group *group = gf_list_get(dash->groups, i); u32 current_idx = group->active_rep_index; if (group->selection != GF_DASH_GROUP_SELECTED) continue; if (group->force_representation_idx_plus_one) current_idx = group->force_representation_idx_plus_one - 1; - if (switch_up) { - if (current_idx + 1 < gf_list_count(group->adaptation_set->representations)) { - group->force_representation_idx_plus_one = 1 + current_idx+1; - do_switch = 1; - } - } else { - if (current_idx) { - group->force_representation_idx_plus_one = 1 + current_idx - 1; - do_switch = 1; + + active_rep = gf_list_get(group->adaptation_set->representations, current_idx); + if (!active_rep) continue; + bandwidth = switch_up ? (u32) -1 : 0; + quality = switch_up ? (u32) -1 : 0; + + for (k=0; kadaptation_set->representations); k++) { + rep = gf_list_get(group->adaptation_set->representations, k); + if (switch_up) { + if ((rep->quality_ranking>active_rep->quality_ranking) || (rep->bandwidth>active_rep->bandwidth)) { + if ((rep->quality_ranking < quality) || (rep->bandwidth < bandwidth)) { + bandwidth = rep->bandwidth; + quality = rep->quality_ranking; + switch_to_rep_idx = k+1; + } + } + } else { + if ((rep->quality_ranking < active_rep->quality_ranking) || (rep->bandwidth < active_rep->bandwidth)) { + if ((rep->quality_ranking > quality) || (rep->bandwidth > bandwidth)) { + bandwidth = rep->bandwidth; + quality = rep->quality_ranking; + switch_to_rep_idx = k+1; + } + } } } - if (do_switch) { + if (switch_to_rep_idx && (switch_to_rep_idx-1 != current_idx) ) { gf_mx_p(dash->dl_mutex); group->force_switch_bandwidth = 1; - /*in local playback just switch at the end of the current segment - for remote, we should let the user decide*/ - while (group->nb_cached_segments>1) { - group->nb_cached_segments--; - gf_free(group->cached[group->nb_cached_segments].url); - group->cached[group->nb_cached_segments].url = NULL; - if (!group->local_files && group->cached[group->nb_cached_segments].cache) { - gf_delete_file( group->cached[group->nb_cached_segments].cache ); - gf_free(group->cached[group->nb_cached_segments].cache); - group->cached[group->nb_cached_segments].cache = NULL; + group->force_representation_idx_plus_one = switch_to_rep_idx; + + if (group->local_files || immediate_switch) { + /*in local playback just switch at the end of the current segment + for remote, we should let the user decide*/ + while (group->nb_cached_segments>1) { + group->nb_cached_segments--; + gf_free(group->cached[group->nb_cached_segments].url); + group->cached[group->nb_cached_segments].url = NULL; + if (!group->local_files && group->cached[group->nb_cached_segments].cache) { + gf_delete_file( group->cached[group->nb_cached_segments].cache ); + gf_free(group->cached[group->nb_cached_segments].cache); + group->cached[group->nb_cached_segments].cache = NULL; + } + group->cached[group->nb_cached_segments].representation_index = 0; + group->cached[group->nb_cached_segments].start_range = 0; + group->cached[group->nb_cached_segments].end_range = 0; + if (group->download_segment_index>1) + group->download_segment_index--; } - group->cached[group->nb_cached_segments].representation_index = 0; - group->cached[group->nb_cached_segments].start_range = 0; - group->cached[group->nb_cached_segments].end_range = 0; - assert(group->download_segment_index>1); - group->download_segment_index--; } gf_mx_v(dash->dl_mutex); } @@ -3075,7 +3509,7 @@ GF_Err gf_dash_group_get_presentation_time_offset(GF_DashClient *dash, u32 idx, } GF_EXPORT -GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, const char **url, u64 *start_range, u64 *end_range, const char **switching_url, u64 *switching_start_range, u64 *switching_end_range, const char **original_url) +GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, const char **url, u64 *start_range, u64 *end_range, s32 *switching_index, const char **switching_url, u64 *switching_start_range, u64 *switching_end_range, const char **original_url) { GF_DASH_Group *group; @@ -3086,6 +3520,7 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, con if (switching_start_range) *switching_start_range = 0; if (switching_end_range) *switching_end_range = 0; if (original_url) *original_url = NULL; + if (switching_index) *switching_index = -1; gf_mx_p(dash->dl_mutex); group = gf_list_get(dash->groups, idx); @@ -3102,6 +3537,8 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, con if (group->cached[0].representation_index != group->prev_active_rep_index) { GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->cached[0].representation_index); + if (switching_index) + *switching_index = group->cached[0].representation_index; if (switching_start_range) *switching_start_range = rep->playback.init_start_range; if (switching_end_range) @@ -3149,5 +3586,35 @@ Double gf_dash_group_current_segment_start_time(GF_DashClient *dash, u32 idx) return gf_dash_get_segment_start_time(group); } +GF_EXPORT +GF_Err gf_dash_group_get_video_info(GF_DashClient *dash, u32 idx, u32 *max_width, u32 *max_height) +{ + GF_DASH_Group *group = gf_list_get(dash->groups, idx); + if (!group || !max_width || !max_height) return GF_BAD_PARAM; + + *max_width = group->adaptation_set->max_width; + *max_height = group->adaptation_set->max_height; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_dash_group_get_representation_info(GF_DashClient *dash, u32 idx, u32 representation_idx, u32 *width, u32 *height, u32 *audio_samplerate, u32 *bandwidth, const char **codecs) +{ + GF_DASH_Group *group = gf_list_get(dash->groups, idx); + GF_MPD_Representation *rep; + if (!group) return GF_BAD_PARAM; + rep = gf_list_get(group->adaptation_set->representations, representation_idx); + if (!rep) return GF_BAD_PARAM; + + if (width) *width = rep->width ? rep->width : group->adaptation_set->width; + if (height) *width = rep->height ? rep->height : group->adaptation_set->height; + if (codecs) *codecs = rep->codecs ? rep->codecs : group->adaptation_set->codecs; + if (bandwidth) *bandwidth = rep->bandwidth; + if (audio_samplerate) *audio_samplerate = rep->samplerate ? rep->samplerate : group->adaptation_set->samplerate; + + return GF_OK; +} + + #endif //GPAC_DISABLE_DASH_CLIENT diff --git a/src/media_tools/dash_segmenter.c b/src/media_tools/dash_segmenter.c index f82e725..476f274 100644 --- a/src/media_tools/dash_segmenter.c +++ b/src/media_tools/dash_segmenter.c @@ -52,7 +52,7 @@ struct _dash_component /*for audio*/ u32 sample_rate, channels; /*for anything*/ - char szLang[4]; + char szLang[5]; }; typedef struct @@ -68,6 +68,7 @@ typedef struct s32 time_shift_depth; Double subduration; const char *bs_switch_segment_file; + Bool inband_param_set; /*set if seg_rad_name depends on input file name (had %s in it). In this case, SegmentTemplate cannot be used at adaptation set level*/ Bool variable_seg_rad_name; @@ -85,6 +86,7 @@ struct _dash_segment_input char *file_name; char representationID[100]; char periodID[100]; + char role[100]; u32 bandwidth; /*if 0, input is disabled*/ @@ -100,7 +102,7 @@ struct _dash_segment_input /*assigns the different media to the same adaptation set or group than the input_idx one*/ GF_Err (* dasher_input_classify) (GF_DashSegInput *dash_inputs, u32 nb_dash_inputs, u32 input_idx, u32 *current_group_id, u32 *max_sap_type); GF_Err ( *dasher_get_components_info) (GF_DashSegInput *dash_input, GF_DASHSegmenterOptions *opts); - GF_Err ( *dasher_create_init_segment) (GF_DashSegInput *dash_inputs, u32 nb_dash_inputs, u32 adaptation_set, char *szInitName, const char *tmpdir, Bool *disable_bs_switching); + GF_Err ( *dasher_create_init_segment) (GF_DashSegInput *dash_inputs, u32 nb_dash_inputs, u32 adaptation_set, char *szInitName, const char *tmpdir, Bool use_inband_param_set, Bool *disable_bs_switching); GF_Err ( *dasher_segment_file) (GF_DashSegInput *dash_input, const char *szOutName, GF_DASHSegmenterOptions *opts, Bool first_in_set); /*shall be set after call to dasher_input_classify*/ @@ -285,6 +287,8 @@ GF_Err gf_media_get_rfc_6381_codec_name(GF_ISOFile *movie, u32 track, char *szCo case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: case GF_ISOM_SUBTYPE_SVC_H264: avcc = gf_isom_avc_config_get(movie, track, 1); sps = gf_list_get(avcc->sequenceParameterSets, 0); @@ -315,7 +319,7 @@ typedef struct Bool done; u32 TrackID; u32 SampleNum, SampleCount; - u32 FragmentLength; + u64 FragmentLength; u32 OriginalTrack; u32 finalSampleDescriptionIndex; u32 TimeScale, MediaType, DefaultDuration, InitialTSOffset; @@ -363,7 +367,7 @@ static GF_Err gf_media_isom_segment_file(GF_ISOFile *input, const char *output_f GF_ISOFile *output, *bs_switch_segment; GF_ISOSample *sample, *next; GF_List *fragmenters; - u32 MaxFragmentDuration, MaxSegmentDuration, SegmentDuration, maxFragDurationOverSegment; + u64 MaxFragmentDuration, MaxSegmentDuration, SegmentDuration, maxFragDurationOverSegment; u32 presentationTimeOffset = 0; Double segment_start_time, file_duration, period_duration, max_segment_duration; u32 nb_segments, width, height, sample_rate, nb_channels, sar_w, sar_h, fps_num, fps_denum, startNumber, startNumberRewind; @@ -383,7 +387,8 @@ static GF_Err gf_media_isom_segment_file(GF_ISOFile *input, const char *output_f u32 tfref_timescale = 0; u32 bandwidth = 0; GF_ISOMTrackFragmenter *tf, *tfref; - FILE *mpd_segs = NULL; + GF_BitStream *mpd_bs = NULL; + char szMPDTempLine[2048]; char SegmentName[GF_MAX_PATH]; char RepSecName[200]; char RepURLsSecName[200]; @@ -503,12 +508,12 @@ static GF_Err gf_media_isom_segment_file(GF_ISOFile *input, const char *output_f opt = gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, "Bandwidth"); if (opt) sscanf(opt, "%u", &bandwidth); } - mpd_segs = gf_temp_file_new(); + mpd_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); } else { output = gf_isom_open(output_file, GF_ISOM_OPEN_WRITE, NULL); if (!output) return gf_isom_last_error(NULL); } - + nb_sync = 0; nb_samp = 0; fragmenters = gf_list_new(); @@ -561,6 +566,7 @@ static GF_Err gf_media_isom_segment_file(GF_ISOFile *input, const char *output_f if (mtype == GF_ISOM_MEDIA_HINT) continue; if (! dash_moov_setup) { + u32 avctype; e = gf_isom_clone_track(input, i+1, output, 0, &TrackNum); if (e) goto err_exit; @@ -576,6 +582,16 @@ static GF_Err gf_media_isom_segment_file(GF_ISOFile *input, const char *output_f continue; } + avctype = gf_isom_get_avc_svc_type(input, i+1, 1); + if (avctype==GF_ISOM_AVCTYPE_AVC_ONLY) { + /*for AVC we concatenate SPS/PPS*/ + if (dash_cfg->inband_param_set) + gf_isom_set_nalu_extract_mode(input, i+1, GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG); + } + else if (avctype > GF_ISOM_AVCTYPE_AVC_ONLY) { + /*for SVC we don't want any rewrite of extractors, and we don't concatenate SPS/PPS*/ + gf_isom_set_nalu_extract_mode(input, i+1, GF_ISOM_NALU_EXTRACT_INSPECT); + } } else { TrackNum = gf_isom_get_track_by_id(output, gf_isom_get_track_id(input, i+1)); count = gf_isom_get_sample_count(input, i+1); @@ -824,7 +840,8 @@ restart_fragmentation_pass: for (i=0; idash_ctx, RepURLsSecName, i); opt = gf_cfg_get_key(dash_cfg->dash_ctx, RepURLsSecName, key_name); - fprintf(mpd_segs, " %s\n", opt); + sprintf(szMPDTempLine, " %s\n", opt); + gf_bs_write_data(mpd_bs, szMPDTempLine, strlen(szMPDTempLine)); } opt = gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, "NextSegmentIndex"); @@ -834,18 +851,21 @@ restart_fragmentation_pass: opt = gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, "CumulatedDuration"); if (opt) period_duration = atof(opt); - sprintf(sKey, "TKID_%d_NextSampleNum", tf->TrackID); - opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); - if (opt) tf->SampleNum = atoi(opt); + for (i=0; iTrackID); + opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); + if (opt) tf->SampleNum = atoi(opt); - sprintf(sKey, "TKID_%d_LastSampleCTS", tf->TrackID); - opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); - if (opt) sscanf(opt, LLU, &tf->last_sample_cts); + sprintf(sKey, "TKID_%d_LastSampleCTS", tf->TrackID); + opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); + if (opt) sscanf(opt, LLU, &tf->last_sample_cts); - sprintf(sKey, "TKID_%d_NextSampleDTS", tf->TrackID); - opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); - if (opt) { - sscanf(opt, LLU, &tf->next_sample_dts); + sprintf(sKey, "TKID_%d_NextSampleDTS", tf->TrackID); + opt = (char *)gf_cfg_get_key(dash_cfg->dash_ctx, RepSecName, sKey); + if (opt) { + sscanf(opt, LLU, &tf->next_sample_dts); + } } } gf_isom_set_next_moof_number(output, fragment_index); @@ -886,7 +906,8 @@ restart_fragmentation_pass: if (!use_url_template) { const char *name = gf_url_get_resource_name(SegmentName); - fprintf(mpd_segs, " \n", name ); + sprintf(szMPDTempLine, " \n", name ); + gf_bs_write_data(mpd_bs, szMPDTempLine, strlen(szMPDTempLine)); if (dash_cfg->dash_ctx) { char szKey[100], szVal[4046]; sprintf(szKey, "UrlInfo%d", cur_seg ); @@ -988,11 +1009,11 @@ restart_fragmentation_pass: if (tf->splitable) { if (tfref==tf) { - u32 frag_dur = (tf->FragmentLength + defaultDuration) * 1000 / tf->TimeScale; + u64 frag_dur = (tf->FragmentLength + defaultDuration) * 1000 / tf->TimeScale; /*if media segment about to be produced is longer than max segment length, force segment split*/ if (SegmentDuration + frag_dur > MaxSegmentDuration) { split_sample_duration = defaultDuration; - defaultDuration = tf->TimeScale * (MaxSegmentDuration - SegmentDuration) / 1000 - tf->FragmentLength; + defaultDuration = (u32) (tf->TimeScale * (MaxSegmentDuration - SegmentDuration) / 1000 - tf->FragmentLength); split_sample_duration -= defaultDuration; } } else if ((tf->last_sample_cts + defaultDuration) * tfref_timescale > tfref->next_sample_dts * tf->TimeScale) { @@ -1083,7 +1104,7 @@ restart_fragmentation_pass: } else if (split_seg_at_rap) { u64 next_sap_time; - u32 frag_dur, next_dur; + u64 frag_dur, next_dur; next_dur = gf_isom_get_sample_duration(input, tf->OriginalTrack, tf->SampleNum + 1); if (!next_dur) next_dur = defaultDuration; /*duration of fragment if we add this rap*/ @@ -1177,7 +1198,7 @@ restart_fragmentation_pass: segment_start_time += SegmentDuration; nb_segments++; if (max_segment_duration * 1000 <= SegmentDuration) { - max_segment_duration = SegmentDuration; + max_segment_duration = (Double) (s64) SegmentDuration; max_segment_duration /= 1000; } force_switch_segment=0; @@ -1198,11 +1219,14 @@ restart_fragmentation_pass: file_size = gf_isom_get_file_size(output); end_range = file_size - 1; if (dash_cfg->single_file_mode!=1) { - fprintf(mpd_segs, " \n"); + gf_bs_write_data(mpd_bs, "/>\n", 3); + if (dash_cfg->dash_ctx) { char szKey[100], szVal[4046]; sprintf(szKey, "UrlInfo%d", cur_seg ); @@ -1246,14 +1270,12 @@ restart_fragmentation_pass: } if (dash_cfg) { - char buffer[1000]; - /*flush last segment*/ if (!switch_segment) { u64 idx_start_range, idx_end_range; if (max_segment_duration * 1000 <= SegmentDuration) { - max_segment_duration = SegmentDuration; + max_segment_duration = (Double) (s64) SegmentDuration; max_segment_duration /= 1000; } @@ -1266,11 +1288,13 @@ restart_fragmentation_pass: file_size = gf_isom_get_file_size(output); end_range = file_size - 1; if (dash_cfg->single_file_mode!=1) { - fprintf(mpd_segs, " \n"); + gf_bs_write_data(mpd_bs, "/>\n", 3); if (dash_cfg->dash_ctx) { char szKey[100], szVal[4046]; @@ -1405,11 +1429,12 @@ restart_fragmentation_pass: fprintf(dash_cfg->mpd, "/>\n"); } } - - gf_f64_seek(mpd_segs, 0, SEEK_SET); - while (!feof(mpd_segs)) { - u32 r = fread(buffer, 1, 100, mpd_segs); - gf_fwrite(buffer, 1, r, dash_cfg->mpd); + if (mpd_bs) { + char *mpd_seg_info = NULL; + u32 size; + gf_bs_get_content(mpd_bs, &mpd_seg_info, &size); + gf_fwrite(mpd_seg_info, 1, size, dash_cfg->mpd); + gf_free(mpd_seg_info); } if (!use_url_template && (dash_cfg->single_file_mode!=1)) { @@ -1480,7 +1505,7 @@ err_exit: if (!bs_switching_is_output && bs_switch_segment) gf_isom_delete(bs_switch_segment); gf_set_progress("ISO File Fragmenting", nb_samp, nb_samp); - if (mpd_segs) fclose(mpd_segs); + if (mpd_bs) gf_bs_del(mpd_bs); return e; } @@ -1544,6 +1569,9 @@ static GF_Err dasher_isom_classify_input(GF_DashSegInput *dash_inputs, u32 nb_da if (strcmp(dash_inputs[input_idx].szMime, dash_inputs[i].szMime)) continue; + if (strcmp(dash_inputs[input_idx].role, dash_inputs[i].role)) + continue; + in = gf_isom_open(dash_inputs[i].file_name, GF_ISOM_OPEN_READ, NULL); for (j=0; jsequenceParameterSets, l); gf_avc_get_sps_info(slc_orig->data, slc_orig->size, &sps_id2, NULL, NULL, NULL, NULL); if (sps_id2==sps_id1) { - do_merge = 0; + merge_mode = 0; break; } } } #endif /*no conflicts in SPS ids, merge all SPS in a single sample desc*/ - if (do_merge) { + if (merge_mode==1) { while (gf_list_count(avccfg1->sequenceParameterSets)) { GF_AVCConfigSlot *slc = gf_list_get(avccfg1->sequenceParameterSets, 0); gf_list_rem(avccfg1->sequenceParameterSets, 0); @@ -1721,7 +1759,7 @@ static GF_Err dasher_isom_create_init_segment(GF_DashSegInput *dash_inputs, u32 } /*cannot merge, clone*/ - if (!do_merge) + if (merge_mode==0) gf_isom_clone_sample_description(init_seg, track, in, j+1, 1, NULL, NULL, &outDescIndex); } } else { @@ -1731,6 +1769,18 @@ static GF_Err dasher_isom_create_init_segment(GF_DashSegInput *dash_inputs, u32 gf_isom_clone_track(in, j+1, init_seg, 0, &track); + switch (gf_isom_get_media_subtype(in, j+1, 1)) { + case GF_4CC( 'a', 'v', 'c', '1'): + case GF_4CC( 'a', 'v', 'c', '2'): + case GF_4CC( 's', 'v', 'c', '1'): + if (use_inband_param_set) { + gf_isom_avc_set_inband_config(init_seg, track, 1); + use_avc3 = 1; + } + break; + } + + gf_isom_get_fragment_defaults(in, j+1, &defaultDuration, &defaultSize, &defaultDescriptionIndex, &defaultRandomAccess, &defaultPadding, &defaultDegradationPriority); @@ -1740,11 +1790,18 @@ static GF_Err dasher_isom_create_init_segment(GF_DashSegInput *dash_inputs, u32 defaultSize, (u8) defaultRandomAccess, defaultPadding, defaultDegradationPriority); if (e) break; + + } + } + if (!i) { + if (use_avc3) { + gf_isom_set_brand_info(init_seg, GF_4CC('i','s','o','6'), 1); + } else { + gf_isom_set_brand_info(init_seg, GF_4CC('i','s','o','5'), 1); } + gf_isom_modify_alternate_brand(init_seg, GF_4CC('d','a','s','h'), 1); } - gf_isom_set_brand_info(init_seg, GF_4CC('i','s','o','5'), 1); - gf_isom_modify_alternate_brand(init_seg, GF_4CC('d','a','s','h'), 1); - if (i) gf_isom_close(in); + gf_isom_close(in); } if (e) { GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: Couldn't create initialization segment: error %s\n", gf_error_to_string(e) )); @@ -1757,10 +1814,11 @@ static GF_Err dasher_isom_create_init_segment(GF_DashSegInput *dash_inputs, u32 *disable_bs_switching = 1; gf_isom_delete(init_seg); gf_delete_file(szInitName); + return GF_OK; } else { - gf_isom_close(init_seg); + e = gf_isom_close(init_seg); } - return GF_OK; + return e; } static GF_Err dasher_isom_segment_file(GF_DashSegInput *dash_input, const char *szOutName, GF_DASHSegmenterOptions *dash_cfg, Bool first_in_set) @@ -1772,6 +1830,8 @@ static GF_Err dasher_isom_segment_file(GF_DashSegInput *dash_input, const char * } #endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ +#ifndef GPAC_DISABLE_MPEG2TS + static GF_Err dasher_generic_classify_input(GF_DashSegInput *dash_inputs, u32 nb_dash_inputs, u32 input_idx, u32 *current_group_id, u32 *max_sap_type) { #ifdef GPAC_DISABLE_MEDIA_IMPORT @@ -1797,6 +1857,9 @@ static GF_Err dasher_generic_classify_input(GF_DashSegInput *dash_inputs, u32 nb if (strcmp(dash_inputs[input_idx].szMime, dash_inputs[i].szMime)) continue; + if (strcmp(dash_inputs[input_idx].role, dash_inputs[i].role)) + continue; + memset(&probe, 0, sizeof(GF_MediaImporter)); probe.flags = GF_IMPORT_PROBE_ONLY; probe.in_name = (char *)dash_inputs[i].file_name; @@ -1863,6 +1926,10 @@ static GF_Err dasher_generic_classify_input(GF_DashSegInput *dash_inputs, u32 nb return GF_OK; } +#endif + +#ifndef GPAC_DISABLE_MPEG2TS + static GF_Err dasher_generic_get_components_info(GF_DashSegInput *input, GF_DASHSegmenterOptions *opts) { #ifdef GPAC_DISABLE_MEDIA_IMPORT @@ -1894,6 +1961,8 @@ static GF_Err dasher_generic_get_components_info(GF_DashSegInput *input, GF_DASH #endif return GF_OK; } +#endif + #ifndef GPAC_DISABLE_MPEG2TS @@ -2445,6 +2514,8 @@ static GF_Err dasher_mp2t_get_components_info(GF_DashSegInput *dash_input, GF_DA return GF_OK; } +#define NB_TSPCK_IO_BYTES 18800 + static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char *szOutName, GF_DASHSegmenterOptions *dash_cfg, Bool first_in_set) { GF_TSSegmenter ts_seg; @@ -2457,8 +2528,7 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * const char *opt; u32 i, startNumberRewind; GF_Err e; - u64 start, pcr_shift, next_pcr_shift, next_dts; - Bool store_params=0; + u64 start, pcr_shift, next_pcr_shift; Double cumulated_duration = 0; u32 bandwidth = 0; u32 segment_index; @@ -2475,17 +2545,23 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * ts_seg.bandwidth = (u32) (ts_seg.file_size * 8 / dash_input->duration); /*create bitstreams*/ - gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, 1, IdxName, basename, dash_input->representationID, dash_cfg->seg_rad_name, "six", 0, 0, 0); segment_index = 1; startNumberRewind = 0; - ts_seg.index_file = NULL; ts_seg.index_bs = NULL; if (!dash_cfg->dash_ctx && (dash_cfg->use_url_template != 2)) { #ifndef GPAC_DISABLE_ISOM_FRAGMENTS GF_SegmentTypeBox *styp; + + gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, 1, IdxName, szOutName, dash_input->representationID, dash_cfg->seg_rad_name, "six", 0, 0, 0); + ts_seg.index_file = gf_f64_open(IdxName, "wb"); + if (!ts_seg.index_file) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create index file %s\n", IdxName)); + e = GF_IO_ERR; + goto exit; + } ts_seg.index_bs = gf_bs_from_file(ts_seg.index_file, GF_BITSTREAM_WRITE); styp = (GF_SegmentTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STYP); @@ -2500,6 +2576,8 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * #endif } + gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, 1, IdxName, basename, dash_input->representationID, dash_cfg->seg_rad_name, "six", 0, 0, 0); + ts_seg.PCR_DTS_initial_diff = (u64) -1; ts_seg.subduration = (u32) (dash_cfg->subduration * 90000); @@ -2515,10 +2593,11 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * sscanf(opt, LLU, &offset); while (!feof(ts_seg.src) && !ts_seg.has_seen_pat) { - char data[188]; - u32 size = fread(data, 1, 188, ts_seg.src); - if (size<188) break; + char data[NB_TSPCK_IO_BYTES]; + u32 size = fread(data, 1, NB_TSPCK_IO_BYTES, ts_seg.src); gf_m2ts_process_data(ts_seg.ts, data, size); + + if (sizedash_ctx, szSectionName, "Setup", "yes"); gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "ID", dash_input->representationID); - store_params = 1; } else { if (!bandwidth) { opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "Bandwidth"); @@ -2601,7 +2678,7 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE, 1, IdxName, basename, dash_input->representationID, gf_url_get_resource_name(dash_cfg->seg_rad_name), "six", 0, bandwidth, segment_index); fprintf(dash_cfg->mpd, " index=\"%s\"", IdxName); } - fprintf(dash_cfg->mpd, "/>\n", (u32) (90000*dash_cfg->segment_duration), segment_index, SegName); + fprintf(dash_cfg->mpd, "/>\n"); } @@ -2618,9 +2695,6 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * if (dash_input->components[i].sample_rate) fprintf(dash_cfg->mpd, " audioSamplingRate=\"%d\"", dash_input->components[i].sample_rate); - - if (dash_input->components[i].szLang[0]) - fprintf(dash_cfg->mpd, " lang=\"%s\"", dash_input->components[i].szLang); } if (strlen(szCodecs)) fprintf(dash_cfg->mpd, " codecs=\"%s\"", szCodecs); @@ -2631,14 +2705,14 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * if (dash_cfg->single_file_mode==1) { gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, basename, dash_input->representationID, gf_url_get_resource_name(dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index); - fprintf(dash_cfg->mpd, " %s\n", dash_cfg->seg_rad_name ? SegName : dash_input->file_name); + fprintf(dash_cfg->mpd, " %s\n", SegName); fprintf(dash_cfg->mpd, " \n"); fprintf(dash_cfg->mpd, " \n", IdxName); fprintf(dash_cfg->mpd, " \n"); /*we rewrite the file*/ - if (dash_cfg->seg_rad_name) rewrite_input = 1; + rewrite_input = 1; } else { if (dash_cfg->seg_rad_name && dash_cfg->use_url_template) { if (dash_cfg->variable_seg_rad_name) { @@ -2650,18 +2724,19 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * } if (dash_cfg->time_shift_depth>=0) - fprintf(dash_cfg->mpd, " presentationTimeOffset=\"%d\"", ts_seg.sidx->earliest_presentation_time + pcr_shift); + fprintf(dash_cfg->mpd, " presentationTimeOffset=\""LLD"\"", ts_seg.sidx->earliest_presentation_time + pcr_shift); fprintf(dash_cfg->mpd, "/>\n"); } else if (dash_cfg->time_shift_depth>=0) { - fprintf(dash_cfg->mpd, " \n", ts_seg.sidx->earliest_presentation_time + pcr_shift); + fprintf(dash_cfg->mpd, " \n", ts_seg.sidx->earliest_presentation_time + pcr_shift); GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH]: PTSOffset "LLD" - startNumber %d - time %g\n", ts_seg.sidx->earliest_presentation_time + pcr_shift, segment_index, (Double) (s64) (ts_seg.sidx->earliest_presentation_time + pcr_shift) / 90000.0)); } } else { gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, basename, dash_input->representationID, gf_url_get_resource_name(dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index); - fprintf(dash_cfg->mpd, " %s\n", dash_cfg->seg_rad_name ? SegName : dash_input->file_name); + if (dash_cfg->single_file_mode) + fprintf(dash_cfg->mpd, " %s\n",SegName); fprintf(dash_cfg->mpd, " segment_duration)); if (dash_cfg->time_shift_depth>=0) - fprintf(dash_cfg->mpd, " presentationTimeOffset=\"%d\"", ts_seg.sidx->earliest_presentation_time + pcr_shift); + fprintf(dash_cfg->mpd, " presentationTimeOffset=\""LLD"\"", ts_seg.sidx->earliest_presentation_time + pcr_shift); fprintf(dash_cfg->mpd, ">\n"); if (!dash_cfg->dash_ctx) { @@ -2670,7 +2745,7 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * } /*rewrite previous SegmentList entries*/ - if ((dash_cfg->single_file_mode==2) || (!dash_cfg->single_file_mode && !dash_cfg->use_url_template)) { + if ( dash_cfg->dash_ctx && ((dash_cfg->single_file_mode==2) || (!dash_cfg->single_file_mode && !dash_cfg->use_url_template))) { /*rewrite previous URLs*/ const char *opt; u32 count, i; @@ -2690,7 +2765,7 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * fprintf(dash_cfg->mpd, " \n", start, start+ref->reference_size-1); start += ref->reference_size; } - if (dash_cfg->seg_rad_name) rewrite_input = 1; + rewrite_input = 1; } else { FILE *src, *dst; @@ -2699,15 +2774,21 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * src = gf_f64_open(dash_input->file_name, "rb"); start = ts_seg.sidx->first_offset; for (i=0; inb_refs; i++) { - char buf[4096]; + char buf[NB_TSPCK_IO_BYTES]; GF_SIDXReference *ref = &ts_seg.sidx->refs[i]; - gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, basename, dash_input->representationID, dash_cfg->seg_rad_name, "ts", 0, bandwidth, segment_index); + gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, szOutName, dash_input->representationID, dash_cfg->seg_rad_name, "ts", 0, bandwidth, segment_index); /*warning - we may introduce repeated sequence number when concatenating files. We should use switching segments to force reset of the continuity counter for all our pids - we don't because most players don't car ...*/ if (dash_cfg->use_url_template != 2) { dst = gf_f64_open(SegName, "wb"); + if (!dst) { + fclose(src); + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create segment file %s\n", SegName)); + e = GF_IO_ERR; + goto exit; + } gf_dasher_store_segment_info(dash_cfg, SegName, current_time); dur = ref->subsegment_duration; @@ -2719,8 +2800,8 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * end = start+ref->reference_size; while (pos= end) { + u32 to_read = NB_TSPCK_IO_BYTES; + if (pos+NB_TSPCK_IO_BYTES >= end) { to_read = (u32) (end-pos); } res = fread(buf, 1, to_read, src); @@ -2737,9 +2818,9 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * fclose(dst); } start += ref->reference_size; - segment_index++; if (!dash_cfg->use_url_template) { + gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, basename, dash_input->representationID, dash_cfg->seg_rad_name, "ts", 0, bandwidth, segment_index); fprintf(dash_cfg->mpd, " \n", SegName); if (dash_cfg->dash_ctx) { @@ -2750,6 +2831,7 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * } } + segment_index++; gf_set_progress("Extracting segment ", i+1, ts_seg.sidx->nb_refs); } fclose(src); @@ -2762,15 +2844,28 @@ static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char * if (rewrite_input) { FILE *in, *out; - char buf[3760]; + u64 fsize, done; + char buf[NB_TSPCK_IO_BYTES]; - in = gf_f64_open(dash_input->file_name, "rb"); + gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, 1, SegName, dash_cfg->seg_rad_name ? basename : szOutName, dash_input->representationID, gf_url_get_resource_name(dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index); out = gf_f64_open(SegName, "wb"); + if (!out) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create segment file %s\n", SegName)); + e = GF_IO_ERR; + goto exit; + } + in = gf_f64_open(dash_input->file_name, "rb"); + gf_f64_seek(in, 0, SEEK_END); + fsize = gf_f64_tell(in); + gf_f64_seek(in, 0, SEEK_SET); + done = 0; while (1) { - u32 read = fread(buf, 1, 3760, in); - if (!read) break; + u32 read = fread(buf, 1, NB_TSPCK_IO_BYTES, in); gf_m2ts_restamp(buf, read, pcr_shift, is_pes); fwrite(buf, 1, read, out); + done+=read; + gf_set_progress("Extracting segment ", done/188, fsize/188); + if (readtm_year, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + fprintf(mpd, " availabilityStartTime=\"%d-%02d-%02dT%02d:%02d:%02dZ\"", 1900+t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); #endif if ((s32)time_shift_depth>=0) { @@ -3000,8 +3095,6 @@ static GF_Err write_adaptation_header(FILE *mpd, GF_DashProfile profile, Bool us if (first_rep) { u32 i; - char langCode[4]; - langCode[3] = 0; if (bitstream_switching_mode) { for (i=0; inb_components; i++) { @@ -3011,6 +3104,18 @@ static GF_Err write_adaptation_header(FILE *mpd, GF_DashProfile profile, Bool us } } } + + /*set role*/ + if (strlen(first_rep->role)) { + if (!strcmp(first_rep->role, "caption") || !strcmp(first_rep->role, "subtitle") || !strcmp(first_rep->role, "main") + || !strcmp(first_rep->role, "alternate") || !strcmp(first_rep->role, "supplementary") || !strcmp(first_rep->role, "commentary") + || !strcmp(first_rep->role, "dub") + ) { + fprintf(mpd, " \n", first_rep->role); + } + } + + if (first_rep->nb_components>1) { for (i=0; inb_components; i++) { struct _dash_component *comp = &first_rep->components[i]; @@ -3034,7 +3139,7 @@ static GF_Err write_adaptation_header(FILE *mpd, GF_DashProfile profile, Bool us } /*if lang not specified at adaptationSet level, put it here*/ if ((!szLang || !szLang[0]) && comp->szLang[0]) { - fprintf(mpd, " lang=\"%s\"", langCode); + fprintf(mpd, " lang=\"%s\"", comp->szLang); } fprintf(mpd, "/>\n"); } @@ -3049,7 +3154,7 @@ static GF_Err write_adaptation_header(FILE *mpd, GF_DashProfile profile, Bool us return GF_OK; } -static GF_Err gf_dasher_init_context(GF_Config *dash_ctx, Bool *dynamic, u32 *timeShiftBufferDepth, const char *periodID) +static GF_Err gf_dasher_init_context(GF_Config *dash_ctx, u32 *dynamic, u32 *timeShiftBufferDepth, const char *periodID) { const char *opt; char szVal[100]; @@ -3066,11 +3171,13 @@ static GF_Err gf_dasher_init_context(GF_Config *dash_ctx, Bool *dynamic, u32 *ti if (!opt) { first_run = 1; sprintf(szVal, "%d", *timeShiftBufferDepth); - gf_cfg_set_key(dash_ctx, "DASH", "SessionType", *dynamic ? "dynamic" : "static"); + gf_cfg_set_key(dash_ctx, "DASH", "SessionType", (*dynamic==2) ? "dynamic-debug" : ( *dynamic ? "dynamic" : "static" ) ); gf_cfg_set_key(dash_ctx, "DASH", "TimeShiftBufferDepth", szVal); gf_cfg_set_key(dash_ctx, "DASH", "StoreParams", "yes"); } else { - *dynamic = !strcmp(opt, "dynamic") ? 1 : 0; + *dynamic = 0; + if (!strcmp(opt, "dynamic")) *dynamic = 1; + else if (!strcmp(opt, "dynamic-debug")) *dynamic = 2; opt = gf_cfg_get_key(dash_ctx, "DASH", "TimeShiftBufferDepth"); *timeShiftBufferDepth = atoi(opt); gf_cfg_set_key(dash_ctx, "DASH", "StoreParams", "no"); @@ -3158,7 +3265,7 @@ u32 gf_dasher_next_update_time(GF_Config *dash_ctx, u32 mpd_update_time) } /*peform all file cleanup*/ -static Bool gf_dasher_cleanup(GF_Config *dash_ctx, Bool dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double dash_duration) +static Bool gf_dasher_cleanup(GF_Config *dash_ctx, u32 dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double dash_duration) { Double max_dur = 0; Double ellapsed = 0; @@ -3187,18 +3294,22 @@ static Bool gf_dasher_cleanup(GF_Config *dash_ctx, Bool dash_dynamic, u32 mpd_up if (!max_dur) return 1; gf_net_get_ntp(&ntp_sec, &frac); - ellapsed = ntp_sec; - ellapsed -= prev_sec; - /*check if we need to generate */ - if (ellapsed < max_dur - safety_dur ) { - GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Asked to regenerate segments before expiration of the current segment list, please wait %g seconds - ignoring\n", max_dur + prev_sec - ntp_sec )); - return 0; - } - if (ellapsed > max_dur) { - GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Generating segments and MPD %g seconds too late\n", ellapsed - (u32) max_dur)); + if (dash_dynamic==2) { + ellapsed = (u32)-1; } else { - /*generate as if max_dur has been reached*/ - ellapsed = max_dur; + ellapsed = ntp_sec; + ellapsed -= prev_sec; + /*check if we need to generate */ + if (ellapsed < max_dur - safety_dur ) { + GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Asked to regenerate segments before expiration of the current segment list, please wait %g seconds - ignoring\n", max_dur + prev_sec - ntp_sec )); + return 0; + } + if (ellapsed > max_dur) { + GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Generating segments and MPD %g seconds too late\n", ellapsed - (u32) max_dur)); + } else { + /*generate as if max_dur has been reached*/ + ellapsed = max_dur; + } } /*cleanup old segments*/ @@ -3252,10 +3363,10 @@ GF_EXPORT GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *inputs, u32 nb_dash_inputs, GF_DashProfile dash_profile, const char *mpd_title, const char *mpd_source, const char *mpd_copyright, const char *mpd_moreInfoURL, const char **mpd_base_urls, u32 nb_mpd_base_urls, - Bool use_url_template, Bool single_segment, Bool single_file, Bool bitstream_switching_mode, + Bool use_url_template, Bool single_segment, Bool single_file, GF_DashSwitchingMode bitstream_switching, Bool seg_at_rap, Double dash_duration, char *seg_name, char *seg_ext, Double frag_duration, s32 subsegs_per_sidx, Bool daisy_chain_sidx, Bool frag_at_rap, const char *tmpdir, - GF_Config *dash_ctx, Bool dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double subduration) + GF_Config *dash_ctx, u32 dash_dynamic, u32 mpd_update_time, u32 time_shift_depth, Double subduration) { u32 i, j, segment_mode; char *sep, szSegName[GF_MAX_PATH], szSolvedSegName[GF_MAX_PATH], szTempMPD[GF_MAX_PATH]; @@ -3267,6 +3378,7 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input u32 max_sap_type = 0; Bool none_supported = 1; Bool has_mpeg2 = 0; + Bool has_role = 0; Double presentation_duration = 0; GF_Err e = GF_OK; FILE *mpd = NULL; @@ -3284,7 +3396,6 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input if (opt) { Double seg_dur = atof(opt); if (seg_dur != dash_duration) { - gf_cfg_del(dash_ctx); return GF_NOT_SUPPORTED; } } else { @@ -3307,8 +3418,12 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input dash_inputs[i].file_name = inputs[i].file_name; strcpy(dash_inputs[i].representationID, inputs[i].representationID); strcpy(dash_inputs[i].periodID, inputs[i].periodID); + strcpy(dash_inputs[i].role, inputs[i].role); dash_inputs[i].bandwidth = inputs[i].bandwidth; + if (strlen(inputs[i].role) && strcmp(inputs[i].role, "main")) + has_role = 1; + if (!strlen(dash_inputs[i].periodID)) { max_period = 1; dash_inputs[i].period = 1; @@ -3322,6 +3437,14 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input } memset(&dash_opts, 0, sizeof(GF_DASHSegmenterOptions)); + /*set all default roles to main if needed*/ + if (has_role) { + for (i=0; i=3) { if (dash_profile) { @@ -3390,7 +3512,6 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input } } - /*adjust params based on profiles*/ switch (dash_profile) { case GF_DASH_PROFILE_LIVE: @@ -3402,7 +3523,7 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input seg_at_rap = 1; single_segment = 1; /*BS switching is meaningless in onDemand profile*/ - bitstream_switching_mode = 0; + bitstream_switching = GF_DASH_BSMODE_NONE; use_url_template = single_file = 0; break; case GF_DASH_PROFILE_MAIN: @@ -3437,7 +3558,6 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("Spliting segments %sat GOP boundaries\n", frag_at_rap ? "and fragments " : "")); } - dash_opts.mpd_name = mpdfile; dash_opts.segments_start_with_rap = seg_at_rap; dash_opts.segment_duration = dash_duration; @@ -3452,6 +3572,7 @@ GF_Err gf_dasher_segment_files(const char *mpdfile, GF_DashSegmenterInput *input dash_opts.dash_ctx = dash_ctx; dash_opts.time_shift_depth = (s32) time_shift_depth; dash_opts.subduration = subduration; + dash_opts.inband_param_set = ((bitstream_switching == GF_DASH_BSMODE_INBAND) || (bitstream_switching == GF_DASH_BSMODE_SINGLE) ) ? 1 : 0; for (cur_period=0; cur_periodisom, track->track_num, 1); diff --git a/src/media_tools/isom_hinter.c b/src/media_tools/isom_hinter.c index 05ebaee..253a53a 100644 --- a/src/media_tools/isom_hinter.c +++ b/src/media_tools/isom_hinter.c @@ -444,16 +444,20 @@ GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, break; case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: case GF_ISOM_SUBTYPE_SVC_H264: { GF_AVCConfig *avcc = gf_isom_avc_config_get(file, TrackNum, 1); + GF_AVCConfig *svcc = gf_isom_svc_config_get(file, TrackNum, 1); required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ hintType = GF_RTP_PAYT_H264_AVC; streamType = GF_STREAM_VISUAL; - avc_nalu_size = avcc->nal_unit_size; + avc_nalu_size = avcc ? avcc->nal_unit_size : svcc->nal_unit_size; oti = GPAC_OTI_VIDEO_AVC; PL_ID = 0x0F; gf_odf_avc_cfg_del(avcc); + gf_odf_avc_cfg_del(svcc); } break; case GF_ISOM_SUBTYPE_3GP_QCELP: @@ -755,7 +759,51 @@ GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint) return GF_OK; } +static u32 write_nalu_config_array(char *sdpLine, GF_List *nalus) +{ + u32 i, count, b64s; + char b64[200]; + + count = gf_list_count(nalus); + for (i=0; idata, sl->size, b64, 200); + b64[b64s]=0; + strcat(sdpLine, b64); + if (i+1sequenceParameterSets) + gf_list_count(avcc->pictureParameterSets) + gf_list_count(avcc->sequenceParameterSetExtensions); + if (svcc) count += gf_list_count(svcc->sequenceParameterSets) + gf_list_count(svcc->pictureParameterSets); + if (!count) return; + + strcat(sdpLine, "; sprop-parameter-sets="); + + if (avcc) { + count = write_nalu_config_array(sdpLine, avcc->sequenceParameterSets); + if (count) strcat(sdpLine, ","); + count = write_nalu_config_array(sdpLine, avcc->sequenceParameterSetExtensions); + if (count) strcat(sdpLine, ","); + count = write_nalu_config_array(sdpLine, avcc->pictureParameterSets); + if (count) strcat(sdpLine, ","); + } + + if (svcc) { + count = write_nalu_config_array(sdpLine, svcc->sequenceParameterSets); + if (count) strcat(sdpLine, ","); + count = write_nalu_config_array(sdpLine, svcc->pictureParameterSets); + if (count) strcat(sdpLine, ","); + } + count = strlen(sdpLine); + if (sdpLine[count-1] == ',') + sdpLine[count-1] = 0; +} GF_EXPORT GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo) @@ -817,31 +865,20 @@ GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo) /*H264/AVC*/ else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H264_AVC) { GF_AVCConfig *avcc = gf_isom_avc_config_get(tkHint->file, tkHint->TrackNum, 1); - sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); - if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { - u32 i, count, b64s; - char b64[200]; - strcat(sdpLine, "; sprop-parameter-sets="); - count = gf_list_count(avcc->sequenceParameterSets); - for (i=0; isequenceParameterSets, i); - b64s = gf_base64_encode(sl->data, sl->size, b64, 200); - b64[b64s]=0; - strcat(sdpLine, b64); - if (i+1pictureParameterSets); - for (i=0; ipictureParameterSets, i); - b64s = gf_base64_encode(sl->data, sl->size, b64, 200); - b64[b64s]=0; - strcat(sdpLine, b64); - if (i+1file, tkHint->TrackNum, 1); + /*TODO - check syntax for SVC (might be some extra signaling)*/ + + if (avcc) { + sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); + } else { + sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, svcc->AVCProfileIndication, svcc->profile_compatibility, svcc->AVCLevelIndication); } + + write_avc_config(sdpLine, avcc, svcc); + gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine); gf_odf_avc_cfg_del(avcc); + gf_odf_avc_cfg_del(svcc); } /*MPEG-4 decoder config*/ else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_MPEG4) { diff --git a/src/media_tools/isom_tools.c b/src/media_tools/isom_tools.c index 1d98204..9f4c1bc 100644 --- a/src/media_tools/isom_tools.c +++ b/src/media_tools/isom_tools.c @@ -40,10 +40,14 @@ GF_Err gf_media_change_par(GF_ISOFile *file, u32 track, s32 ar_num, s32 ar_den) if (e) return e; stype = gf_isom_get_media_subtype(file, track, 1); - if ((stype==GF_ISOM_SUBTYPE_AVC_H264) || (stype==GF_ISOM_SUBTYPE_AVC2_H264) ) { + if ((stype==GF_ISOM_SUBTYPE_AVC_H264) + || (stype==GF_ISOM_SUBTYPE_AVC2_H264) + || (stype==GF_ISOM_SUBTYPE_AVC3_H264) + || (stype==GF_ISOM_SUBTYPE_AVC4_H264) + ) { #ifndef GPAC_DISABLE_AV_PARSERS GF_AVCConfig *avcc = gf_isom_avc_config_get(file, track, 1); - AVC_ChangePAR(avcc, ar_num, ar_den); + gf_media_avc_change_par(avcc, ar_num, ar_den); e = gf_isom_avc_config_update(file, track, 1, avcc); gf_odf_avc_cfg_del(avcc); if (e) return e; @@ -543,6 +547,8 @@ GF_Err gf_media_make_3gpp(GF_ISOFile *mp4file) break; case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: case GF_ISOM_SUBTYPE_SVC_H264: nb_vid++; nb_avc++; @@ -748,10 +754,14 @@ GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track) case GF_ISOM_SUBTYPE_MPEG4_CRYP: case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: case GF_ISOM_SUBTYPE_SVC_H264: case GF_ISOM_SUBTYPE_3GP_EVRC: case GF_ISOM_SUBTYPE_3GP_QCELP: case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_SUBTYPE_HVC1: + case GF_ISOM_SUBTYPE_HEV1: return gf_isom_get_esd(mp4, track, 1); } @@ -884,21 +894,32 @@ static s32 gf_get_DQId(GF_ISOFile *file, u32 track) { GF_AVCConfig *svccfg; GF_ISOSample *samp; - u32 di = 0; + u32 di = 0, cur_extract_mode; char *buffer; GF_BitStream *bs; u32 max_size = 4096; u32 size, nalu_size_length; u8 nal_type; + s32 DQId=0; + samp = NULL; + bs = NULL; + cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track); + gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT); + buffer = (char*)gf_malloc(sizeof(char) * max_size); svccfg = gf_isom_svc_config_get(file, track, 1); if (!svccfg) - return 0; + { + DQId = 0; + goto exit; + } samp = gf_isom_get_sample(file, track, 1, &di); if (!samp) - return -1; + { + DQId = -1; + goto exit; + } bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); - buffer = (char*)gf_malloc(sizeof(char) * max_size); nalu_size_length = 8 * svccfg->nal_unit_size; while (gf_bs_available(bs)) { @@ -910,10 +931,48 @@ static s32 gf_get_DQId(GF_ISOFile *file, u32 track) gf_bs_read_data(bs, buffer, size); nal_type = buffer[0] & 0x1F; if (nal_type == GF_AVC_NALU_SVC_SLICE) - return buffer[2] & 0x7F; + { + DQId = buffer[2] & 0x7F; + goto exit; + } } - return -1; +exit: + if (svccfg) gf_odf_avc_cfg_del(svccfg); + if (samp) gf_isom_sample_del(&samp); + if (buffer) gf_free(buffer); + if (bs) gf_bs_del(bs); + gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode); + return DQId;; } + +static Bool gf_isom_has_svc_explicit(GF_ISOFile *file, u32 track) +{ + GF_AVCConfig *svccfg; + GF_AVCConfigSlot *slc; + u32 i; + u8 type; + Bool ret = 0; + + svccfg = gf_isom_svc_config_get(file, track, 1); + if (!svccfg) + return 0; + + for (i = 0; i < gf_list_count(svccfg->sequenceParameterSets); i++) + { + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, i); + type = slc->data[0] & 0x1F; + if (type == GF_AVC_NALU_SEQ_PARAM) + { + ret = 1; + break; + } + } + + if (svccfg) gf_odf_avc_cfg_del(svccfg); + return ret; +} + + static u32 gf_isom_get_track_id_max(GF_ISOFile *file) { u32 num_track, i, trackID; @@ -934,87 +993,116 @@ static u32 gf_isom_get_track_id_max(GF_ISOFile *file) GF_EXPORT GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) { - GF_AVCConfig *avccfg, *svccfg; - u32 num_svc_track, num_sample, svc_track, dst_track, ref_trackID, ref_trackNum, max_id; - u32 di, width, height, size, nalu_size_length; - u32 i, j, t; + GF_AVCConfig *svccfg, *cfg; + u32 num_svc_track, num_sample, svc_track, dst_track, ref_trackID, ref_trackNum, max_id, di, width, height, size, nalu_size_length, i, j, t, max_size, num_pps, num_sps, num_subseq, NALUnitHeader, data_offset, data_length, count, timescale, cur_extract_mode; GF_Err e; - GF_AVCConfigSlot *slc; + GF_AVCConfigSlot *slc, *sl; AVCState avc; s32 sps_id, pps_id; - GF_ISOSample *samp; - GF_BitStream *bs; + GF_ISOSample *samp, *dst_samp; + GF_BitStream *bs, *dst_bs; GF_BitStream ** sample_bs; - u8 nal_type, nal_hdr; - GF_ISOSample *dst_samp; - GF_BitStream *dst_bs; - char *buffer; - //u8 dependency_id, quality_id, temporal_id, avc_dependency_id, avc_quality_id; - u32 max_size = 4096; + u8 nal_type, nal_hdr, track_ref_index; + char *buffer; s32 *sps_track, *sps, *pps; - u32 num_pps, num_sps; u64 offset; Bool is_splited; - Bool *first_sample_track; + Bool *first_sample_track, *is_subseq_pps; u64 *first_DTS_track; - u32 NALUnitHeader; - u8 track_ref_index; s8 sample_offset; - u32 data_offset; - u32 data_length; - u32 count, timescale; - avccfg = gf_isom_avc_config_get(file, track, 1); + max_size = 4096; + e = GF_OK; + samp = dst_samp = NULL; + bs = NULL; + sample_bs = NULL; + sps_track = sps = pps = NULL; + first_DTS_track = NULL; + first_sample_track = is_subseq_pps = NULL; + buffer = NULL; + cfg = NULL; + num_svc_track=0; + cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track); + gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT); svccfg = gf_isom_svc_config_get(file, track, 1); - - is_splited = (avccfg) ? 0 : 1; - - /*if we have not any SVC -> stop*/ if (!svccfg) - return GF_OK; - - timescale = gf_isom_get_media_timescale(file, track); - + { + e = GF_OK; + goto exit; + } num_sps = gf_list_count(svccfg->sequenceParameterSets); + if (!num_sps) + { + e = GF_OK; + goto exit; + } num_pps = gf_list_count(svccfg->pictureParameterSets); - /*we have a splited file with 1 SVC / track*/ - if (is_splited && num_pps == 1) + if ((gf_isom_get_avc_svc_type(file, track, 1) == GF_ISOM_AVCTYPE_SVC_ONLY) && !gf_isom_has_svc_explicit(file, track)) + is_splited = 1; + else + is_splited = 0; + num_subseq = gf_isom_has_svc_explicit(file, track) ? num_sps - 1 : num_sps; + + if (is_splited) { - /*use 'all' mode -> stop*/ - if (splitAll) - return GF_OK; - /*use 'base' mode -> merge SVC tracks*/ - else - return gf_media_merge_svc(file, track, 0); + /*this track has only one SVC ...*/ + if (num_sps == 1) + { + /*use 'all' mode -> stop*/ + if (splitAll) + goto exit; + /*use 'base' mode -> merge SVC tracks*/ + else + { + e = gf_media_merge_svc(file, track, 0); + goto exit; + } + } + /*this file has been in 'splitbase' mode*/ + else if (!splitAll) + goto exit; + } - num_svc_track = splitAll ? num_sps : 1; + + timescale = gf_isom_get_media_timescale(file, track); + num_svc_track = splitAll ? num_subseq : 1; max_id = gf_isom_get_track_id_max(file); di = 0; memset(&avc, 0, sizeof(AVCState)); avc.sps_active_idx = -1; nalu_size_length = 8 * svccfg->nal_unit_size; - /*read all sps*/ - sps = (s32 *) gf_malloc(num_sps * sizeof(s32)); - sps_track = (s32 *) gf_malloc(num_sps * sizeof(s32)); + /*read all sps, but we need only the subset sequence parameter sets*/ + sps = (s32 *) gf_malloc(num_subseq * sizeof(s32)); + sps_track = (s32 *) gf_malloc(num_subseq * sizeof(s32)); + count = 0; for (i = 0; i < num_sps; i++) { - slc = gf_list_get(svccfg->sequenceParameterSets, i); - sps_id = AVC_ReadSeqInfo(slc->data+1, slc->size-1, &avc,0, NULL); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, i); + nal_type = slc->data[0] & 0x1F; + sps_id = gf_media_avc_read_sps(slc->data, slc->size, &avc, 0, NULL); if (sps_id < 0) { - return GF_NON_COMPLIANT_BITSTREAM; + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM) + { + sps[count] = sps_id; + sps_track[count] = i; + count++; } - sps[i] = sps_id; - sps_track[i] = i; } + /*for testing*/ + assert(count == num_subseq); /*read all pps*/ pps = (s32 *) gf_malloc(num_pps * sizeof(s32)); for (j = 0; j < num_pps; j++) { - slc = gf_list_get(svccfg->pictureParameterSets, j); - pps_id = AVC_ReadPictParamSet(slc->data+1, slc->size-1, &avc); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j); + pps_id = gf_media_avc_read_pps(slc->data, slc->size, &avc); if (pps_id < 0) { - return GF_NON_COMPLIANT_BITSTREAM; + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; } pps[j] = pps_id; } @@ -1025,14 +1113,21 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) gf_isom_get_reference(file, track, GF_ISOM_REF_BASE, 1, &ref_trackNum); ref_trackID = gf_isom_get_track_id(file, ref_trackNum); } + + buffer = (char*)gf_malloc(sizeof(char) * max_size); /*read first sample for determinating the order of SVC tracks*/ count = 0; samp = gf_isom_get_sample(file, track, 1, &di); if (!samp) - return GF_IO_ERR; + { + e = gf_isom_last_error(file); + goto exit; + } bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); offset = 0; - buffer = (char*)gf_malloc(sizeof(char) * max_size); + is_subseq_pps = (Bool *) gf_malloc(num_pps*sizeof(Bool)); + for (i = 0; i < num_pps; i++) + is_subseq_pps[i] = 0; while (gf_bs_available(bs)) { size = gf_bs_read_int(bs, nalu_size_length); @@ -1042,16 +1137,28 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) } nal_hdr = gf_bs_read_u8(bs); nal_type = nal_hdr & 0x1F; - AVC_ParseNALU(bs, nal_hdr, &avc); - gf_bs_seek(bs, offset+nalu_size_length/8); + gf_media_avc_parse_nalu(bs, nal_hdr, &avc); + e = gf_bs_seek(bs, offset+nalu_size_length/8); + if (e) + goto exit; gf_bs_read_data(bs, buffer, size); offset += size + nalu_size_length/8; if (nal_type == GF_AVC_NALU_SVC_SLICE) { + for (i = 0; i < num_pps; i++) + { + if (avc.s_info.pps->id == pps[i]) + { + is_subseq_pps[i] = 1; + break; + } + } + if ((count > 0) && (avc.s_info.pps->sps_id == sps[count-1])) + continue; /*verify the order of SPS, reorder if necessary*/ if (avc.s_info.pps->sps_id != sps[count]) { - for (i = count+1; i < num_sps; i++) + for (i = count+1; i < num_subseq; i++) { /*swap two SPS*/ if (avc.s_info.pps->sps_id == sps[i]) @@ -1062,36 +1169,37 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) break; } } - /*for testing: ensure that there are not two NALU which use the same SPS, so that we can use sps for separating layers*/ - assert(i < num_sps); } count++; } } - /*for testing: ensure that the number of SPS is equal to the number of SVC layers*/ - assert(count == num_sps); + gf_bs_del(bs); + bs = NULL; - gf_free(buffer); - buffer = NULL; + gf_isom_sample_del(&samp); + samp = NULL; for (t = 0; t < num_svc_track; t++) { - GF_AVCConfig *cfg; - e = GF_OK; svc_track = gf_isom_new_track(file, t+1+max_id, GF_ISOM_MEDIA_VISUAL, timescale); if (!svc_track) { e = gf_isom_last_error(file); - return e; + goto exit; } gf_isom_set_track_enabled(file, svc_track, 1); gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_BASE, ref_trackID); cfg = gf_odf_avc_cfg_new(); cfg->complete_representation = 1; //SVC + /*this layer depends on the base layer and the lower layers*/ + gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_SCAL, ref_trackID); + for (i = 0; i < t; i++) + gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_SCAL, i+1+max_id); + e = gf_isom_svc_config_new(file, svc_track, cfg, NULL, NULL, &di); if (e) - return e; + goto exit; if (splitAll) { sps_id = sps[t]; @@ -1106,19 +1214,31 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) cfg->AVCLevelIndication = avc.sps[sps_id].level_idc; cfg->AVCProfileIndication = avc.sps[sps_id].profile_idc; cfg->nal_unit_size = svccfg->nal_unit_size; - gf_list_add(cfg->sequenceParameterSets, gf_list_get(svccfg->sequenceParameterSets, sps_track[t])); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, sps_track[t]); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(cfg->sequenceParameterSets, sl); for (j = 0; j < num_pps; j++) { pps_id = pps[j]; - if (avc.pps[pps_id].sps_id == sps_id) + if (is_subseq_pps[j] && (avc.pps[pps_id].sps_id == sps_id)) { - gf_list_add(cfg->pictureParameterSets, gf_list_get(svccfg->pictureParameterSets, j)); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(cfg->pictureParameterSets, sl); } } } else { - for (i = 0; i < num_sps; i++) + for (i = 0; i < num_subseq; i++) { sps_id = sps[i]; width = avc.sps[sps_id].width; @@ -1132,22 +1252,34 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) cfg->AVCLevelIndication = avc.sps[sps_id].level_idc; cfg->AVCProfileIndication = avc.sps[sps_id].profile_idc; cfg->nal_unit_size = svccfg->nal_unit_size; - gf_list_add(cfg->sequenceParameterSets, gf_list_get(svccfg->sequenceParameterSets, sps_track[i])); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, sps_track[i]); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(cfg->sequenceParameterSets, sl); for (j = 0; j < num_pps; j++) { - slc = gf_list_get(svccfg->pictureParameterSets, t); - pps_id = AVC_ReadPictParamSet(slc->data+1, slc->size-1, &avc); - if (pps_id < 0) { - return GF_NON_COMPLIANT_BITSTREAM; - } + pps_id = pps[j]; if (avc.pps[pps_id].sps_id == sps_id) { - gf_list_add(cfg->pictureParameterSets, gf_list_get(svccfg->pictureParameterSets, j)); + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(cfg->pictureParameterSets, sl); } } } } - gf_isom_svc_config_update(file, svc_track, 1, cfg, 0); + e = gf_isom_svc_config_update(file, svc_track, 1, cfg, 0); + if (e) + goto exit; + gf_odf_avc_cfg_del(cfg); + cfg = NULL; } num_sample = gf_isom_get_sample_count(file, track); @@ -1157,28 +1289,69 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) first_DTS_track = (u64 *) gf_malloc((num_svc_track+1) * sizeof(u64)); for (t = 0; t <= num_svc_track; t++) first_DTS_track[t] = 0; - for (i = 1; i <= num_sample; i++) { - u32 *prev_layer; - u32 count_prev_layer; - - prev_layer = (u32 *) gf_malloc(num_svc_track * sizeof(u32)); - count_prev_layer = 0; + /*reset*/ + memset(buffer, 0, max_size); - samp = gf_isom_get_sample(file, track, i, &di); if (!samp) - return GF_IO_ERR; + { + e = GF_IO_ERR; + goto exit; + } /* Create (num_svc_track) SVC bitstreams + 1 AVC bitstream*/ sample_bs = (GF_BitStream **) gf_malloc(sizeof(GF_BitStream *) * (num_svc_track+1)); for (j = 0; j <= num_svc_track; j++) sample_bs[j] = (GF_BitStream *) gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*write extractor*/ + for (t = 0; t < num_svc_track; t++) + { + //reference to base layer + gf_bs_write_int(sample_bs[t+1], 14, nalu_size_length); // extractor 's size = 14 + NALUnitHeader = 0; //reset + NALUnitHeader |= 0x1F000000; // NALU type = 31 + gf_bs_write_u32(sample_bs[t+1], NALUnitHeader); + track_ref_index = (u8) gf_isom_has_track_reference(file, t+1+max_id, GF_ISOM_REF_SCAL, ref_trackID); + if (!track_ref_index) + { + e = GF_CORRUPTED_DATA; + goto exit; + } + gf_bs_write_u8(sample_bs[t+1], track_ref_index); + sample_offset = 0; + gf_bs_write_u8(sample_bs[t+1], sample_offset); + data_offset = 0; + gf_bs_write_u32(sample_bs[t+1], data_offset); + data_length = 0; + gf_bs_write_u32(sample_bs[t+1], data_length); + //reference to previous layer(s) + for (j = 0; j < t; j++) + { + gf_bs_write_int(sample_bs[t+1], 14, nalu_size_length); + NALUnitHeader = 0; + NALUnitHeader |= 0x1F000000; + gf_bs_write_u32(sample_bs[t+1], NALUnitHeader); + track_ref_index = (u8) gf_isom_has_track_reference(file, t+1+max_id, GF_ISOM_REF_SCAL, j+1+max_id); + if (!track_ref_index) + { + e = GF_CORRUPTED_DATA; + goto exit; + } + gf_bs_write_u8(sample_bs[t+1], track_ref_index); + sample_offset = 0; + gf_bs_write_u8(sample_bs[t+1], sample_offset); + data_offset = (j+1) * (nalu_size_length/8 + 14); // (nalu_size_length/8) bytes of NALU length field + 14 bytes of extractor per layer + gf_bs_write_u32(sample_bs[t+1], data_offset); + data_length = 0; + gf_bs_write_u32(sample_bs[t+1], data_length); + } + } + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); offset = 0; - buffer = (char*)gf_malloc(sizeof(char) * max_size); while (gf_bs_available(bs)) { size = gf_bs_read_int(bs, nalu_size_length); @@ -1188,20 +1361,50 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) } nal_hdr = gf_bs_read_u8(bs); nal_type = nal_hdr & 0x1F; - AVC_ParseNALU(bs, nal_hdr, &avc); - gf_bs_seek(bs, offset+nalu_size_length/8); + gf_media_avc_parse_nalu(bs, nal_hdr, &avc); + e = gf_bs_seek(bs, offset+nalu_size_length/8); + if (e) + goto exit; gf_bs_read_data(bs, buffer, size); offset += size + nalu_size_length/8; - + switch (nal_type) { - //case GF_AVC_NALU_SVC_PREFIX_NALU: - case GF_AVC_NALU_SVC_SLICE: + case GF_AVC_NALU_PIC_PARAM: + pps_id = gf_media_avc_read_pps(buffer, size, &avc);; + j = 0; + dst_track = 0; + while (j < num_pps) + { + if (pps_id == pps[j]) + break; + j++; + } + if ((j < num_pps) && (is_subseq_pps[j])) + { + if (splitAll) + { + for (t = 0; t < num_svc_track; t++) + { + if (sps[t] == avc.pps[pps_id].sps_id) + { + dst_track = t + 1; + break; + } + } + } + else + dst_track = 1; + } + dst_bs = sample_bs[dst_track]; + break; + case GF_AVC_NALU_SVC_SUBSEQ_PARAM: + sps_id = gf_media_avc_read_sps(buffer, size, &avc, 0, NULL); dst_track = 0; if (splitAll) { - for (t = 0; t < num_svc_track; t++) // num_svc_track == num_pps + for (t = 0; t < num_svc_track; t++) { - if (sps_track[t] == (avc.s_info.pps)->sps_id) + if (sps[t] == sps_id) { dst_track = t + 1; break; @@ -1211,42 +1414,23 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) else dst_track = 1; dst_bs = sample_bs[dst_track]; - /*write extractor*/ - if (!gf_bs_get_position(dst_bs)) + break; + case GF_AVC_NALU_SVC_SLICE: + dst_track = 0; + if (splitAll) { - //reference to base layer - gf_bs_write_int(dst_bs, 14, nalu_size_length); // extractor 's size = 14 - NALUnitHeader = 0; //reset - NALUnitHeader |= 0x1F000000; // NALU type = 31 - gf_bs_write_u32(dst_bs, NALUnitHeader); - //track_ref_index is a trackID, not a trackNum. So when rewrite, gf_isom_get_track_id must be used to find trackNum - track_ref_index = ref_trackID; - gf_bs_write_u8(dst_bs, track_ref_index); - sample_offset = 0; - gf_bs_write_u8(dst_bs, sample_offset); - data_offset = 0; - gf_bs_write_u32(dst_bs, data_offset); - data_length = 0; - gf_bs_write_u32(dst_bs, data_length); - //reference to previous layer(s) - for (t = 0; t < count_prev_layer; t++) + for (t = 0; t < num_svc_track; t++) { - gf_bs_write_int(dst_bs, 14, nalu_size_length); - NALUnitHeader = 0; - NALUnitHeader |= 0x1F000000; - gf_bs_write_u32(dst_bs, NALUnitHeader); - track_ref_index = max_id + prev_layer[t]; - gf_bs_write_u8(dst_bs, track_ref_index); - sample_offset = 0; - gf_bs_write_u8(dst_bs, sample_offset); - data_offset = (t+1) * (nalu_size_length/8 + 14); // (nalu_size_length/8) bytes of NALU length field + 14 bytes of extractor per layer - gf_bs_write_u32(dst_bs, data_offset); - data_length = 0; - gf_bs_write_u32(dst_bs, data_length); + if (sps[t] == (avc.s_info.pps)->sps_id) + { + dst_track = t + 1; + break; + } } } - prev_layer[count_prev_layer] = dst_track; - count_prev_layer++; + else + dst_track = 1; + dst_bs = sample_bs[dst_track]; break; default: dst_bs = sample_bs[0]; @@ -1275,7 +1459,7 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) else e = gf_isom_update_sample(file, track, i, dst_samp, 1); if (e) - return e; + goto exit; gf_isom_sample_del(&dst_samp); dst_samp = NULL; } @@ -1283,7 +1467,11 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) sample_bs[j] = NULL; } gf_free(sample_bs); - gf_free(bs); + sample_bs = NULL; + gf_bs_del(bs); + bs = NULL; + gf_isom_sample_del(&samp); + samp = NULL; } /*add Editlist entry if DTS of the first sample is not zero*/ @@ -1302,84 +1490,131 @@ GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll) } } - /*if this is a merged file: delete SVC config*/ + /*if this is a merged file*/ if (!is_splited) - gf_isom_svc_config_del(file, track, 1); + { + /*a normal stream: delete SVC config*/ + if (!gf_isom_has_svc_explicit(file, track)) + { + gf_isom_svc_config_del(file, track, 1); + } + else + { + s32 shift=0; + + for (i = 0; i < gf_list_count(svccfg->sequenceParameterSets); i++) + { + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, i); + sps_id = gf_media_avc_read_sps(slc->data, slc->size, &avc, 0, NULL); + if (sps_id < 0) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + nal_type = slc->data[0] & 0x1F; + if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM) + { + gf_list_rem(svccfg->sequenceParameterSets, i); + gf_free(slc->data); + gf_free(slc); + i--; + } + } + + for (j = 0; j < gf_list_count(svccfg->pictureParameterSets); j++) + { + slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j); + pps_id = gf_media_avc_read_pps(slc->data, slc->size, &avc); + if (pps_id < 0) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + if (is_subseq_pps[j+shift]) + { + gf_list_rem(svccfg->pictureParameterSets, j); + gf_free(slc->data); + gf_free(slc); + j--; + } + } + e = gf_isom_svc_config_update(file, track, 1, svccfg, 0); + if (e) + goto exit; + } + } /*if this is as splited file: delete this track*/ else { gf_isom_remove_track(file, track); } - return GF_OK; + +exit: + if (svccfg) gf_odf_avc_cfg_del(svccfg); + if (cfg) gf_odf_avc_cfg_del(cfg); + if (samp) gf_isom_sample_del(&samp); + if (dst_samp) gf_isom_sample_del(&dst_samp); + if (bs) gf_bs_del(bs); + if (sample_bs) + { + for (i = 0; i <= num_svc_track; i++) + gf_bs_del(sample_bs[i]); + gf_free(sample_bs); + } + if (sps_track) gf_free(sps_track); + if (sps) gf_free(sps); + if (pps) gf_free(pps); + if (first_sample_track) gf_free(first_sample_track); + if (first_DTS_track) gf_free(first_DTS_track); + if (buffer) gf_free(buffer); + if (is_subseq_pps) gf_free(is_subseq_pps); + gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode); + return e; } /* Merge SVC layers*/ GF_EXPORT GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) { - GF_AVCConfig *avccfg, *svccfg, *cfg; - u32 merge_track; - u32 num_track, num_sample; + GF_AVCConfig *svccfg, *cfg; + u32 merge_track, num_track, num_sample, size, i, t, di, max_size, nalu_size_length, ref_trackNum, ref_trackID, count, width, height, nb_EditList, media_ts, moov_ts; GF_ISOSample *avc_samp, *samp, *dst_samp; GF_BitStream *bs, *dst_bs; GF_Err e; - u32 size; - u32 i, t; - u32 di = 0; char *buffer; - u32 max_size = 4096; - u32 nalu_size_length; - u32 ref_trackNum, ref_trackID; s32 *DQId; - u32 count; - u32 *list_track_sorted; - u32 *cur_sample, *max_sample; - u32 width = 0, height = 0; + u32 *list_track_sorted, *cur_sample, *max_sample; u64 *DTS_offset; - u32 nb_EditList; - u32 media_ts, moov_ts; u64 EditTime, SegmentDuration, MediaTime; - u8 EditMode; + u8 EditMode, nal_type; Bool first_sample; - u64 first_DTS, offset, dur; - u32 max_id; - u32 timescale; - u8 nal_type; - - avc_samp = samp = NULL; - - avccfg = gf_isom_avc_config_get(file, track, 1); - if (!avccfg && mergeAll) - return GF_BAD_PARAM; - timescale = gf_isom_get_media_timescale(file, track); + u64 first_DTS, offset, dur; + GF_AVCConfigSlot *slc, *sl; + + e = GF_OK; + di = 1; + max_size = 4096; + width = height = 0; + avc_samp = samp = dst_samp = NULL; + svccfg = cfg = NULL; + buffer = NULL; + bs = dst_bs = NULL; + DQId = NULL; + list_track_sorted = cur_sample = max_sample = NULL; + DTS_offset = NULL; + + if (gf_isom_get_avc_svc_type(file, track, 1) == GF_ISOM_AVCTYPE_AVC_SVC) + goto exit; num_track = gf_isom_get_track_count(file); - if (num_track == 1) { - if (avccfg) gf_odf_avc_cfg_del(avccfg); - return GF_OK; - } - - /*create a new merged track*/ - max_id = gf_isom_get_track_id_max(file); - merge_track = gf_isom_new_track(file, max_id+1, GF_ISOM_MEDIA_VISUAL, timescale); - gf_isom_set_track_enabled(file, merge_track, 1); - /*add avc configuration if any*/ - if (avccfg) - gf_isom_avc_config_new(file, merge_track, avccfg, NULL, NULL, &di); - - svccfg = gf_odf_avc_cfg_new(); - svccfg->complete_representation = 1; - e = gf_isom_svc_config_new(file, merge_track, svccfg, NULL, NULL, &di); - if (e) goto exit; - - if (avccfg) { - ref_trackNum = track; - ref_trackID = gf_isom_get_track_id(file, track); - } else { - gf_isom_get_reference(file, track, GF_ISOM_REF_BASE, 1, &ref_trackNum); - ref_trackID = gf_isom_get_track_id(file, ref_trackNum); + if (num_track == 1) + goto exit; + gf_isom_get_reference(file, track, GF_ISOM_REF_BASE, 1, &ref_trackNum); + ref_trackID = gf_isom_get_track_id(file, ref_trackNum); + if (!ref_trackID) + { + e = GF_ISOM_INVALID_MEDIA; + goto exit; } - + list_track_sorted = (u32 *) gf_malloc(num_track * sizeof(u32)); DQId = (s32 *) gf_malloc(num_track * sizeof(s32)); count = 0; @@ -1390,8 +1625,13 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) e = GF_ISOM_INVALID_MEDIA; goto exit; } - if ((t != track) && !gf_isom_has_track_reference(file, t, GF_ISOM_REF_BASE, ref_trackID)) - continue; + + if (!gf_isom_has_track_reference(file, t, GF_ISOM_REF_BASE, ref_trackID)) + { + if (t != ref_trackNum) continue; + else if (!mergeAll) continue; + } + while ((pos < count ) && (DQId[pos] <= track_DQId)) pos++; for (i = count; i > pos; i--) @@ -1404,16 +1644,20 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) count++; } - if (mergeAll) - { - gf_isom_get_visual_info(file, list_track_sorted[0], 1, &width, &height); - } - else + merge_track = list_track_sorted[0]; + gf_isom_set_track_enabled(file, merge_track, 1); + /*rewrite svccfg*/ + svccfg = gf_odf_avc_cfg_new(); + svccfg->complete_representation = 1; + e = gf_isom_svc_config_new(file, merge_track, svccfg, NULL, NULL, &di); + if (e) goto exit; + /*rewrite visual info*/ + if (!mergeAll) { for (t = 0; t < count; t++) gf_isom_get_visual_info(file, list_track_sorted[t], 1, &width, &height); + gf_isom_set_visual_info(file, merge_track, 1, width, height); } - gf_isom_set_visual_info(file, merge_track, 1, width, height); for (t = 0; t < count; t++) { @@ -1430,18 +1674,30 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) svccfg->nal_unit_size = cfg->nal_unit_size; for (i = 0; i < gf_list_count(cfg->sequenceParameterSets); i++) { - gf_list_add(svccfg->sequenceParameterSets, gf_list_get(cfg->sequenceParameterSets, i)); - } + slc = (GF_AVCConfigSlot *)gf_list_get(cfg->sequenceParameterSets, i); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(svccfg->sequenceParameterSets, sl); + } for (i = 0; i < gf_list_count(cfg->pictureParameterSets); i++) { - gf_list_add(svccfg->pictureParameterSets, gf_list_get(cfg->pictureParameterSets, i)); + slc = (GF_AVCConfigSlot *)gf_list_get(cfg->pictureParameterSets, i); + sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + sl->id = slc->id; + sl->size = slc->size; + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + memcpy(sl->data, slc->data, sizeof(char)*sl->size); + gf_list_add(svccfg->pictureParameterSets, sl); } if (mergeAll) gf_isom_svc_config_update(file, merge_track, 1, svccfg, 1); else gf_isom_svc_config_update(file, merge_track, 1, svccfg, 0); - gf_odf_avc_cfg_del(cfg); + cfg = NULL; } cur_sample = (u32 *) gf_malloc(count * sizeof(u32)); @@ -1479,6 +1735,9 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) nalu_size_length = 8 * svccfg->nal_unit_size; first_sample = 1; first_DTS = 0; + buffer = (char*)gf_malloc(sizeof(char) * max_size); + for (t = 1; t <= num_track; t++) + gf_isom_set_nalu_extract_mode(file, t, GF_ISOM_NALU_EXTRACT_INSPECT); for (i = 1; i <= num_sample; i++) { dst_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); @@ -1494,7 +1753,12 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) gf_bs_write_int(dst_bs, 14, nalu_size_length); // extractor 's size = 14 NALUnitHeader |= 0x1F000000; // NALU type = 31 gf_bs_write_u32(dst_bs, NALUnitHeader); - track_ref_index = ref_trackID; + track_ref_index = (u8) gf_isom_has_track_reference(file, merge_track, GF_ISOM_REF_SCAL, ref_trackID); + if (!track_ref_index) + { + e = GF_CORRUPTED_DATA; + goto exit; + } gf_bs_write_u8(dst_bs, track_ref_index); sample_offset = 0; gf_bs_write_u8(dst_bs, sample_offset); @@ -1503,7 +1767,6 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) data_length = 0; gf_bs_write_u32(dst_bs, data_length); } - buffer = (char*)gf_malloc(sizeof(char) * max_size); avc_samp = gf_isom_get_sample(file, ref_trackNum, i, &di); if (!avc_samp) { @@ -1524,6 +1787,8 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) if ((samp->DTS + DTS_offset[t]) != avc_samp->DTS) continue; bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + /*reset*/ + memset(buffer, 0, sizeof(char) * max_size); while (gf_bs_available(bs)) { size = gf_bs_read_int(bs, nalu_size_length); @@ -1560,15 +1825,16 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) dst_samp->DTS = avc_samp->DTS - first_DTS; dst_samp->IsRAP = avc_samp->IsRAP; gf_bs_get_content(dst_bs, &dst_samp->data, &dst_samp->dataLength); - e = gf_isom_add_sample(file, merge_track, 1, dst_samp); - gf_bs_del(dst_bs); - dst_bs = NULL; - gf_isom_sample_del(&dst_samp); + e = gf_isom_update_sample(file, merge_track, i, dst_samp, 1); if (e) goto exit; } gf_isom_sample_del(&avc_samp); avc_samp = NULL; + gf_bs_del(dst_bs); + dst_bs = NULL; + gf_isom_sample_del(&dst_samp); + dst_samp = NULL; } /*Add EditList if nessessary*/ @@ -1585,18 +1851,30 @@ GF_Err gf_media_merge_svc(GF_ISOFile *file, u32 track, Bool mergeAll) /*Delete SVC track(s) that references to ref_track*/ for (t = 1; t <= num_track; t++) { - if ((t != track) && !gf_isom_has_track_reference(file, t, GF_ISOM_REF_BASE, ref_trackID)) - continue; - gf_isom_remove_track(file, t); - num_track--; //we removed one track from file - t--; + if (gf_isom_has_track_reference(file, t, GF_ISOM_REF_BASE, ref_trackID) && (t != merge_track)) + { + gf_isom_remove_track(file, t); + num_track--; //we removed one track from file + t--; + } } exit: if (avc_samp) gf_isom_sample_del(&avc_samp); if (samp) gf_isom_sample_del(&samp); - if (avccfg) gf_odf_avc_cfg_del(avccfg); + if (dst_samp) gf_isom_sample_del(&dst_samp); if (svccfg) gf_odf_avc_cfg_del(svccfg); + if (cfg) gf_odf_avc_cfg_del(cfg); + if (bs) gf_bs_del(bs); + if (dst_bs) gf_bs_del(dst_bs); + if (buffer) gf_free(buffer); + if (DQId) gf_free(DQId); + if (list_track_sorted) gf_free(list_track_sorted); + if (cur_sample) gf_free(cur_sample); + if (max_sample) gf_free(max_sample); + if (DTS_offset) gf_free(DTS_offset); + for (t = 1; t <= gf_isom_get_track_count(file); t++) + gf_isom_set_nalu_extract_mode(file, t, GF_ISOM_NALU_EXTRACT_DEFAULT); return e; } diff --git a/src/media_tools/m2ts_mux.c b/src/media_tools/m2ts_mux.c index ed1f578..05bb8fc 100644 --- a/src/media_tools/m2ts_mux.c +++ b/src/media_tools/m2ts_mux.c @@ -1619,9 +1619,17 @@ GF_M2TS_Mux_Stream *gf_m2ts_program_stream_add(GF_M2TS_Mux_Program *program, str break; case GPAC_OTI_VIDEO_AVC: stream->mpeg2_stream_type = GF_M2TS_VIDEO_H264; - /*make sure we send AU delim NALU in same PES as first VCL NAL: 6 bytes (AU delim) + 4 byte start code + first nal header*/ + /*make sure we send AU delim NALU in same PES as first VCL NAL: 6 bytes (start code + 1 nal hdr + AU delim) + + 4 byte start code + first nal header*/ stream->min_bytes_copy_from_next = 11; break; + case GPAC_OTI_VIDEO_HEVC: + stream->mpeg2_stream_type = GF_M2TS_VIDEO_HEVC; + /*make sure we send AU delim NALU in same PES as first VCL NAL: 7 bytes (4 start code + 2 nal header + 1 AU delim) + + 4 byte start code + first nal header*/ + stream->min_bytes_copy_from_next = 12; + break; + case GPAC_OTI_VIDEO_MPEG1: stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG1; break; @@ -1739,6 +1747,7 @@ GF_M2TS_Mux_Program *gf_m2ts_mux_program_add(GF_M2TS_Mux *muxer, u32 program_num } program->pmt = gf_m2ts_stream_new(pmt_pid); program->pmt->program = program; + program->pmt->table_needs_update = 1; muxer->pat->table_needs_update = 1; program->pmt->process = gf_m2ts_stream_process_pmt; program->pmt->refresh_rate_ms = pmt_refresh_rate ? pmt_refresh_rate : (u32) -1; diff --git a/src/media_tools/m3u8.c b/src/media_tools/m3u8.c index b8777bc..fdaad15 100644 --- a/src/media_tools/m3u8.c +++ b/src/media_tools/m3u8.c @@ -495,20 +495,30 @@ GF_Err parse_root_playlist(const char * file, VariantPlaylist ** playlist, const GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const char * baseURL, Program * in_program, PlaylistElement *sub_playlist) { int len, i, currentLineNumber; - FILE * f; + FILE * f=NULL; + char *m3u8_payload; + u32 m3u8_size, m3u8pos; VariantPlaylist * pl; char currentLine[M3U8_BUF_SIZE]; char ** attributes = NULL; s_accumulated_attributes attribs; - f = gf_f64_open(file, "rt"); - if (!f) { - GF_LOG(GF_LOG_ERROR, GF_LOG_DASH,("[M3U8] Cannot Open m3u8 file %s for reading\n", file)); - return GF_SERVICE_ERROR; + + if (!strncmp(file, "gmem://", 7)) { + if (sscanf(file, "gmem://%d@%p", &m3u8_size, &m3u8_payload) != 2) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH,("[M3U8] Cannot Open m3u8 source %s for reading\n", file)); + return GF_SERVICE_ERROR; + } + } else { + f = gf_f64_open(file, "rt"); + if (!f) { + GF_LOG(GF_LOG_ERROR, GF_LOG_DASH,("[M3U8] Cannot Open m3u8 file %s for reading\n", file)); + return GF_SERVICE_ERROR; + } } if (*playlist == NULL) { *playlist = variant_playlist_new(); if (!(*playlist)) { - fclose(f); + if (f) fclose(f); return GF_OUT_OF_MEM; } } @@ -522,8 +532,26 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const attribs.isPlaylistEnded = 0; attribs.minMediaSequence = 0; attribs.currentMediaSequence = 0; - while (fgets(currentLine, sizeof(currentLine), f)) { + m3u8pos=0; + while (1) { char * eof; + if (f) { + if (!fgets(currentLine, sizeof(currentLine), f)) + break; + } else { + u32 __idx=0; + if (m3u8pos>=m3u8_size) + break; + while (1) { + currentLine[__idx] = m3u8_payload[m3u8pos]; + __idx++; + m3u8pos++; + if ((currentLine[__idx-1]=='\n') || (currentLine[__idx-1]=='\r')) { + currentLine[__idx]=0; + break; + } + } + } currentLineNumber++; eof = strchr(currentLine, '\r'); if (eof) @@ -601,7 +629,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const if (program == NULL) { /* OUT of memory */ variant_playlist_del(*playlist); - fclose(f); + if (f) fclose(f); playlist = NULL; return GF_OUT_OF_MEM; } @@ -643,7 +671,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const /* OUT of memory */ variant_playlist_del(*playlist); playlist = NULL; - fclose(f); + if (f) fclose(f); return GF_OUT_OF_MEM; } assert( fullURL); @@ -671,7 +699,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const /* OUT of memory */ variant_playlist_del(*playlist); playlist = NULL; - fclose(f); + if (f) fclose(f); return GF_OUT_OF_MEM; } assert(currentPlayList->element.playlist.elements); @@ -690,7 +718,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const variant_playlist_del(*playlist); playlist_element_del(currentPlayList); playlist = NULL; - fclose(f); + if (f) fclose(f); return GF_OUT_OF_MEM; } gf_list_add(currentPlayList->element.playlist.elements, subElement); @@ -717,7 +745,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const variant_playlist_del(*playlist); playlist_element_del(currentPlayList); playlist = NULL; - fclose(f); + if (f) fclose(f); return GF_OUT_OF_MEM; } gf_list_add(currentPlayList->element.playlist.elements, subElement); @@ -763,7 +791,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const } } } - fclose(f); + if (f) fclose(f); for (i=0; i < (int) gf_list_count(pl->programs); i++) { u32 j; diff --git a/src/media_tools/media_export.c b/src/media_tools/media_export.c index d112e28..ccb5283 100644 --- a/src/media_tools/media_export.c +++ b/src/media_tools/media_export.c @@ -42,8 +42,9 @@ #include #endif - +#ifndef GPAC_DISABLE_ZLIB #include +#endif GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc); @@ -287,9 +288,13 @@ GF_Err gf_media_export_samples(GF_MediaExporter *dumper) gf_export_message(dumper, GF_OK, "Dumping MPEG-4 Visual sample%s", szNum); break; case GPAC_OTI_VIDEO_AVC: - strcpy(szEXT, ".h264"); + strcpy(szEXT, ".264"); gf_export_message(dumper, GF_OK, "Dumping MPEG-4 AVC-H264 Visual sample%s", szNum); break; + case GPAC_OTI_VIDEO_HEVC: + strcpy(szEXT, ".hvc"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-H HEVC Visual sample%s", szNum); + break; case GPAC_OTI_IMAGE_JPEG: strcpy(szEXT, ".jpg"); gf_export_message(dumper, GF_OK, "Dumping JPEG image%s", szNum); @@ -374,9 +379,19 @@ GF_Err gf_media_export_samples(GF_MediaExporter *dumper) } else if (m_stype==GF_ISOM_SUBTYPE_AC3) { gf_export_message(dumper, GF_OK, "Extracting AC3 sample%s", szNum); strcpy(szEXT, ".ac3"); - } else if ((m_stype==GF_ISOM_SUBTYPE_AVC_H264) || (m_stype==GF_ISOM_SUBTYPE_AVC2_H264) || (m_stype==GF_ISOM_SUBTYPE_SVC_H264) ) { + } else if ((m_stype==GF_ISOM_SUBTYPE_AVC_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC2_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC3_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC4_H264) + || (m_stype==GF_ISOM_SUBTYPE_SVC_H264) + ) { strcpy(szEXT, ".h264"); gf_export_message(dumper, GF_OK, "Dumping MPEG-4 AVC-H264 Visual sample%s", szNum); + } else if ((m_stype==GF_ISOM_SUBTYPE_HVC1) + || (m_stype==GF_ISOM_SUBTYPE_HEV1) + ) { + strcpy(szEXT, ".hvc"); + gf_export_message(dumper, GF_OK, "Dumping MPEG-H HEVC Visual sample%s", szNum); } else if (m_type==GF_ISOM_MEDIA_FLASH) { gf_export_message(dumper, GF_OK, "Extracting Macromedia Flash Movie sample%s", szNum); strcpy(szEXT, ".swf"); @@ -617,7 +632,8 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) char szName[1000], szEXT[5], GUID[16]; FILE *out; unsigned int *qcp_rates, rt_cnt; /*contains constants*/ - GF_AVCConfig *avccfg; + GF_AVCConfig *avccfg, *svccfg; + GF_HEVCConfig *hevccfg; GF_M4ADecSpecInfo a_cfg; GF_BitStream *bs; u32 track, i, di, count, m_type, m_stype, dsi_size, qcp_type; @@ -629,8 +645,10 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) dsi_size = 0; dsi = NULL; + hevccfg = NULL; avccfg = NULL; - + svccfg = NULL; + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); return GF_BAD_PARAM; @@ -667,10 +685,17 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) break; case GPAC_OTI_VIDEO_AVC: avccfg = gf_isom_avc_config_get(dumper->file, track, 1); + svccfg = gf_isom_svc_config_get(dumper->file, track, 1); if (add_ext) strcat(szName, ".h264"); gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264"); break; + case GPAC_OTI_VIDEO_HEVC: + hevccfg = gf_isom_hevc_config_get(dumper->file, track, 1); + if (add_ext) + strcat(szName, ".hvc"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-H HEVC stream to hevc"); + break; case GPAC_OTI_VIDEO_MPEG1: if (add_ext) strcat(szName, ".m1v"); @@ -827,11 +852,24 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) strcat(szName, ".263"); } else if (m_stype==GF_ISOM_SUBTYPE_3GP_DIMS) { return gf_media_export_nhml(dumper, 1); - } else if ((m_stype==GF_ISOM_SUBTYPE_AVC_H264) || (m_stype==GF_ISOM_SUBTYPE_AVC2_H264) || (m_stype==GF_ISOM_SUBTYPE_SVC_H264) ) { + } else if ((m_stype==GF_ISOM_SUBTYPE_AVC_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC2_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC3_H264) + || (m_stype==GF_ISOM_SUBTYPE_AVC4_H264) + || (m_stype==GF_ISOM_SUBTYPE_SVC_H264) + ) { avccfg = gf_isom_avc_config_get(dumper->file, track, 1); + svccfg = gf_isom_svc_config_get(dumper->file, track, 1); if (add_ext) strcat(szName, ".h264"); gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264"); + } else if ((m_stype==GF_ISOM_SUBTYPE_HEV1) + || (m_stype==GF_ISOM_SUBTYPE_HVC1) + ) { + hevccfg = gf_isom_hevc_config_get(dumper->file, track, 1); + if (add_ext) + strcat(szName, ".hvc"); + gf_export_message(dumper, GF_OK, "Extracting MPEG-H HEVC stream to hevc"); } else if (m_type==GF_ISOM_MEDIA_FLASH) { gf_export_message(dumper, GF_OK, "Extracting Macromedia Flash Movie"); if (add_ext) @@ -863,8 +901,12 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) if (dumper->flags & GF_EXPORT_PROBE_ONLY) { if (dsi) gf_free(dsi); if (avccfg) gf_odf_avc_cfg_del(avccfg); + if (svccfg) gf_odf_avc_cfg_del(svccfg); + if (hevccfg) gf_odf_hevc_cfg_del(hevccfg); return GF_OK; } + if (dumper->flags & GF_EXPORT_SVC_LAYER) + gf_isom_set_nalu_extract_mode(dumper->file, track, GF_ISOM_NALU_EXTRACT_LAYER_ONLY); if (is_ogg) return gf_dump_to_ogg(dumper, is_stdout ? NULL : szName, track); @@ -897,6 +939,8 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) if (!out) { if (dsi) gf_free(dsi); if (avccfg) gf_odf_avc_cfg_del(avccfg); + if (svccfg) gf_odf_avc_cfg_del(svccfg); + if (hevccfg) gf_odf_hevc_cfg_del(hevccfg); return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName); } bs = gf_bs_from_file(out, GF_BITSTREAM_WRITE); @@ -925,6 +969,92 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) gf_bs_write_data(bs, sl->data, sl->size); } } + if (svccfg) { + if (!(dumper->flags & GF_EXPORT_SVC_LAYER)) + { + GF_AVCConfig *cfg; + u32 ref_track = 0, t; + s32 countRef; + + // copy avcC and svcC from base layer + gf_isom_get_reference(dumper->file, track, GF_ISOM_REF_BASE, 1, &ref_track); + cfg = gf_isom_avc_config_get(dumper->file, ref_track, 1); + if (cfg) + { + count = gf_list_count(cfg->sequenceParameterSets); + for (i =0; i < count; i++) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->sequenceParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(cfg->pictureParameterSets); + for (i = 0; i < count; i++) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->pictureParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + gf_odf_avc_cfg_del(cfg); + cfg = NULL; + } + + // copy avcC and svcC from lower layer + countRef = gf_isom_get_reference_count(dumper->file, track, GF_ISOM_REF_SCAL); + if (countRef < 0) + { + e = gf_isom_last_error(dumper->file); + goto exit; + } + for (t = 2; t <= (u32) countRef; t++) // referenceIndex 1 is the base layer + { + gf_isom_get_reference(dumper->file, track, GF_ISOM_REF_SCAL, t, &ref_track); + cfg = gf_isom_svc_config_get(dumper->file, ref_track, 1); + if (cfg) + { + count = gf_list_count(cfg->sequenceParameterSets); + for (i = 0; i < count; i++) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->sequenceParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(cfg->pictureParameterSets); + for (i = 0; i < count; i++) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->pictureParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + gf_odf_avc_cfg_del(cfg); + cfg = NULL; + } + } + } + + count = gf_list_count(svccfg->sequenceParameterSets); + for (i=0;isequenceParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(svccfg->pictureParameterSets); + for (i=0;ipictureParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + } + + + if (hevccfg) { + count = gf_list_count(hevccfg->param_array); + for (i=0;iparam_array, i); + for (j=0; jnalus); j++) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(ar->nalus, j); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, sl->data, sl->size); + } + } + } qcp_rates = NULL; count = gf_isom_get_sample_count(dumper->file, track); @@ -1039,19 +1169,23 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) break; } /*AVC sample to NALU*/ - if (avccfg) { - u32 j, nal_size, remain; + if (avccfg || svccfg || hevccfg) { + u32 j, nal_size, remain, nal_unit_size; char *ptr = samp->data; + nal_unit_size = 0; + if (avccfg) nal_unit_size= avccfg->nal_unit_size; + else if (svccfg) nal_unit_size = svccfg->nal_unit_size; + else if (hevccfg) nal_unit_size = hevccfg->nal_unit_size; remain = samp->dataLength; while (remain) { nal_size = 0; - if (remainnal_unit_size){ - GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Sample %d (size %d): Corrupted NAL Unit: header size %d - bytes left %d\n", i+1, samp->dataLength, avccfg->nal_unit_size, remain) ); + if (remaindataLength, nal_unit_size, remain) ); break; } - for (j=0; jnal_unit_size; j++) { + for (j=0; jnal_unit_size) nal_size<<=8; + if (j+1data, samp->dataLength); + if (!avccfg && !svccfg && !hevccfg) gf_bs_write_data(bs, samp->data, samp->dataLength); gf_isom_sample_del(&samp); gf_set_progress("Media Export", i+1, count); if (dumper->flags & GF_EXPORT_DO_ABORT) break; } if (has_qcp_pad) gf_bs_write_u8(bs, 0); - +exit: if (avccfg) gf_odf_avc_cfg_del(avccfg); + if (svccfg) gf_odf_avc_cfg_del(svccfg); gf_bs_del(bs); if (!is_stdout) fclose(out); @@ -1429,7 +1564,11 @@ static GF_Err MP4T_CopyTrack(GF_MediaExporter *dumper, GF_ISOFile *infile, u32 i if (msubtype == GF_ISOM_SUBTYPE_MPEG4_CRYP) { esd = gf_isom_get_esd(infile, inTrackNum, 1); - } else if ((msubtype == GF_ISOM_SUBTYPE_AVC_H264) || (msubtype == GF_ISOM_SUBTYPE_AVC2_H264) ) { + } else if ((msubtype == GF_ISOM_SUBTYPE_AVC_H264) + || (msubtype == GF_ISOM_SUBTYPE_AVC2_H264) + || (msubtype == GF_ISOM_SUBTYPE_AVC3_H264) + || (msubtype == GF_ISOM_SUBTYPE_AVC4_H264) + ) { return gf_isom_set_pl_indication(outfile, GF_ISOM_PL_VISUAL, 0x0F); } /*likely 3gp or any non-MPEG-4 isomedia file*/ @@ -1848,6 +1987,7 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) if (flags & GF_DIMS_UNIT_C) fprintf(nhml, " compressed=\"yes\""); fprintf(nhml, ">"); if (uncompress && (flags & GF_DIMS_UNIT_C)) { +#ifndef GPAC_DISABLE_ZLIB char svg_data[2049]; int err; u32 done = 0; @@ -1874,6 +2014,14 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) } inflateEnd(&d_stream); } +#else + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Error: your version of GPAC was compile with no libz support.")); + gf_bs_del(bs); + gf_isom_sample_del(&samp); + if (med) fclose(med); + fclose(nhml); + return GF_NOT_SUPPORTED; +#endif } else { gf_fwrite(samp->data+pos+3, size-1, 1, nhml); } @@ -2028,6 +2176,7 @@ GF_Err gf_media_export_saf(GF_MediaExporter *dumper) void m2ts_export_check(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { if (evt_type == GF_M2TS_EVT_PAT_REPEAT) ts->user = NULL; + else if (evt_type == GF_M2TS_EVT_PAT_FOUND) ts->segment_switch = 1; } void m2ts_export_dump(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { @@ -2073,11 +2222,12 @@ GF_Err gf_media_export_ts_native(GF_MediaExporter *dumper) gf_m2ts_process_data(ts, data, (u32)size); if (!ts->user) break; } - if (ts->user) { + if (!ts->segment_switch && ts->user) { fclose(src); gf_m2ts_demux_del(ts); return gf_export_message(dumper, GF_URL_ERROR, "Cannot locate program association table"); } + ts->segment_switch = 0; stream = NULL; for (i=0; i #include #include -/*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/ -#include #ifndef GPAC_DISABLE_MEDIA_IMPORT @@ -71,7 +69,9 @@ static GF_Err gf_media_update_par(GF_ISOFile *file, u32 track) if (e) return e; stype = gf_isom_get_media_subtype(file, track, 1); - if ((stype==GF_ISOM_SUBTYPE_AVC_H264) || (stype==GF_ISOM_SUBTYPE_AVC2_H264) ) { + if ((stype==GF_ISOM_SUBTYPE_AVC_H264) || (stype==GF_ISOM_SUBTYPE_AVC2_H264) + || (stype==GF_ISOM_SUBTYPE_AVC3_H264) || (stype==GF_ISOM_SUBTYPE_AVC4_H264) + ) { s32 par_n, par_d; GF_AVCConfig *avcc = gf_isom_avc_config_get(file, track, 1); GF_AVCConfigSlot *slc = (GF_AVCConfigSlot *)gf_list_get(avcc->sequenceParameterSets, 0); @@ -118,7 +118,7 @@ static void MP4T_RecomputeBitRate(GF_ISOFile *file, u32 track) esd->decoderConfig->avgBitrate = 0; esd->decoderConfig->maxBitrate = 0; rate = max_rate = avg_rate = time_wnd = 0; - + timescale = gf_isom_get_media_timescale(file, track); count = gf_isom_get_sample_count(file, track); for (i=0; iorig)); + Bool sbr, ps; GF_ISOSample *samp; GF_ESD *origin_esd; GF_InitialObjectDescriptor *iod; @@ -1589,6 +1589,7 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) ps = 0; sbr = 0; sbr_sr = 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); @@ -1643,72 +1644,16 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) gf_odf_desc_del((GF_Descriptor *) iod); - /*check if MPEG-4 or not - if crypted use clone track */ - is_clone = 0; - stype = gf_isom_get_media_subtype(import->orig, track_in, 1); - if ((stype==GF_ISOM_SUBTYPE_MPEG4) /*|| (stype==GF_ISOM_SUBTYPE_MPEG4_CRYP)*/) { - track = gf_isom_new_track(import->dest, import->esd ? import->esd->ESID : 0, gf_isom_get_media_type(import->orig, track_in), gf_isom_get_media_timescale(import->orig, track_in)); - if (!track) { - e = gf_isom_last_error(import->dest); - goto exit; - } - gf_isom_set_track_enabled(import->dest, track, 1); - if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); - /*setup data ref*/ - urn = url = NULL; - if (import->flags & GF_IMPORT_USE_DATAREF) { - url = gf_isom_get_filename(import->orig); - if (!gf_isom_is_self_contained(import->orig, track_in, 1)) { - e = gf_isom_get_data_reference(import->orig, track_in, 1, &url, &urn); - if (e) goto exit; - } - } - e = gf_isom_new_mpeg4_description(import->dest, track, origin_esd, (char *) url, (char *) urn, &di); - if (e) goto exit; - /*copy over language*/ - lang[3] = 0; - gf_isom_get_media_language(import->orig, track_in, lang); - gf_isom_set_media_language(import->dest, track, lang); + e = gf_isom_clone_track(import->orig, track_in, import->dest, (import->flags & GF_IMPORT_USE_DATAREF), &track); + if (e) goto exit; - } else { - if (! (import->flags & GF_IMPORT_KEEP_ALL_TRACKS) ) { - mstype = gf_isom_get_media_subtype(import->orig, track_in, 1); - switch (mstype) { - case GF_ISOM_SUBTYPE_MPEG4: - case GF_ISOM_SUBTYPE_MPEG4_CRYP: - case GF_ISOM_SUBTYPE_AVC_H264: - case GF_ISOM_SUBTYPE_AVC2_H264: - case GF_ISOM_SUBTYPE_SVC_H264: - case GF_ISOM_SUBTYPE_3GP_H263: - case GF_ISOM_SUBTYPE_3GP_AMR: - case GF_ISOM_SUBTYPE_3GP_AMR_WB: - case GF_ISOM_SUBTYPE_3GP_EVRC: - case GF_ISOM_SUBTYPE_3GP_QCELP: - case GF_ISOM_SUBTYPE_3GP_SMV: - break; - default: - switch (mtype) { - case GF_ISOM_MEDIA_HINT: - case GF_ISOM_MEDIA_TEXT: - case GF_ISOM_MEDIA_SUBT: - break; - default: - return gf_import_message(import, GF_OK, "IsoMedia import - skipping track ID %d (unknown type \'%s\')", trackID, gf_4cc_to_str(mstype)); - } - } + di = 1; - } - e = gf_isom_clone_track(import->orig, track_in, import->dest, (import->flags & GF_IMPORT_USE_DATAREF), &track); - is_clone = 1; - di = 1; + if (import->esd && import->esd->ESID) { + e = gf_isom_set_track_id(import->dest, track, import->esd->ESID); if (e) goto exit; - - if (import->esd && import->esd->ESID) { - e = gf_isom_set_track_id(import->dest, track, import->esd->ESID); - if (e) goto exit; - } } - 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); @@ -1716,29 +1661,21 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) switch (mtype) { case GF_ISOM_MEDIA_VISUAL: - if (!is_clone) { - gf_isom_set_visual_info(import->dest, track, di, w, h); - gf_media_update_par(import->dest, track); - } - gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - Video (size %d x %d)", trackID, w, h); + 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_AUDIO: { - if (!is_clone) gf_isom_set_audio_info(import->dest, track, di, (sbr==2) ? sbr_sr : sr, (ch>1) ? 2 : 1, bps); if (ps) { - gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - HE-AACv2 (SR %d - SBR-SR %d - %d channels)", trackID, sr, sbr_sr, ch); + 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 - track ID %d - HE-AAC (SR %d - SBR-SR %d - %d channels)", trackID, sr, sbr_sr, ch); + 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 - track ID %d - Audio (SR %d - %d channels)", trackID, sr, ch); + gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - Audio (SR %d - %d channels)", orig_name, trackID, sr, ch); } } break; case GF_ISOM_MEDIA_SUBPIC: - if (!is_clone) { - gf_isom_set_track_layout_info(import->dest, track, w << 16, h << 16, trans_x, trans_y, layer); - } - gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - VobSub (size %d x %d)", trackID, w, h); + gf_import_message(import, GF_OK, "IsoMedia import %s - track ID %d - VobSub (size %d x %d)", orig_name, trackID, w, h); break; default: { @@ -1746,14 +1683,13 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) mstype = gf_isom_get_mpeg4_subtype(import->orig, track_in, di); if (!mstype) mstype = gf_isom_get_media_subtype(import->orig, track_in, di); strcpy(szT, gf_4cc_to_str(mtype)); - gf_import_message(import, GF_OK, "IsoMedia import - track ID %d - media type \"%s:%s\"", - trackID, szT, gf_4cc_to_str(mstype)); + 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; } duration = (u64) (((Double)import->duration * gf_isom_get_media_timescale(import->orig, track_in)) / 1000); - + gf_isom_set_nalu_extract_mode(import->orig, track_in, GF_ISOM_NALU_EXTRACT_INSPECT); num_samples = gf_isom_get_sample_count(import->orig, track_in); for (i=0; iflags & GF_IMPORT_USE_DATAREF) { @@ -1808,6 +1744,7 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) exit: 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; } @@ -2478,6 +2415,12 @@ exit: return e; } + +#ifndef GPAC_DISABLE_ZLIB + +/*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/ +#include + #define ZLIB_COMPRESS_SAFE 4 static GF_Err compress_sample_data(GF_ISOSample *samp, u32 *max_size, char **dict, u32 offset) @@ -2534,6 +2477,8 @@ static GF_Err compress_sample_data(GF_ISOSample *samp, u32 *max_size, char **dic return GF_OK; } +#endif /*GPAC_DISABLE_ZLIB*/ + static void nhml_on_progress(void *cbk, u64 done, u64 tot) { gf_set_progress("NHML Loading", done, tot); @@ -3003,6 +2948,7 @@ GF_Err gf_import_nhml_dims(GF_MediaImporter *import, Bool dims_doc) } if (compress) { +#ifndef GPAC_DISABLE_ZLIB e = compress_sample_data(samp, &max_size, use_dict ? &dictionary : NULL, is_dims ? 3 : 0); if (e) goto exit; if (is_dims) { @@ -3010,6 +2956,11 @@ GF_Err gf_import_nhml_dims(GF_MediaImporter *import, Bool dims_doc) gf_bs_write_u16(bs, samp->dataLength-2); gf_bs_del(bs); } +#else + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: your version of GPAC was compile with no libz support. Abort.")); + e = GF_NOT_SUPPORTED; + goto exit; +#endif } if (is_dims && (samp->dataLength > 0xFFFF)) { e = gf_import_message(import, GF_BAD_PARAM, "DIMS import failure: sample data is too long - maximum size allowed: 65532 bytes"); @@ -3768,7 +3719,7 @@ GF_Err gf_media_avc_rewrite_samples(GF_ISOFile *file, u32 track, u32 prev_size, #ifndef GPAC_DISABLE_AV_PARSERS -GF_Err gf_import_h264(GF_MediaImporter *import) +static GF_Err gf_import_avc_h264(GF_MediaImporter *import) { u64 nal_start, nal_end, total_size; u32 nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, max_total_delay; @@ -3837,7 +3788,7 @@ restart_import: sei_recovery_frame_count = -1; bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); - if (!AVC_IsStartCode(bs)) { + if (!gf_media_nalu_is_start_code(bs)) { e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Cannot find H264 start code"); goto exit; } @@ -3893,7 +3844,7 @@ restart_import: while (gf_bs_available(bs)) { u8 nal_hdr, skip_nal, is_subseq, add_sps; - nal_size = AVC_NextStartCode(bs); + nal_size = gf_media_nalu_next_start_code_bs(bs); if (nal_size>max_size) { buffer = (char*)gf_realloc(buffer, sizeof(char)*nal_size); @@ -3915,7 +3866,7 @@ restart_import: avc.is_svc = 1; } - switch (AVC_ParseNALU(bs, nal_hdr, &avc)) { + switch (gf_media_avc_parse_nalu(bs, nal_hdr, &avc)) { case 1: flush_sample = 1; break; @@ -3933,7 +3884,7 @@ restart_import: case GF_AVC_NALU_SVC_SUBSEQ_PARAM: is_subseq = 1; case GF_AVC_NALU_SEQ_PARAM: - idx = AVC_ReadSeqInfo(buffer+1/*skip NALU type*/, nal_size-1, &avc, is_subseq, NULL); + idx = gf_media_avc_read_sps(buffer, nal_size, &avc, is_subseq, NULL); if (idx<0) { if (avc.sps[0].profile_idc) { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Error parsing SeqInfo")); @@ -4050,7 +4001,7 @@ restart_import: } break; case GF_AVC_NALU_PIC_PARAM: - idx = AVC_ReadPictParamSet(buffer+1/*skip NALU type*/, nal_size-1, &avc); + idx = gf_media_avc_read_pps(buffer, nal_size, &avc); if (idx<0) { e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param"); goto exit; @@ -4075,12 +4026,15 @@ restart_import: they will be moved to the SVC layer upon analysis of SVC slice. */ dstcfg = avccfg; + if (import->flags & GF_IMPORT_SVC_EXPLICIT) + dstcfg = svccfg; + gf_list_add(dstcfg->pictureParameterSets, slc); } break; case GF_AVC_NALU_SEI: if (avc.sps_active_idx != -1) { - copy_size = AVC_ReformatSEI_NALU(buffer, nal_size, &avc); + copy_size = gf_media_avc_reformat_sei(buffer, nal_size, &avc); if (copy_size) nb_sei++; } @@ -4149,7 +4103,7 @@ restart_import: break; case GF_AVC_NALU_SEQ_PARAM_EXT: - idx = AVC_ReadSeqParamSetExtId(buffer+1/*skip NALU type*/, nal_size-1); + idx = gf_media_avc_read_sps_ext(buffer, nal_size); if (idx<0) { e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Sequence Param Extension"); goto exit; @@ -4363,7 +4317,7 @@ restart_import: if (first_nal) { first_nal = 0; if (avc.sei.recovery_point.valid || (import->flags & GF_IMPORT_FORCE_SYNC)) { - Bool bIntraSlice = AVC_SliceIsIntra(&avc); + Bool bIntraSlice = gf_media_avc_slice_is_intra(&avc); assert(avc.s_info.nal_unit_type!=GF_AVC_NALU_IDR_SLICE || bIntraSlice); sei_recovery_frame_count = avc.sei.recovery_point.frame_cnt; @@ -4380,7 +4334,7 @@ restart_import: if (bIntraSlice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) slice_force_ref = 1; } - sample_is_rap = AVC_SliceIsIDR(&avc); + sample_is_rap = gf_media_avc_slice_is_IDR(&avc); } if (avc.s_info.pocflags & GF_IMPORT_DO_ABORT) break; /*consume next start code*/ - nal_start = AVC_NextStartCode(bs); + nal_start = gf_media_nalu_next_start_code_bs(bs); if (nal_start) { GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[avc-h264] invalid nal_size (%u)? Skipping "LLU" bytes to reach next start code\n", nal_size, nal_start)); gf_bs_skip_bytes(bs, nal_start); } - nal_start = AVC_IsStartCode(bs); + nal_start = gf_media_nalu_is_start_code(bs); if (!nal_start) { GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[avc-h264] error: no start code found ("LLU" bytes read out of "LLU") - leaving\n", gf_bs_get_position(bs), gf_bs_get_size(bs))); break; } nal_start = gf_bs_get_position(bs); } - + /*final flush*/ if (sample_data) { GF_ISOSample *samp = gf_isom_sample_new(); @@ -4644,157 +4598,901 @@ exit: return e; } -#endif /*GPAC_DISABLE_AV_PARSERS*/ -#ifndef GPAC_DISABLE_OGG +static GF_Err gf_import_hevc(GF_MediaImporter *import) +{ + u64 nal_start, nal_end, total_size; + u32 nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, max_total_delay; + s32 idx, sei_recovery_frame_count; + u64 duration; + GF_Err e; + FILE *mdia; + HEVCState hevc; + GF_AVCConfigSlot *slc; + GF_HEVCConfig *hevccfg; + GF_HEVCParamArray *spss, *ppss, *vpss; + GF_BitStream *bs; + GF_BitStream *sample_data; + Bool flush_sample, sample_is_rap, sample_has_islice, first_nal, slice_is_ref, has_cts_offset, is_paff, set_subsamples, slice_force_ref; + u32 ref_frame, timescale, copy_size, size_length, dts_inc; + s32 last_poc, max_last_poc, max_last_b_poc, poc_diff, prev_last_poc, min_poc, poc_shift; + Bool first_avc; + Bool use_opengop_gdr = 0; -#define OGG_BUFFER_SIZE 4096 + Double FPS; + char *buffer; + u32 max_size = 4096; -Bool OGG_ReadPage(FILE *f_in, ogg_sync_state *oy, ogg_page *oggpage) -{ - if (feof(f_in)) return 0; - while (ogg_sync_pageout(oy, oggpage ) != 1 ) { - char *buffer = ogg_sync_buffer(oy, OGG_BUFFER_SIZE); - u32 bytes = fread(buffer, sizeof(char), OGG_BUFFER_SIZE, f_in); - ogg_sync_wrote(oy, bytes); - if (feof(f_in)) return 1; + if (import->flags & GF_IMPORT_PROBE_ONLY) { + import->nb_tracks = 1; + import->tk_info[0].track_num = 1; + import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[0].flags = GF_IMPORT_OVERRIDE_FPS | GF_IMPORT_FORCE_PACKED; + return GF_OK; } - return 1; -} -static u32 get_ogg_serial_no_for_stream(char *fileName, u32 stream_num, Bool is_video) -{ - ogg_sync_state oy; - u32 track, serial_no; - ogg_page oggpage; - ogg_packet oggpacket; - ogg_stream_state os; - FILE *f_in; + set_subsamples = (import->flags & GF_IMPORT_SET_SUBSAMPLES) ? 1 : 0; - /*means first one*/ - if (!stream_num) return 0; + mdia = gf_f64_open(import->in_name, "rb"); + if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name); - f_in = gf_f64_open(fileName, "rb"); - if (!f_in) return 0; + //detect_fps = 1; + FPS = (Double) import->video_fps; + if (!FPS) { + FPS = GF_IMPORT_DEFAULT_FPS; + } else { + if (import->video_fps == GF_IMPORT_AUTO_FPS) { + import->video_fps = GF_IMPORT_DEFAULT_FPS; /*fps=auto is handled as auto-detection is h264*/ + } else { + /*fps is forced by the caller*/ + //detect_fps = 0; + } + } + get_video_timing(FPS, ×cale, &dts_inc); - track = 0; - serial_no = 0; - ogg_sync_init(&oy); - while (1) { - if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; - if (!ogg_page_bos(&oggpage)) break; - track ++; - if (track != stream_num) continue; + poc_diff = 0; - serial_no = ogg_page_serialno(&oggpage); - ogg_stream_init(&os, serial_no); - ogg_stream_pagein(&os, &oggpage); - ogg_stream_packetpeek(&os, &oggpacket); - if (is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { - ogg_stream_clear(&os); - break; - } - if (!is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { - ogg_stream_clear(&os); - break; - } - ogg_stream_clear(&os); - serial_no = 0; - } - ogg_sync_clear(&oy); - fclose(f_in); - return serial_no; -} +//restart_import: -GF_Err gf_import_ogg_video(GF_MediaImporter *import) -{ - GF_Err e; - ogg_sync_state oy; - u32 di, track; - u64 tot_size, done, duration; - u32 w, h, fps_num, fps_den, keyframe_freq_force, theora_kgs, flag, dts_inc, timescale; - Double FPS; - Bool destroy_esd, go; - u32 serial_no, sno, num_headers; - ogg_packet oggpacket; - ogg_page oggpage; - ogg_stream_state os; - oggpack_buffer opb; - GF_BitStream *bs; - FILE *f_in; - GF_ISOSample *samp; + memset(&hevc, 0, sizeof(HEVCState)); + hevc.sps_active_idx = -1; + hevccfg = gf_odf_hevc_cfg_new(); + buffer = (char*)gf_malloc(sizeof(char) * max_size); + sample_data = NULL; + first_avc = 1; + sei_recovery_frame_count = -1; + spss = ppss = vpss = NULL; + bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); + if (!gf_media_nalu_is_start_code(bs)) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Cannot find H264 start code"); + goto exit; + } - dts_inc = 0; - /*assume audio or simple AV file*/ - if (import->flags & GF_IMPORT_PROBE_ONLY) { - f_in = gf_f64_open(import->in_name, "rb"); - if (!f_in) return GF_URL_ERROR; + /*NALU size packing disabled*/ + if (!(import->flags & GF_IMPORT_FORCE_PACKED)) size_length = 32; + /*if import in edit mode, use smallest NAL size and adjust on the fly*/ + else if (gf_isom_get_mode(import->dest)!=GF_ISOM_OPEN_WRITE) size_length = 8; + else size_length = 32; - import->nb_tracks = 0; - go = 1; - ogg_sync_init(&oy); - while (go) { - if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + trackID = 0; + e = GF_OK; + if (import->esd) trackID = import->esd->ESID; - if (!ogg_page_bos(&oggpage)) { - go = 0; - continue; - } - serial_no = ogg_page_serialno(&oggpage); - ogg_stream_init(&os, serial_no); - ogg_stream_pagein(&os, &oggpage); - ogg_stream_packetpeek(&os, &oggpacket); + track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_VISUAL, timescale); + if (!track) { + e = gf_isom_last_error(import->dest); + goto exit; + } + gf_isom_set_track_enabled(import->dest, track, 1); + if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track); + 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); + } - import->tk_info[import->nb_tracks].track_num = import->nb_tracks+1; - if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { - import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_VISUAL; - import->tk_info[import->nb_tracks].flags = GF_IMPORT_OVERRIDE_FPS; + e = gf_isom_hevc_config_new(import->dest, track, hevccfg, NULL, NULL, &di); + if (e) goto exit; - bs = gf_bs_new((char*)oggpacket.packet, oggpacket.bytes, GF_BITSTREAM_READ); - gf_bs_read_int(bs, 80); - import->tk_info[import->nb_tracks].video_info.width = gf_bs_read_u16(bs) << 4; - import->tk_info[import->nb_tracks].video_info.height = gf_bs_read_u16(bs) << 4; - gf_bs_read_int(bs, 64); - fps_num = gf_bs_read_u32(bs); - fps_den = gf_bs_read_u32(bs); - gf_bs_del(bs); - import->tk_info[import->nb_tracks].video_info.FPS = fps_num; - import->tk_info[import->nb_tracks].video_info.FPS /= fps_den; - import->tk_info[import->nb_tracks].media_type = GF_4CC('t','h','e','o'); - } else if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { - import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_AUDIO; - import->tk_info[import->nb_tracks].flags = 0; - } - ogg_stream_clear(&os); - import->nb_tracks++; - } - ogg_sync_clear(&oy); - fclose(f_in); - return GF_OK; - } + sample_data = NULL; + sample_is_rap = 0; + sample_has_islice = 0; + cur_samp = 0; + is_paff = 0; + total_size = gf_bs_get_size(bs); + nal_start = gf_bs_get_position(bs); + duration = (u64) ( ((Double)import->duration) * timescale / 1000.0); - if (import->flags & GF_IMPORT_USE_DATAREF) return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with OGG files"); + nb_i = nb_idr = nb_p = nb_b = nb_sp = nb_si = nb_sei = 0; + max_w = max_h = 0; + first_nal = 1; + ref_frame = 0; + last_poc = max_last_poc = max_last_b_poc = prev_last_poc = 0; + max_total_delay = 0; - sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 1); - /*not our stream*/ - if (!sno && import->trackID) return GF_OK; + gf_isom_set_cts_packing(import->dest, track, 1); + has_cts_offset = 0; + min_poc = 0; + poc_shift = 0; - f_in = gf_f64_open(import->in_name, "rb"); - if (!f_in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + while (gf_bs_available(bs)) { + s32 res; + u8 nal_unit_type, temporal_id; + Bool skip_nal, add_sps, is_slice; + nal_size = gf_media_nalu_next_start_code_bs(bs); - e = GF_OK; - done = 0; - gf_f64_seek(f_in, 0, SEEK_END); - tot_size = gf_f64_tell(f_in); - gf_f64_seek(f_in, 0, SEEK_SET); + if (nal_size>max_size) { + buffer = (char*)gf_realloc(buffer, sizeof(char)*nal_size); + max_size = nal_size; + } + /*read the file, and work on a memory buffer*/ + gf_bs_read_data(bs, buffer, nal_size); - destroy_esd = 0; - samp = gf_isom_sample_new(); + gf_bs_seek(bs, nal_start); - /*avoids gcc warnings*/ + res = gf_media_hevc_parse_nalu(bs, &hevc, &nal_unit_type, &temporal_id); + + skip_nal = 0; + copy_size = flush_sample = 0; + is_slice = 0; + + switch (res) { + case 1: + flush_sample = 1; + break; + case -1: + gf_import_message(import, GF_OK, "Waring: Error parsing NAL unit"); + skip_nal = 1; + break; + case -2: + skip_nal = 1; + break; + default: + break; + } + switch (nal_unit_type) { + case GF_HEVC_NALU_VID_PARAM: + idx = gf_media_hevc_read_vps(buffer, nal_size , &hevc); + if (idx<0) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param"); + goto exit; + } + /*if we get twice the same VPS put in the the bitstream and set array_completeness to 0 ...*/ + if (hevc.vps[idx].state == 2) { + copy_size = nal_size; + assert(vpss); + vpss->array_completeness = 0; + } + + if (hevc.vps[idx].state==1) { + hevc.vps[idx].state = 2; + + hevccfg->avgFrameRate = hevc.vps[idx].rates[0].avg_pic_rate; + hevccfg->constantFrameRate = hevc.vps[idx].rates[0].constand_pic_rate_idc; + hevccfg->numTemporalLayers = hevc.vps[idx].max_sub_layer; + + if (!vpss) { + GF_SAFEALLOC(vpss, GF_HEVCParamArray); + vpss->nalus = gf_list_new(); + gf_list_add(hevccfg->param_array, vpss); + vpss->array_completeness = 1; + vpss->type = GF_HEVC_NALU_VID_PARAM; + } + + slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + slc->size = nal_size; + slc->id = idx; + slc->data = (char*)gf_malloc(sizeof(char)*slc->size); + memcpy(slc->data, buffer, sizeof(char)*slc->size); + + gf_list_add(vpss->nalus, slc); + } + break; + case GF_HEVC_NALU_SEQ_PARAM: + idx = gf_media_hevc_read_sps(buffer, nal_size, &hevc); + if (idx<0) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing SeqInfo"); + goto exit; + } + add_sps = 0; + if ((hevc.sps[idx].state & AVC_SPS_PARSED) && !(hevc.sps[idx].state & AVC_SPS_DECLARED)) { + hevc.sps[idx].state |= AVC_SPS_DECLARED; + add_sps = 1; + } + + /*if we get twice the same VPS put in the the bitstream and set array_completeness to 0 ...*/ + if (hevc.sps[idx].state & AVC_SUBSPS_DECLARED) { + if (import->flags & GF_IMPORT_SVC_NONE) { + copy_size = 0; + } else { + copy_size = nal_size; + assert(spss); + spss->array_completeness = 0; + } + } + + if (add_sps) { + hevccfg->configurationVersion = 1; + hevccfg->profile_space = hevc.sps[idx].ptl.profile_space; + hevccfg->profile_idc = hevc.sps[idx].ptl.profile_idc; + hevccfg->constraint_indicator_flags = 0; + hevccfg->level_idc = hevc.sps[idx].ptl.level_idc; + hevccfg->profile_compatibility_indications = hevc.sps[idx].ptl.profile_compatibility_flag; + hevccfg->chromaFormat = hevc.sps[idx].chroma_format_idc; + hevccfg->luma_bit_depth = hevc.sps[idx].bit_depth_luma; + hevccfg->chroma_bit_depth = hevc.sps[idx].bit_depth_chroma; + + /* + todo FPS detection + + timescale = 2 * avc.sps[idx].vui.time_scale; + dts_inc = 2 * avc.sps[idx].vui.num_units_in_tick * DeltaTfiDivisorIdx; + FPS = (Double)timescale / dts_inc; + detect_fps = 0; + gf_isom_remove_track(import->dest, track); + if (sample_data) gf_bs_del(sample_data); + gf_odf_avc_cfg_del(avccfg); + avccfg = NULL; + gf_free(buffer); + buffer = NULL; + gf_bs_del(bs); + bs = NULL; + gf_f64_seek(mdia, 0, SEEK_SET); + goto restart_import; + } + */ + + if (!spss) { + GF_SAFEALLOC(spss, GF_HEVCParamArray); + spss->nalus = gf_list_new(); + gf_list_add(hevccfg->param_array, spss); + spss->array_completeness = 1; + spss->type = GF_HEVC_NALU_SEQ_PARAM; + } + + slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + slc->size = nal_size; + slc->id = idx; + slc->data = (char*)gf_malloc(sizeof(char)*slc->size); + memcpy(slc->data, buffer, sizeof(char)*slc->size); + gf_list_add(spss->nalus, slc); + + if (first_avc) { + first_avc = 0; + gf_import_message(import, GF_OK, "AVC-H264 import - frame size %d x %d at %02.3f FPS", hevc.sps[idx].width, hevc.sps[idx].height, FPS); + } + + if ((max_w <= hevc.sps[idx].width) && (max_h <= hevc.sps[idx].height)) { + max_w = hevc.sps[idx].width; + max_h = hevc.sps[idx].height; + } + } + break; + + case GF_HEVC_NALU_PIC_PARAM: + idx = gf_media_hevc_read_pps(buffer, nal_size, &hevc); + if (idx<0) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param"); + goto exit; + } + /*if we get twice the same VPS put in the the bitstream and set array_completeness to 0 ...*/ + if (hevc.pps[idx].state == 2) { + copy_size = nal_size; + assert(ppss); + ppss->array_completeness = 0; + } + + if (hevc.pps[idx].state==1) { + hevc.pps[idx].state = 2; + + if (!ppss) { + GF_SAFEALLOC(ppss, GF_HEVCParamArray); + ppss->nalus = gf_list_new(); + gf_list_add(hevccfg->param_array, ppss); + ppss->array_completeness = 1; + ppss->type = GF_HEVC_NALU_PIC_PARAM; + } + + slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + slc->size = nal_size; + slc->id = idx; + slc->data = (char*)gf_malloc(sizeof(char)*slc->size); + memcpy(slc->data, buffer, sizeof(char)*slc->size); + + gf_list_add(ppss->nalus, slc); + } + break; + case GF_HEVC_NALU_SEI_PREFIX: + if (hevc.sps_active_idx != -1) { + /*TODO*/ + //copy_size = gf_media_avc_reformat_sei(buffer, nal_size, &hevc); + copy_size = nal_size; + if (copy_size) + nb_sei++; + } + break; + + /*slice_layer_rbsp*/ +// case GF_HEVC_NALU_SLICE_STSA_N: +// case GF_HEVC_NALU_SLICE_STSA_R: + case GF_HEVC_NALU_SLICE_RADL_N: +// case GF_HEVC_NALU_SLICE_RADL_R: + case GF_HEVC_NALU_SLICE_RASL_N: +// case GF_HEVC_NALU_SLICE_RASL_R: + is_slice = 1; + if (! skip_nal) { + copy_size = nal_size; + } + break; + + /*slice_segment_layer_rbsp*/ + case GF_HEVC_NALU_SLICE_STSA_N: + case GF_HEVC_NALU_SLICE_STSA_R: + case GF_HEVC_NALU_SLICE_RADL_R: + case GF_HEVC_NALU_SLICE_RASL_R: + case GF_HEVC_NALU_SLICE_TRAIL_N: + case GF_HEVC_NALU_SLICE_TRAIL_R: + case GF_HEVC_NALU_SLICE_TSA_N: + case GF_HEVC_NALU_SLICE_TSA_R: + case GF_HEVC_NALU_SLICE_BLA_W_LP: + case GF_HEVC_NALU_SLICE_BLA_W_DLP: + case GF_HEVC_NALU_SLICE_BLA_N_LP: + case GF_HEVC_NALU_SLICE_IDR_W_DLP: + case GF_HEVC_NALU_SLICE_IDR_N_LP: + case GF_HEVC_NALU_SLICE_CRA: + is_slice = 1; + if (! skip_nal) { + copy_size = nal_size; + switch (hevc.s_info.slice_type) { + case GF_HEVC_TYPE_P: nb_p++; break; + case GF_HEVC_TYPE_I: nb_i++; + sample_has_islice = 1; + break; + case GF_HEVC_TYPE_B: nb_b++; break; + } + } + break; + + /*remove*/ + case GF_HEVC_NALU_ACCESS_UNIT: + case GF_HEVC_NALU_FILLER_DATA: + case GF_HEVC_NALU_END_OF_SEQ: + case GF_HEVC_NALU_END_OF_STREAM: + break; + + default: + gf_import_message(import, GF_OK, "WARNING: NAL Unit type %d not handled - adding", nal_unit_type); + copy_size = nal_size; + break; + } + + if (!nal_size) break; + + if (flush_sample && sample_data) { + GF_ISOSample *samp = gf_isom_sample_new(); + samp->DTS = (u64)dts_inc*cur_samp; + samp->IsRAP = sample_is_rap; + if (!sample_is_rap) { + if (sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) { + samp->IsRAP = 1; + if (!use_opengop_gdr) { + use_opengop_gdr = 1; + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AVC Import] Forcing non-IDR samples with I slices to be marked as sync points - resulting file will not be ISO conformant\n")); + } + } + } + gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); + gf_bs_del(sample_data); + sample_data = NULL; + + + + /*CTS recomuting is much trickier than with MPEG-4 ASP due to b-slice used as references - we therefore + store the POC as the CTS offset and update the whole table at the end*/ + samp->CTS_Offset = last_poc - poc_shift; + assert(last_poc >= poc_shift); + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + + cur_samp++; + + /*write sampleGroups info*/ + if (!samp->IsRAP && (sei_recovery_frame_count>=0)) { + /*generic GDR*/ + if (sei_recovery_frame_count) { + if (!use_opengop_gdr) use_opengop_gdr = 1; + e = gf_isom_set_sample_roll_group(import->dest, track, cur_samp, (s16) sei_recovery_frame_count); + } + /*open-GOP*/ + else if (sample_has_islice) { + if (!use_opengop_gdr) use_opengop_gdr = 2; + e = gf_isom_set_sample_rap_group(import->dest, track, cur_samp, 0); + } + if (e) goto exit; + } + + gf_isom_sample_del(&samp); + gf_set_progress("Importing AVC-H264", (u32) (nal_start/1024), (u32) (total_size/1024) ); + first_nal = 1; + + if (min_poc > last_poc) + min_poc = last_poc; + + sample_has_islice = 0; + sei_recovery_frame_count = -1; + } + + if (copy_size) { + if ((size_length<32) && ( (u32) (1<dest, track, size_length, size_length+diff_size); + + /*rewrite current sample*/ + if (sample_data) { + char *sd; + u32 sd_l; + GF_BitStream *prev_sd; + gf_bs_get_content(sample_data, &sd, &sd_l); + gf_bs_del(sample_data); + sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + prev_sd = gf_bs_new(sd, sd_l, GF_BITSTREAM_READ); + while (gf_bs_available(prev_sd)) { + char *buf; + u32 s = gf_bs_read_int(prev_sd, size_length); + gf_bs_write_int(sample_data, s, size_length+diff_size); + buf = (char*)gf_malloc(sizeof(char)*s); + gf_bs_read_data(prev_sd, buf, s); + gf_bs_write_data(sample_data, buf, s); + gf_free(buf); + } + gf_bs_del(prev_sd); + gf_free(sd); + } + size_length+=diff_size; + + } + if (!sample_data) sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(sample_data, copy_size, size_length); + gf_bs_write_data(sample_data, buffer, copy_size); + + if (set_subsamples) { + /* use the res and priority value of last prefix NALU */ + gf_isom_add_subsample(import->dest, track, cur_samp+1, copy_size+size_length/8, 0, 0, 0); + } + + if (is_slice) { + slice_is_ref = gf_media_hevc_slice_is_IDR(&hevc); + if (slice_is_ref) + nb_idr++; + slice_force_ref = 0; + + /*we only indicate TRUE IDRs for sync samples (cf AVC file format spec). + SEI recovery should be used to build sampleToGroup & RollRecovery tables*/ + if (first_nal) { + first_nal = 0; + if (hevc.sei.recovery_point.valid || (import->flags & GF_IMPORT_FORCE_SYNC)) { + Bool bIntraSlice = gf_media_hevc_slice_is_intra(&hevc); + assert(hevc.s_info.nal_unit_type!=GF_AVC_NALU_IDR_SLICE || bIntraSlice); + + sei_recovery_frame_count = hevc.sei.recovery_point.frame_cnt; + + /*we allow to mark I-frames as sync on open-GOPs (with sei_recovery_frame_count=0) when forcing sync even when the SEI RP is not available*/ + if (!hevc.sei.recovery_point.valid && bIntraSlice) { + sei_recovery_frame_count = 0; + if (use_opengop_gdr == 1) { + use_opengop_gdr = 2; /*avoid message flooding*/ + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AVC Import] No valid SEI Recovery Point found although needed - forcing\n")); + } + } + hevc.sei.recovery_point.valid = 0; + if (bIntraSlice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) + slice_force_ref = 1; + } + sample_is_rap = gf_media_hevc_slice_is_IDR(&hevc); + } + + if (hevc.s_info.pocdest, track, j, NULL, NULL); + if (!samp) break; + samp->CTS_Offset += poc_shift; + samp->CTS_Offset -= hevc.s_info.poc; + gf_isom_modify_cts_offset(import->dest, track, j, samp->CTS_Offset); + gf_isom_sample_del(&samp); + } + } + poc_shift = hevc.s_info.poc; + } + + /*if #pics, compute smallest POC increase*/ + if (hevc.s_info.poc != last_poc) { + if (!poc_diff || (poc_diff > abs(hevc.s_info.poc-last_poc))) { + poc_diff = abs(hevc.s_info.poc - last_poc);/*ideally we would need to start the parsing again as poc_diff helps computing max_total_delay*/ + } + last_poc = hevc.s_info.poc; + } + + /*ref slice, reset poc*/ + if (slice_is_ref) { + ref_frame = cur_samp+1; + max_last_poc = last_poc = max_last_b_poc = 0; + poc_shift = 0; + } + /*forced ref slice*/ + else if (slice_force_ref) { + ref_frame = cur_samp+1; + /*adjust POC shift as sample will now be marked as sync, so wo must store poc as if IDR (eg POC=0) for our CTS offset computing to be correct*/ + poc_shift = hevc.s_info.poc; + } + /*strictly less - this is a new P slice*/ + else if (max_last_poclast_poc) { + /*need to store TS offsets*/ + has_cts_offset = 1; + switch (hevc.s_info.slice_type) { + case GF_AVC_TYPE_B: + case GF_AVC_TYPE2_B: + if (!max_last_b_poc) { + max_last_b_poc = last_poc; + } + /*if same poc than last max, this is a B-slice*/ + else if (last_poc>max_last_b_poc) { + max_last_b_poc = last_poc; + } + /*otherwise we had a B-slice reference: do nothing*/ + + break; + } + } + + /*compute max delay (applicable when B slice are present)*/ + if (ref_frame && poc_diff && (s32)(cur_samp-(ref_frame-1)-last_poc/poc_diff)>(s32)max_total_delay) { + max_total_delay = cur_samp - (ref_frame-1) - last_poc/poc_diff; + } + } + } + + gf_bs_align(bs); + nal_end = gf_bs_get_position(bs); + assert(nal_start <= nal_end); + assert(nal_end <= nal_start + nal_size); + if (nal_end != nal_start + nal_size) + gf_bs_seek(bs, nal_start + nal_size); + + if (!gf_bs_available(bs)) break; + if (duration && (dts_inc*cur_samp > duration)) break; + if (import->flags & GF_IMPORT_DO_ABORT) break; + + /*consume next start code*/ + nal_start = gf_media_nalu_next_start_code_bs(bs); + if (nal_start) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[avc-h264] invalid nal_size (%u)? Skipping "LLU" bytes to reach next start code\n", nal_size, nal_start)); + gf_bs_skip_bytes(bs, nal_start); + } + nal_start = gf_media_nalu_is_start_code(bs); + if (!nal_start) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[avc-h264] error: no start code found ("LLU" bytes read out of "LLU") - leaving\n", gf_bs_get_position(bs), gf_bs_get_size(bs))); + break; + } + nal_start = gf_bs_get_position(bs); + } + + /*final flush*/ + if (sample_data) { + GF_ISOSample *samp = gf_isom_sample_new(); + samp->DTS = (u64)dts_inc*cur_samp; + samp->IsRAP = sample_is_rap; + if (!sample_is_rap && sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC)) { + samp->IsRAP = 1; + } + /*we store the frame order (based on the POC) as the CTS offset and update the whole table at the end*/ + samp->CTS_Offset = last_poc - poc_shift; + gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); + gf_bs_del(sample_data); + sample_data = NULL; + e = gf_isom_add_sample(import->dest, track, di, samp); + if (e) goto exit; + gf_isom_sample_del(&samp); + gf_set_progress("Importing AVC-H264", (u32) cur_samp, cur_samp+1); + cur_samp++; + } + + + /*recompute all CTS offsets*/ + if (has_cts_offset) { + u32 i, last_cts_samp; + u64 last_dts, max_cts; + if (!poc_diff) poc_diff = 1; + /*no b-frame references, no need to cope with negative poc*/ + if (!max_total_delay) { + min_poc=0; + max_total_delay = 1; + } + cur_samp = gf_isom_get_sample_count(import->dest, track); + min_poc *= -1; + last_dts = 0; + max_cts = 0; + last_cts_samp = 0; + + for (i=0; idest, track, i+1, NULL, NULL); + /*poc re-init (RAP and POC to 0, otherwise that's SEI recovery), update base DTS*/ + if (samp->IsRAP /*&& !samp->CTS_Offset*/) + last_dts = samp->DTS * (1+is_paff); + + /*CTS offset is frame POC (refers to last IDR)*/ + cts = (min_poc + (s32) samp->CTS_Offset) * dts_inc/poc_diff + (u32) last_dts; + + /*if PAFF, 2 pictures (eg poc) <=> 1 aggregated frame (eg sample), divide by 2*/ + if (is_paff) { + cts /= 2; + /*in some cases the poc is not on the top field - if that is the case, round up*/ + if (cts%dts_inc) { + cts = ((cts/dts_inc)+1)*dts_inc; + } + } + + /*B-frames offset*/ + cts += (u32) (max_total_delay*dts_inc); + + samp->CTS_Offset = (u32) (cts - samp->DTS); + + if (max_cts < samp->DTS + samp->CTS_Offset) { + max_cts = samp->DTS + samp->CTS_Offset; + last_cts_samp = i; + } + /*this should never happen, however some streams seem to do weird POC increases (cf sorenson streams, last 2 frames), + this should hopefully take care of some bugs and ensure proper CTS...*/ + if ((s32)samp->CTS_Offset<0) { + u32 j, k; + samp->CTS_Offset = 0; + gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset); + for (j=last_cts_samp; jdest, track, j+1, NULL, NULL); + for (k=j+1; k<=i; k++) { + GF_ISOSample *bsamp = gf_isom_get_sample_info(import->dest, track, k+1, NULL, NULL); + if (asamp->CTS_Offset+asamp->DTS==bsamp->CTS_Offset+bsamp->DTS) { + max_cts += dts_inc; + bsamp->CTS_Offset = (u32) (max_cts - bsamp->DTS); + gf_isom_modify_cts_offset(import->dest, track, k+1, bsamp->CTS_Offset); + } + gf_isom_sample_del(&bsamp); + } + gf_isom_sample_del(&asamp); + } + max_cts = samp->DTS + samp->CTS_Offset; + } else { + gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset); + } + gf_isom_sample_del(&samp); + } + /*and repack table*/ + gf_isom_set_cts_packing(import->dest, track, 0); + } else { + gf_isom_remove_cts_info(import->dest, track); + } + + gf_set_progress("Importing AVC-H264", (u32) cur_samp, cur_samp); + + gf_isom_set_visual_info(import->dest, track, di, max_w, max_h); + hevccfg->nal_unit_size = size_length/8; + + gf_isom_hevc_config_update(import->dest, track, 1, hevccfg); + gf_media_update_par(import->dest, track); + MP4T_RecomputeBitRate(import->dest, track); + +// gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, 0x15); + gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_ISO4, 1); + gf_isom_modify_alternate_brand(import->dest, GF_ISOM_BRAND_ISOM, 0); + gf_isom_modify_alternate_brand(import->dest, GF_4CC('h','v','c','1'), 1); + + if (!vpss || !ppss || !spss) { + e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Import results: No SPS or PPS found in the bitstream ! Nothing imported\n"); + } else { + if (nb_sp || nb_si) { + gf_import_message(import, GF_OK, "AVC Import results: %d samples - Slices: %d I %d P %d B %d SP %d SI - %d SEI - %d IDR", + cur_samp, nb_i, nb_p, nb_b, nb_sp, nb_si, nb_sei, nb_idr); + } else { + gf_import_message(import, GF_OK, "AVC Import results: %d samples - Slices: %d I %d P %d B - %d SEI - %d IDR", + cur_samp, nb_i, nb_p, nb_b, nb_sei, nb_idr); + } + + if (max_total_delay>1) { + gf_import_message(import, GF_OK, "Stream uses forward prediction - stream CTS offset: %d frames", max_total_delay); + } + } + + if (use_opengop_gdr==2) { + gf_import_message(import, GF_OK, "OpenGOP detected - adjusting file brand"); + gf_isom_modify_alternate_brand(import->dest, GF_4CC('i', 's', 'o', '6'), 1); + } + + /*rewrite ESD*/ + if (import->esd) { + if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); + import->esd->slConfig->predefined = 2; + import->esd->slConfig->timestampResolution = timescale; + if (import->esd->decoderConfig) gf_odf_desc_del((GF_Descriptor *)import->esd->decoderConfig); + import->esd->decoderConfig = gf_isom_get_decoder_config(import->dest, track, 1); + gf_isom_change_mpeg4_description(import->dest, track, 1, import->esd); + } + +exit: + if (sample_data) gf_bs_del(sample_data); + gf_odf_hevc_cfg_del(hevccfg); + gf_free(buffer); + gf_bs_del(bs); + fclose(mdia); + return e; +} + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +#ifndef GPAC_DISABLE_OGG + +#define OGG_BUFFER_SIZE 4096 + +Bool OGG_ReadPage(FILE *f_in, ogg_sync_state *oy, ogg_page *oggpage) +{ + if (feof(f_in)) return 0; + while (ogg_sync_pageout(oy, oggpage ) != 1 ) { + char *buffer = ogg_sync_buffer(oy, OGG_BUFFER_SIZE); + u32 bytes = fread(buffer, sizeof(char), OGG_BUFFER_SIZE, f_in); + ogg_sync_wrote(oy, bytes); + if (feof(f_in)) return 1; + } + return 1; +} + +static u32 get_ogg_serial_no_for_stream(char *fileName, u32 stream_num, Bool is_video) +{ + ogg_sync_state oy; + u32 track, serial_no; + ogg_page oggpage; + ogg_packet oggpacket; + ogg_stream_state os; + FILE *f_in; + + /*means first one*/ + if (!stream_num) return 0; + + f_in = gf_f64_open(fileName, "rb"); + if (!f_in) return 0; + + track = 0; + serial_no = 0; + ogg_sync_init(&oy); + while (1) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + if (!ogg_page_bos(&oggpage)) break; + track ++; + if (track != stream_num) continue; + + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + + if (is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { + ogg_stream_clear(&os); + break; + } + if (!is_video && (oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { + ogg_stream_clear(&os); + break; + } + ogg_stream_clear(&os); + serial_no = 0; + } + ogg_sync_clear(&oy); + fclose(f_in); + return serial_no; +} + +GF_Err gf_import_ogg_video(GF_MediaImporter *import) +{ + GF_Err e; + ogg_sync_state oy; + u32 di, track; + u64 tot_size, done, duration; + u32 w, h, fps_num, fps_den, keyframe_freq_force, theora_kgs, flag, dts_inc, timescale; + Double FPS; + Bool destroy_esd, go; + u32 serial_no, sno, num_headers; + ogg_packet oggpacket; + ogg_page oggpage; + ogg_stream_state os; + oggpack_buffer opb; + GF_BitStream *bs; + FILE *f_in; + GF_ISOSample *samp; + + + dts_inc = 0; + /*assume audio or simple AV file*/ + if (import->flags & GF_IMPORT_PROBE_ONLY) { + f_in = gf_f64_open(import->in_name, "rb"); + if (!f_in) return GF_URL_ERROR; + + import->nb_tracks = 0; + go = 1; + ogg_sync_init(&oy); + while (go) { + if (!OGG_ReadPage(f_in, &oy, &oggpage)) break; + + if (!ogg_page_bos(&oggpage)) { + go = 0; + continue; + } + serial_no = ogg_page_serialno(&oggpage); + ogg_stream_init(&os, serial_no); + ogg_stream_pagein(&os, &oggpage); + ogg_stream_packetpeek(&os, &oggpacket); + + import->tk_info[import->nb_tracks].track_num = import->nb_tracks+1; + if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "theora", 6)) { + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[import->nb_tracks].flags = GF_IMPORT_OVERRIDE_FPS; + + bs = gf_bs_new((char*)oggpacket.packet, oggpacket.bytes, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 80); + import->tk_info[import->nb_tracks].video_info.width = gf_bs_read_u16(bs) << 4; + import->tk_info[import->nb_tracks].video_info.height = gf_bs_read_u16(bs) << 4; + gf_bs_read_int(bs, 64); + fps_num = gf_bs_read_u32(bs); + fps_den = gf_bs_read_u32(bs); + gf_bs_del(bs); + import->tk_info[import->nb_tracks].video_info.FPS = fps_num; + import->tk_info[import->nb_tracks].video_info.FPS /= fps_den; + import->tk_info[import->nb_tracks].media_type = GF_4CC('t','h','e','o'); + } else if ((oggpacket.bytes >= 7) && !strncmp((char *)&oggpacket.packet[1], "vorbis", 6)) { + import->tk_info[import->nb_tracks].type = GF_ISOM_MEDIA_AUDIO; + import->tk_info[import->nb_tracks].flags = 0; + } + ogg_stream_clear(&os); + import->nb_tracks++; + } + ogg_sync_clear(&oy); + fclose(f_in); + return GF_OK; + } + + if (import->flags & GF_IMPORT_USE_DATAREF) return gf_import_message(import, GF_NOT_SUPPORTED, "Cannot use data referencing with OGG files"); + + sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 1); + /*not our stream*/ + if (!sno && import->trackID) return GF_OK; + + f_in = gf_f64_open(import->in_name, "rb"); + if (!f_in) return gf_import_message(import, GF_URL_ERROR, "Opening file %s failed", import->in_name); + + e = GF_OK; + done = 0; + gf_f64_seek(f_in, 0, SEEK_END); + tot_size = gf_f64_tell(f_in); + gf_f64_seek(f_in, 0, SEEK_SET); + + + destroy_esd = 0; + samp = gf_isom_sample_new(); + + /*avoids gcc warnings*/ duration = 0; FPS = 0; num_headers = w = h = track = 0; @@ -5395,6 +6093,9 @@ typedef struct #ifndef GPAC_DISABLE_AV_PARSERS GF_AVCConfig *avccfg; AVCState avc; + + GF_HEVCConfig *hevccfg; + HEVCState hevc; #endif Bool force_next_au_start; Bool stream_setup; @@ -5597,6 +6298,34 @@ void m2ts_rewrite_avc_sample(GF_MediaImporter *import, GF_TSImport *tsimp) gf_isom_sample_del(&samp); } +static void hevc_cfg_add_nalu(GF_HEVCConfig *hevccfg, u8 nal_type, char *data, u32 data_len) +{ + u32 i, count; + GF_AVCConfigSlot *sl; + GF_HEVCParamArray *ar = NULL; + count = gf_list_count(hevccfg->param_array); + for (i=0; iparam_array, i); + if (ar->type == nal_type) break; + ar = NULL; + } + if (!ar) { + GF_SAFEALLOC(ar, GF_HEVCParamArray); + ar->array_completeness = 1; + ar->type = nal_type; + ar->nalus = gf_list_new(); + } + GF_SAFEALLOC(sl, GF_AVCConfigSlot); + if (data) { + sl->data = gf_malloc(sizeof(char)*data_len); + sl->size = data_len; + memcpy(sl->data, data, data_len); + gf_list_add(ar->nalus, ar); + } else { + ar->array_completeness = 0; + } +} + void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { GF_Err e; @@ -5688,6 +6417,13 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) import->nb_tracks++; tsimp->nb_video++; break; + case GF_M2TS_VIDEO_HEVC: + import->tk_info[idx].media_type = GF_4CC('h','e','v','c'); + import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; + import->tk_info[idx].lang = pes->lang; + import->nb_tracks++; + tsimp->nb_video++; + break; case GF_M2TS_AUDIO_MPEG1: import->tk_info[idx].media_type = GF_4CC('M','P','G','1'); import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; @@ -5813,6 +6549,12 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) oti = GPAC_OTI_VIDEO_AVC; tsimp->avccfg = gf_odf_avc_cfg_new(); break; + case GF_M2TS_VIDEO_HEVC: + mtype = GF_ISOM_MEDIA_VISUAL; + stype = GF_STREAM_VISUAL; + oti = GPAC_OTI_VIDEO_HEVC; + tsimp->hevccfg = gf_odf_hevc_cfg_new(); + break; case GF_M2TS_AUDIO_MPEG1: mtype = GF_ISOM_MEDIA_AUDIO; stype = GF_STREAM_AUDIO; @@ -5929,7 +6671,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) case GF_AVC_NALU_SVC_SUBSEQ_PARAM: is_subseq = 1; case GF_AVC_NALU_SEQ_PARAM: - idx = AVC_ReadSeqInfo(pck->data+5, pck->data_len-5, &tsimp->avc, is_subseq, NULL); + idx = gf_media_avc_read_sps(pck->data+4, pck->data_len-4, &tsimp->avc, is_subseq, NULL); add_sps = 0; if (idx>=0) { @@ -5965,7 +6707,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } return; case GF_AVC_NALU_PIC_PARAM: - idx = AVC_ReadPictParamSet(pck->data+5, pck->data_len-5, &tsimp->avc); + idx = gf_media_avc_read_pps(pck->data+4, pck->data_len-4, &tsimp->avc); if ((idx>=0) && (tsimp->avc.pps[idx].status==1)) { tsimp->avc.pps[idx].status = 2; slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); @@ -5987,9 +6729,96 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) case GF_AVC_NALU_SEI: break; if (tsimp->avc.sps_active_idx != -1) { - idx = AVC_ReformatSEI_NALU(pck->data+4, pck->data_len-4, &tsimp->avc); + idx = gf_media_avc_reformat_sei(pck->data+4, pck->data_len-4, &tsimp->avc); + if (idx>0) pck->data_len = idx+4; + } + break; + } + + if (tsimp->force_next_au_start) { + is_au_start = 1; + tsimp->force_next_au_start = 0; + } + } + + /*avc data for the current sample is stored in annex-B, as we don't know the size of each nal + when called back (depending on PES packetization, the end of the nal could be in following pes)*/ + else if (tsimp->hevccfg && !pck->data[0] && !pck->data[1]) { + s32 idx; + Bool add_sps, is_subseq = 0; + u32 nal_type = (pck->data[4] & 0x7E) >> 1; + + switch (nal_type) { + case GF_HEVC_NALU_SEQ_PARAM: + idx = gf_media_hevc_read_sps(pck->data+4, pck->data_len-4, &tsimp->hevc); + add_sps = 0; + if (idx>=0) { + if (is_subseq) { + if ((tsimp->hevc.sps[idx].state & AVC_SUBSPS_PARSED) && !(tsimp->hevc.sps[idx].state & AVC_SUBSPS_DECLARED)) { + tsimp->hevc.sps[idx].state |= AVC_SUBSPS_DECLARED; + add_sps = 1; + } + } else { + if ((tsimp->hevc.sps[idx].state & AVC_SPS_PARSED) && !(tsimp->hevc.sps[idx].state & AVC_SPS_DECLARED)) { + tsimp->hevc.sps[idx].state |= AVC_SPS_DECLARED; + add_sps = 1; + } + } + if (add_sps) { + /*always store nalu size on 4 bytes*/ + tsimp->hevccfg->nal_unit_size = 4; + tsimp->hevccfg->configurationVersion = 1; + + tsimp->hevccfg->configurationVersion = 1; + tsimp->hevccfg->profile_space = tsimp->hevc.sps[idx].ptl.profile_space; + tsimp->hevccfg->profile_idc = tsimp->hevc.sps[idx].ptl.profile_idc; + tsimp->hevccfg->constraint_indicator_flags = 0; + tsimp->hevccfg->level_idc = tsimp->hevc.sps[idx].ptl.level_idc; + tsimp->hevccfg->profile_compatibility_indications = tsimp->hevc.sps[idx].ptl.profile_compatibility_flag; + tsimp->hevccfg->chromaFormat = tsimp->hevc.sps[idx].chroma_format_idc; + tsimp->hevccfg->luma_bit_depth = tsimp->hevc.sps[idx].bit_depth_luma; + tsimp->hevccfg->chroma_bit_depth = tsimp->hevc.sps[idx].bit_depth_chroma; + + hevc_cfg_add_nalu(tsimp->hevccfg, nal_type, pck->data+4, pck->data_len-4); + + if (pck->stream->vid_w < tsimp->avc.sps[idx].width) + pck->stream->vid_w = tsimp->avc.sps[idx].width; + if (pck->stream->vid_h < tsimp->avc.sps[idx].height) + pck->stream->vid_h = tsimp->avc.sps[idx].height; + } + } + return; + case GF_HEVC_NALU_PIC_PARAM: + idx = gf_media_hevc_read_pps(pck->data+4, pck->data_len-4, &tsimp->hevc); + if ((idx>=0) && (tsimp->hevc.pps[idx].state==1)) { + tsimp->hevc.pps[idx].state = 2; + hevc_cfg_add_nalu(tsimp->hevccfg, nal_type, pck->data+4, pck->data_len-4); + } + return; + case GF_HEVC_NALU_VID_PARAM: + idx = gf_media_hevc_read_vps(pck->data+4, pck->data_len-4, &tsimp->hevc); + if ((idx>=0) && (tsimp->hevc.vps[idx].state==1)) { + tsimp->hevc.vps[idx].state = 2; + tsimp->hevccfg->avgFrameRate = tsimp->hevc.vps[idx].rates[0].avg_pic_rate; + tsimp->hevccfg->constantFrameRate = tsimp->hevc.vps[idx].rates[0].constand_pic_rate_idc; + tsimp->hevccfg->numTemporalLayers = tsimp->hevc.vps[idx].max_sub_layer; + hevc_cfg_add_nalu(tsimp->hevccfg, nal_type, pck->data+4, pck->data_len-4); + } + return; + /*remove*/ + case GF_HEVC_NALU_ACCESS_UNIT: + tsimp->force_next_au_start = 1; + return; + case GF_HEVC_NALU_FILLER_DATA: + case GF_HEVC_NALU_END_OF_SEQ: + case GF_HEVC_NALU_END_OF_STREAM: + return; + case GF_HEVC_NALU_SEI_PREFIX: +/*TODO if (tsimp->avc.sps_active_idx != -1) { + idx = gf_media_avc_reformat_sei(pck->data+4, pck->data_len-4, &tsimp->avc); if (idx>0) pck->data_len = idx+4; } +*/ break; } @@ -5998,6 +6827,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) tsimp->force_next_au_start = 0; } } + if (!is_au_start) { e = gf_isom_append_sample_data(import->dest, tsimp->track, (char*)pck->data, pck->data_len); if (e) { @@ -6028,6 +6858,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) case GF_M2TS_VIDEO_MPEG2: gf_import_message(import, GF_OK, "MPEG-2 Video import (TS PID %d)", pck->stream->pid); break; case GF_M2TS_VIDEO_MPEG4: gf_import_message(import, GF_OK, "MPEG-4 Video import (TS PID %d)", pck->stream->pid); break; case GF_M2TS_VIDEO_H264: gf_import_message(import, GF_OK, "MPEG-4 AVC/H264 Video import (TS PID %d)", pck->stream->pid); break; + case GF_M2TS_VIDEO_HEVC: gf_import_message(import, GF_OK, "MPEG-H HEVC Video import (TS PID %d)", pck->stream->pid); break; case GF_M2TS_AUDIO_MPEG1: gf_import_message(import, GF_OK, "MPEG-1 Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; case GF_M2TS_AUDIO_MPEG2: gf_import_message(import, GF_OK, "MPEG-2 Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; case GF_M2TS_AUDIO_AAC: gf_import_message(import, GF_OK, "MPEG-4 AAC Audio import - SampleRate %d Channels %d Language %s (TS PID %d)", pck->stream->aud_sr, pck->stream->aud_nb_ch, gf_4cc_to_str(pck->stream->lang), pck->stream->pid); break; @@ -7086,7 +7917,11 @@ GF_Err gf_media_import(GF_MediaImporter *importer) if (!strnicmp(ext, ".h264", 5) || !strnicmp(ext, ".264", 4) || !strnicmp(ext, ".x264", 5) || !strnicmp(ext, ".h26L", 5) || !strnicmp(ext, ".26l", 4) || !stricmp(fmt, "AVC") || !stricmp(fmt, "H264") ) - return gf_import_h264(importer); + return gf_import_avc_h264(importer); + /*HEVC video*/ + if (!strnicmp(ext, ".hevc", 5) || !strnicmp(ext, ".hvc", 4) || !strnicmp(ext, ".265", 4) + || !stricmp(fmt, "HEVC") || !stricmp(fmt, "H265") ) + return gf_import_hevc(importer); /*AC3*/ if (!strnicmp(ext, ".ac3", 4) || !stricmp(fmt, "AC3") ) return gf_import_ac3(importer); @@ -7171,6 +8006,8 @@ GF_Err gf_media_change_pl(GF_ISOFile *file, u32 track, u32 profile, u32 level) switch (stype) { case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_AVC3_H264: + case GF_ISOM_SUBTYPE_AVC4_H264: break; default: return GF_OK; diff --git a/src/media_tools/mpd.c b/src/media_tools/mpd.c index 67727ee..57ae337 100644 --- a/src/media_tools/mpd.c +++ b/src/media_tools/mpd.c @@ -311,7 +311,7 @@ static void gf_mpd_parse_segment_base_generic(GF_MPD *mpd, GF_MPD_SegmentBase *s while ( (att = gf_list_enum(root->attributes, &i)) ) { if (!strcmp(att->name, "timescale")) seg->timescale = gf_mpd_parse_int(att->value); else if (!strcmp(att->name, "presentationTimeOffset")) seg->presentation_time_offset = gf_mpd_parse_long_int(att->value); - else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_byte_range(att->value); else if (!strcmp(att->name, "indexRangeExact")) seg->index_range_exact = gf_mpd_parse_bool(att->value); } @@ -784,6 +784,7 @@ void gf_mpd_segment_base_free(void *_item) GF_MPD_SegmentBase *ptr = (GF_MPD_SegmentBase *)_item; if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment); if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index); + if (ptr->index_range) gf_free(ptr->index_range); gf_free(ptr); } @@ -1343,7 +1344,9 @@ GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url, count2 = gf_list_count(prog->bitrates); for (j = 0; jbitrates, j); @@ -1365,9 +1368,11 @@ GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url, strncpy(pe->codecs, pe->codecs+1, len-1); pe->codecs[len-2] = 0; } +#ifndef GPAC_DISABLE_MEDIA_IMPORT if (pe->bandwidth && pe->codecs && pe->width && pe->height) { import_file = 0; } +#endif /*get rid of level 0 aac*/ elt = gf_list_get(pe->element.playlist.elements, 0); diff --git a/src/media_tools/mpegts.c b/src/media_tools/mpegts.c index 2a7b2c2..c401d79 100644 --- a/src/media_tools/mpegts.c +++ b/src/media_tools/mpegts.c @@ -62,6 +62,7 @@ const char *gf_m2ts_get_stream_name(u32 streamType) case GF_M2TS_AUDIO_AAC: return "AAC Audio"; case GF_M2TS_VIDEO_MPEG4: return "MPEG-4 Video"; case GF_M2TS_VIDEO_H264: return "MPEG-4/H264 Video"; + case GF_M2TS_VIDEO_HEVC: return "MPEG-H HEVC Video"; case GF_M2TS_AUDIO_AC3: return "Dolby AC3 Audio"; case GF_M2TS_AUDIO_DTS: return "Dolby DTS Audio"; @@ -109,7 +110,7 @@ static u32 gf_m2ts_reframe_reset(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool sam return 0; } -static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_nalu_video(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len, Bool is_hevc) { Bool au_start_in_pes=0; Bool prev_is_au_delim=0; @@ -186,47 +187,94 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool } start_code_found = short_start_code ? 2 : 1; - nal_type = pck.data[4] & 0x1F; + if (is_hevc) { + nal_type = (pck.data[4] & 0x7E) >> 1; - /*check for SPS and update stream info*/ + /*check for SPS and update stream info*/ #ifndef GPAC_DISABLE_AV_PARSERS - if (!pes->vid_w && (nal_type==GF_AVC_NALU_SEQ_PARAM)) { - AVCState avc; - s32 idx; - memset(&avc, 0, sizeof(AVCState)); - avc.sps_active_idx = -1; - idx = AVC_ReadSeqInfo(data+5, sc_pos-5, &avc, 0, NULL); - - if (idx>=0) { - pes->vid_w = avc.sps[idx].width; - pes->vid_h = avc.sps[idx].height; + if (!pes->vid_w && (nal_type==GF_HEVC_NALU_SEQ_PARAM)) { + HEVCState hevc; + s32 idx; + memset(&hevc, 0, sizeof(HEVCState)); + hevc.sps_active_idx = -1; + idx = gf_media_hevc_read_sps(data+4, sc_pos-4, &hevc); + + if (idx>=0) { + pes->vid_w = hevc.sps[idx].width; + pes->vid_h = hevc.sps[idx].height; + } } - } #endif - /*check AU start type*/ - if (nal_type==GF_AVC_NALU_ACCESS_UNIT) { - if (!prev_is_au_delim) { - if (au_start_in_pes) { - /*FIXME - we should check the AVC framerate to update the timing ...*/ - pck.DTS += 3000; - pck.PTS += 3000; -// GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID%d: Two AVC AUs start in this PES packet - cannot recompute non-first AU timing\n", pes->pid)); + /*check AU start type*/ + if (nal_type==GF_HEVC_NALU_ACCESS_UNIT) { + if (!prev_is_au_delim) { + if (au_start_in_pes) { + /*FIXME - we should check the AVC framerate to update the timing ...*/ + pck.DTS += 3000; + pck.PTS += 3000; + // GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID%d: Two AVC AUs start in this PES packet - cannot recompute non-first AU timing\n", pes->pid)); + } + pck.flags = GF_M2TS_PES_PCK_AU_START; + force_new_au = 0; + au_start_in_pes = 1; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + prev_is_au_delim=1; } - pck.flags = GF_M2TS_PES_PCK_AU_START; - force_new_au = 0; - au_start_in_pes = 1; + } else if ((nal_type==GF_HEVC_NALU_SLICE_IDR_W_DLP) + || (nal_type==GF_HEVC_NALU_SLICE_IDR_N_LP) + ) { + pck.flags = GF_M2TS_PES_PCK_RAP; ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); - prev_is_au_delim=1; + prev_is_au_delim=0; + } + else { + pck.flags = 0; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + prev_is_au_delim=0; + } + } else { + nal_type = pck.data[4] & 0x1F; + + /*check for SPS and update stream info*/ +#ifndef GPAC_DISABLE_AV_PARSERS + if (!pes->vid_w && (nal_type==GF_AVC_NALU_SEQ_PARAM)) { + AVCState avc; + s32 idx; + memset(&avc, 0, sizeof(AVCState)); + avc.sps_active_idx = -1; + idx = gf_media_avc_read_sps(data+4, sc_pos-4, &avc, 0, NULL); + + if (idx>=0) { + pes->vid_w = avc.sps[idx].width; + pes->vid_h = avc.sps[idx].height; + } + } +#endif + /*check AU start type*/ + if (nal_type==GF_AVC_NALU_ACCESS_UNIT) { + if (!prev_is_au_delim) { + if (au_start_in_pes) { + /*FIXME - we should check the AVC framerate to update the timing ...*/ + pck.DTS += 3000; + pck.PTS += 3000; + // GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID%d: Two AVC AUs start in this PES packet - cannot recompute non-first AU timing\n", pes->pid)); + } + pck.flags = GF_M2TS_PES_PCK_AU_START; + force_new_au = 0; + au_start_in_pes = 1; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + prev_is_au_delim=1; + } + } else if (nal_type==GF_AVC_NALU_IDR_SLICE) { + pck.flags = GF_M2TS_PES_PCK_RAP; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + prev_is_au_delim=0; + } + else { + pck.flags = 0; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + prev_is_au_delim=0; } - } else if (nal_type==GF_AVC_NALU_IDR_SLICE) { - pck.flags = GF_M2TS_PES_PCK_RAP; - ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); - prev_is_au_delim=0; - } - else { - pck.flags = 0; - ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); - prev_is_au_delim=0; } data += sc_pos; @@ -243,8 +291,10 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool } /*we did not consume all data*/ if (!start_code_found) { + u32 min_size = is_hevc ? 6 : 5; /*if not enough data to locate start code, store it*/ - if (data_len<5) return data_len; + if (data_len < min_size ) + return data_len; /*otherwise this is the middle of a frame, let's dispatch it*/ } @@ -275,6 +325,16 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool return 0; } +static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) +{ + return gf_m2ts_reframe_nalu_video(ts, pes, same_pts, data, data_len, 0); +} + +static u32 gf_m2ts_reframe_hevc(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) +{ + return gf_m2ts_reframe_nalu_video(ts, pes, same_pts, data, data_len, 1); +} + static u32 gf_m2ts_reframe_mpeg_video(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { u32 sc_pos = 0; @@ -1533,6 +1593,7 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF case GF_M2TS_VIDEO_MPEG4: case GF_M2TS_SYSTEMS_MPEG4_PES: case GF_M2TS_VIDEO_H264: + case GF_M2TS_VIDEO_HEVC: case GF_M2TS_AUDIO_AC3: case GF_M2TS_AUDIO_DTS: case GF_M2TS_SUBTITLE_DVB: @@ -2478,6 +2539,9 @@ GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode) case GF_M2TS_VIDEO_H264: pes->reframe = gf_m2ts_reframe_avc_h264; break; + case GF_M2TS_VIDEO_HEVC: + pes->reframe = gf_m2ts_reframe_hevc; + break; case GF_M2TS_AUDIO_AAC: pes->reframe = gf_m2ts_reframe_aac_adts; break; @@ -2686,8 +2750,9 @@ static u32 TSDemux_DemuxRun(void *_p) gf_dm_sess_process(ts->dnload); gf_sleep(1); } - } else if (ts->file) { + } else if (ts->file || ts->ts_data_chunk) { u32 pos = 0; + GF_BitStream *ts_bs = NULL; if (ts->segment_switch) { ts->segment_switch = 0; @@ -2703,19 +2768,23 @@ static u32 TSDemux_DemuxRun(void *_p) pos = 0; } } - gf_f64_seek(ts->file, pos, SEEK_SET); -restart_file: - gf_f64_seek(ts->file, ts->start_byterange, SEEK_SET); +restart_stream: + + if (ts->file) + ts_bs = gf_bs_from_file(ts->file, GF_BITSTREAM_READ); + else + ts_bs = gf_bs_new(ts->ts_data_chunk, ts->ts_data_chunk_size, GF_BITSTREAM_READ); + + gf_bs_seek(ts_bs, ts->start_byterange); - while (ts->run_state && !feof(ts->file)) { + while (ts->run_state && gf_bs_available(ts_bs)) { /*m2ts chunks by chunks*/ - size = fread(data, 1, 188, ts->file); + size = gf_bs_read_data(ts_bs, data, 188); if (!size && (ts->loop_demux == 1)) { - gf_f64_seek(ts->file, pos, SEEK_SET); + gf_bs_seek(ts_bs, pos); GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSDemux] Loop \n")); - gf_sleep(500); - size = fread(data, 1, 188, ts->file); + size = gf_bs_read_data(ts_bs, data, 188); } if (!size) break; if (size != 188) { @@ -2725,9 +2794,7 @@ restart_file: gf_m2ts_process_data(ts, data, size); ts->nb_pck++; - //fprintf(stderr, "TS packet #%d\r", ts->nb_pck); - - if (ts->end_byterange && (gf_f64_tell(ts->file)>=ts->end_byterange)) + if (ts->end_byterange && (gf_bs_get_position(ts_bs) >= ts->end_byterange)) break; //gf_sleep(0); @@ -2737,13 +2804,13 @@ restart_file: continue; } - if(feof(ts->file) && ts->loop_demux == 1){ - gf_f64_seek(ts->file, pos, SEEK_SET); + if (!gf_bs_available(ts_bs) && ts->loop_demux == 1){ + gf_bs_seek(ts_bs, pos); GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSDemux] Loop \n")); gf_sleep(3000); } } - if (feof(ts->file)) { + if (!gf_bs_available(ts_bs)) { u32 i; for (i=0; iess[i]) { @@ -2757,16 +2824,30 @@ restart_file: } next_segment_setup: + gf_bs_del(ts_bs); + ts_bs = NULL; if (ts->run_state && ts->query_next) { const char *next_url = NULL; ts->query_next(ts->query_udta, 0, &next_url, &ts->start_byterange, &ts->end_byterange); if (next_url) { - fclose(ts->file); - ts->file = gf_f64_open(next_url, "rb"); gf_m2ts_set_segment_switch(ts); - if (ts->file) goto restart_file; - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSDemux] Cannot open next file %s\n", next_url)); + if (!strncmp(next_url, "gmem://", 7)) { + u32 size; + void *mem_address; + if (sscanf(next_url, "gmem://%d@%p", &size, &mem_address) != 2) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSDemux] Cannot open next file %s\n", next_url)); + } else { + ts->ts_data_chunk_size = size; + ts->ts_data_chunk = mem_address; + if (ts->ts_data_chunk_size) goto restart_stream; + } + } else { + if (ts->file) fclose(ts->file); + ts->file = gf_f64_open(next_url, "rb"); + if (ts->file) goto restart_stream; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSDemux] Cannot open next file %s\n", next_url)); + } } } } @@ -3052,16 +3133,28 @@ static GF_Err TSDemux_SetupFile(GF_M2TS_Demuxer *ts, char *url) GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[TSDemux] TS file already being processed: %s\n", url)); return GF_IO_ERR; } - - ts->file = gf_f64_open(url, "rb"); - if (!ts->file) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[TSDemux] Could not open TS file: %s\n", url)); - return GF_IO_ERR; - } strcpy(ts->filename, url); - gf_f64_seek(ts->file, 0, SEEK_END); - ts->file_size = gf_f64_tell(ts->file); + if (!strncmp(url, "gmem://", 7)) { + u32 size; + void *mem_address; + if (sscanf(url, "gmem://%d@%p", &size, &mem_address) != 2) + return GF_IO_ERR; + ts->ts_data_chunk_size = size; + ts->ts_data_chunk = mem_address; + } else { + + ts->file = gf_f64_open(url, "rb"); + if (!ts->file) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[TSDemux] Could not open TS file: %s\n", url)); + return GF_IO_ERR; + } + strcpy(ts->filename, url); + + gf_f64_seek(ts->file, 0, SEEK_END); + ts->file_size = gf_f64_tell(ts->file); + + } /* reinitialization for seek */ ts->end_range = ts->start_range = 0; @@ -3131,6 +3224,7 @@ GF_Err TSDemux_CloseDemux(GF_M2TS_Demuxer *ts) if (ts->file) fclose(ts->file); ts->file = NULL; + ts->ts_data_chunk = NULL; return GF_OK; } @@ -3155,7 +3249,25 @@ Bool gf_m2ts_probe_file(const char *fileName) { char buf[188]; u32 count = 10; - FILE *t = gf_f64_open(fileName, "rb"); + FILE *t; + + if (!strncmp(fileName, "gmem://", 7)) { + u32 size; + u8 *mem_address; + if (sscanf(fileName, "gmem://%d@%p", &size, &mem_address) != 2) { + return GF_URL_ERROR; + } + while (size>188 && count) { + if (mem_address[0] != 0x47) + return 0; + mem_address+=188; + size-=188; + count--; + } + return 1; + } + + t = gf_f64_open(fileName, "rb"); while (t && count) { u32 read = fread(buf, 1, 188, t); if (!read) { @@ -3175,13 +3287,13 @@ Bool gf_m2ts_probe_file(const char *fileName) static void rewrite_pts_dts(unsigned char *ptr, u64 TS) { ptr[0] &= 0xf1; - ptr[0] |= (unsigned char)((TS&0x1c0000000)>>29); - ptr[1] = (unsigned char)((TS&0x03fc00000)>>22); + ptr[0] |= (unsigned char)((TS&0x1c0000000ULL)>>29); + ptr[1] = (unsigned char)((TS&0x03fc00000ULL)>>22); ptr[2] &= 0x1; - ptr[2] |= (unsigned char)((TS&0x0003f8000)>>14); - ptr[3] = (unsigned char)((TS&0x000007f80)>>7); + ptr[2] |= (unsigned char)((TS&0x0003f8000ULL)>>14); + ptr[3] = (unsigned char)((TS&0x000007f80ULL)>>7); ptr[4] &= 0x1; - ptr[4] |= (unsigned char)((TS&0x00000007f)<<1); + ptr[4] |= (unsigned char)((TS&0x00000007fULL)<<1); assert(((u64)(ptr[0]&0xe)<<29) + ((u64)ptr[1]<<22) + ((u64)(ptr[2]&0xfe)<<14) + ((u64)ptr[3]<<7) + ((ptr[4]&0xfe)>>1) == TS); } @@ -3235,13 +3347,13 @@ GF_Err gf_m2ts_restamp(char *buffer, u32 size, s64 ts_shift, u8 *is_pes) } pck[11] = (unsigned char)(0xff&pcr_ext); } + /*add adaptation_field_length field*/ + adaptation_field_length++; } if (!is_pes[pid] || !(pck[1]&0x40)) { done+=188; continue; } - if (adaptation_field_length) - adaptation_field_length++; /*add adaptation_field_length field*/ pesh = &pck[4+adaptation_field_length]; diff --git a/src/media_tools/text_import.c b/src/media_tools/text_import.c index 8b4e181..3feedfb 100644 --- a/src/media_tools/text_import.c +++ b/src/media_tools/text_import.c @@ -1960,7 +1960,10 @@ GF_Err gf_import_timed_text(GF_MediaImporter *import) u32 fmt; e = gf_text_guess_format(import->in_name, &fmt); if (e) return e; - if (!fmt) return GF_NOT_SUPPORTED; + if (!fmt) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[TTXT Import] Input %s does not look like a supported text format - ignoring\n", import->in_name)); + return GF_NOT_SUPPORTED; + } if (import->flags & GF_IMPORT_PROBE_ONLY) { if (fmt==GF_TEXT_IMPORT_SUB) import->flags |= GF_IMPORT_OVERRIDE_FPS; return GF_OK; diff --git a/src/odf/descriptors.c b/src/odf/descriptors.c index ea42ff4..70d668e 100644 --- a/src/odf/descriptors.c +++ b/src/odf/descriptors.c @@ -791,6 +791,154 @@ exit: return e; } + + +GF_EXPORT +GF_HEVCConfig *gf_odf_hevc_cfg_new() +{ + GF_HEVCConfig *cfg; + GF_SAFEALLOC(cfg, GF_HEVCConfig); + if (!cfg) return NULL; + cfg->param_array = gf_list_new(); + cfg->nal_unit_size = 4; + return cfg; +} + +GF_EXPORT +void gf_odf_hevc_cfg_del(GF_HEVCConfig *cfg) +{ + if (!cfg) return; + while (gf_list_count(cfg->param_array)) { + GF_HEVCParamArray *pa = (GF_HEVCParamArray*)gf_list_get(cfg->param_array, 0); + gf_list_rem(cfg->param_array, 0); + + while (gf_list_count(pa->nalus)) { + GF_AVCConfigSlot *n = (GF_AVCConfigSlot*)gf_list_get(pa->nalus, 0); + gf_list_rem(pa->nalus, 0); + if (n->data) gf_free(n->data); + gf_free(n); + } + gf_free(pa); + } + gf_free(cfg); +} + +GF_EXPORT +GF_Err gf_odf_hevc_cfg_write_bs(GF_HEVCConfig *cfg, GF_BitStream *bs) +{ + u32 i, count; + + gf_bs_write_int(bs, cfg->configurationVersion, 8); + gf_bs_write_int(bs, cfg->profile_space, 3); + gf_bs_write_int(bs, cfg->profile_idc, 5); + gf_bs_write_int(bs, cfg->constraint_indicator_flags, 16); + gf_bs_write_int(bs, cfg->level_idc, 8); + gf_bs_write_int(bs, cfg->profile_compatibility_indications, 32); + gf_bs_write_int(bs, 0xFF, 6); + gf_bs_write_int(bs, cfg->chromaFormat, 2); + gf_bs_write_int(bs, 0xFF, 5); + gf_bs_write_int(bs, cfg->luma_bit_depth-8, 3); + gf_bs_write_int(bs, 0xFF, 5); + gf_bs_write_int(bs, cfg->chroma_bit_depth-8, 3); + gf_bs_write_int(bs, cfg->avgFrameRate, 16); + gf_bs_write_int(bs, cfg->constantFrameRate, 2); + gf_bs_write_int(bs, cfg->numTemporalLayers, 3); + gf_bs_write_int(bs, 1, 1); + gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); + + count = gf_list_count(cfg->param_array); + gf_bs_write_int(bs, count, 8); + for (i=0; iparam_array, i); + gf_bs_write_int(bs, ar->array_completeness, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ar->type, 6); + nalucount = gf_list_count(ar->nalus); + gf_bs_write_int(bs, nalucount, 16); + for (j=0; jnalus, j); + gf_bs_write_int(bs, sl->size, 16); + gf_bs_write_data(bs, sl->data, sl->size); + } + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_hevc_cfg_write(GF_HEVCConfig *cfg, char **outData, u32 *outSize) +{ + GF_Err e; + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + *outSize = 0; + *outData = NULL; + e = gf_odf_hevc_cfg_write_bs(cfg, bs); + if (e==GF_OK) + gf_bs_get_content(bs, outData, outSize); + + gf_bs_del(bs); + return e; +} + +GF_EXPORT +GF_HEVCConfig *gf_odf_hevc_cfg_read_bs(GF_BitStream *bs) +{ + u32 i, count; + GF_HEVCConfig *cfg = gf_odf_hevc_cfg_new(); + + cfg->configurationVersion = gf_bs_read_int(bs, 8); + cfg->profile_space = gf_bs_read_int(bs, 3); + cfg->profile_idc = gf_bs_read_int(bs, 5); + cfg->constraint_indicator_flags = gf_bs_read_int(bs, 16); + cfg->level_idc = gf_bs_read_int(bs, 8); + cfg->profile_compatibility_indications = gf_bs_read_int(bs, 32); + gf_bs_read_int(bs, 6); + cfg->chromaFormat = gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 5); + cfg->luma_bit_depth = 8 + gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 5); + cfg->chroma_bit_depth = 8 + gf_bs_read_int(bs, 3); + cfg->avgFrameRate = gf_bs_read_int(bs, 16); + cfg->constantFrameRate = gf_bs_read_int(bs, 2); + cfg->numTemporalLayers = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 1); + cfg->nal_unit_size = 1 + gf_bs_read_int(bs, 2); + + count = gf_bs_read_int(bs, 8); + for (i=0; inalus = gf_list_new(); + gf_list_add(cfg->param_array, ar); + + ar->array_completeness = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); + ar->type = gf_bs_read_int(bs, 6); + nalucount = gf_bs_read_int(bs, 16); + for (j=0; jsize = gf_bs_read_int(bs, 16); + + sl->data = (char *)gf_malloc(sizeof(char) * sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(ar->nalus, sl); + } + } + return cfg; +} + +GF_EXPORT +GF_HEVCConfig *gf_odf_hevc_cfg_read(char *dsi, u32 dsi_size) +{ + GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); + GF_HEVCConfig *cfg = gf_odf_hevc_cfg_read_bs(bs); + gf_bs_del(bs); + return cfg; +} + GF_EXPORT const char *gf_afx_get_type_description(u8 afx_code) { diff --git a/src/scene_manager/loader_bt.c b/src/scene_manager/loader_bt.c index 5ceeb21..40a9038 100644 --- a/src/scene_manager/loader_bt.c +++ b/src/scene_manager/loader_bt.c @@ -34,7 +34,7 @@ #include -#ifndef GPAC_DISABLE_LOADER_BT +#if !defined(GPAC_DISABLE_LOADER_BT) && !defined(GPAC_DISABLE_ZLIB) #include @@ -1094,23 +1094,31 @@ u32 gf_bt_get_next_proto_id(GF_BTParser *parser) u32 gf_bt_get_def_id(GF_BTParser *parser, char *defName) { - GF_Node *n; - u32 ID; + GF_Node *n=NULL; + u32 ID=0; if (sscanf(defName, "N%u", &ID) == 1) { - ID ++; - n = gf_sg_find_node(parser->load->scene_graph, ID); - /*if an existing node use*/ - if (n) { - u32 id; - u32 nID = gf_bt_get_next_node_id(parser); - const char *name = gf_node_get_name_and_id(n, &id); - gf_bt_report(parser, GF_OK, "changing node \"%s\" Binary ID from %d to %d", name, id -1, nID-1); - gf_node_set_id(n, nID, name); + u32 k=1; + while (defName[k]) { + if (strchr("0123456789", defName[k])==0) { + ID = 0; + break; + } + k++; } - if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; - } else { - ID = gf_bt_get_next_node_id(parser); - } + if (ID) { + ID ++; + n = gf_sg_find_node(parser->load->scene_graph, ID); + if (!n) { + if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; + return ID; + } + } + } + + ID = gf_bt_get_next_node_id(parser); + if (n) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[BT Parsing] (line %d) Binary ID %d already assigned to %s - keeping internal ID %d", parser->line, gf_node_get_name(n), ID)); + } return ID; } diff --git a/src/scene_manager/loader_xmt.c b/src/scene_manager/loader_xmt.c index a617c83..a7b85fa 100644 --- a/src/scene_manager/loader_xmt.c +++ b/src/scene_manager/loader_xmt.c @@ -607,19 +607,29 @@ static u32 xmt_get_next_node_id(GF_XMTParser *parser) } static u32 xmt_get_node_id(GF_XMTParser *parser, char *name) { - GF_Node *n; - u32 ID; + GF_Node *n = NULL; + u32 ID = 0; if (sscanf(name, "N%u", &ID) == 1) { - ID ++; - n = gf_sg_find_node(parser->load->scene_graph, ID); - if (n) { - u32 nID = xmt_get_next_node_id(parser); - xmt_report(parser, GF_OK, "WARNING: changing node \"%s\" ID from %d to %d", gf_node_get_name(n), gf_node_get_id(n)-1, nID-1); - gf_node_set_id(n, nID, gf_node_get_name(n)); - } - if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; - } else { - ID = xmt_get_next_node_id(parser); + u32 k=1; + while (name[k]) { + if (strchr("0123456789", name[k])==0) { + ID = 0; + break; + } + k++; + } + if (ID) { + ID ++; + n = gf_sg_find_node(parser->load->scene_graph, ID); + if (!n) { + if (parser->load->ctx && (parser->load->ctx->max_node_idload->ctx->max_node_id=ID; + return ID; + } + } + } + ID = xmt_get_next_node_id(parser); + if (n) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[XMT Parsing] (line %d) Binary ID %s already assigned to %s - keeping internal ID %d\n", gf_xml_sax_get_line(parser->sax_parser), name, gf_node_get_name(n), ID)); } return ID; } diff --git a/src/scene_manager/scene_engine.c b/src/scene_manager/scene_engine.c index e2254fb..9afe84e 100644 --- a/src/scene_manager/scene_engine.c +++ b/src/scene_manager/scene_engine.c @@ -417,10 +417,16 @@ start: assert(fsize < 1<<31); GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[SceneEngine] Sending DIMS data - sizes: raw (%d)", buffer_len)); if (compress_dims) { +#ifndef GPAC_DISABLE_ZLIB dims_header |= GF_DIMS_UNIT_C; e = gf_gz_compress_payload(&buffer, buffer_len, &buffer_len); GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("/ compressed (%d)", buffer_len)); if (e) goto exit; +#else + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: your version of GPAC was compile with no libz support. Abort.")); + e = GF_NOT_SUPPORTED; + goto exit; +#endif } GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n")); diff --git a/src/scene_manager/swf_parse.c b/src/scene_manager/swf_parse.c index 1b5b39b..46307e9 100644 --- a/src/scene_manager/swf_parse.c +++ b/src/scene_manager/swf_parse.c @@ -28,10 +28,10 @@ #include #include -#include +#if !defined(GPAC_DISABLE_SWF_IMPORT) && !defined(GPAC_DISABLE_ZLIB) -#ifndef GPAC_DISABLE_SWF_IMPORT +#include enum { diff --git a/src/scenegraph/dom_smjs.c b/src/scenegraph/dom_smjs.c index 7b1cc04..bf7cb49 100644 --- a/src/scenegraph/dom_smjs.c +++ b/src/scenegraph/dom_smjs.c @@ -472,7 +472,7 @@ static SMJS_FUNC_PROP_GET( dom_nodelist_getProperty) } return JS_TRUE; } -static SMJS_FUNC_PROP_SET( dom_nodelist_setProperty) +static SMJS_FUNC_PROP_SET_NOVP( dom_nodelist_setProperty) /*avoids gcc warning*/ if (!obj) obj=NULL; @@ -1405,7 +1405,7 @@ static SMJS_FUNC_PROP_GET( dom_document_getProperty ) return JS_TRUE; } -static SMJS_FUNC_PROP_SET(dom_document_setProperty) +static SMJS_FUNC_PROP_SET_NOVP(dom_document_setProperty) u32 prop_id; GF_SceneGraph *sg = dom_get_doc(c, obj); @@ -3445,10 +3445,11 @@ static SMJS_FUNC_PROP_GET( storage_getProperty) /*avoids gcc warning*/ if (!id) id=0; if (!GF_JS_InstanceOf(c, obj, &dom_rt->storageClass, NULL) ) return JS_TRUE; + *vp = JSVAL_VOID; return JS_TRUE; } -static SMJS_FUNC_PROP_SET( storage_setProperty) +static SMJS_FUNC_PROP_SET_NOVP( storage_setProperty) /*avoids gcc warning*/ if (!id) id=0; @@ -3526,29 +3527,29 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec nodeProps[] = { - {"nodeName", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"nodeValue", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"nodeType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"parentNode", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"childNodes", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"firstChild", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"lastChild", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"previousSibling", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"nextSibling", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"attributes", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"ownerDocument", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"namespaceURI", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"prefix", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"localName", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"baseURI", 14, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"textContent", 15, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, + SMJS_PROPERTY_SPEC("nodeName", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("nodeValue", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("nodeType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("parentNode", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("childNodes", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("firstChild", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("lastChild", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("previousSibling", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("nextSibling", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("attributes", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("ownerDocument", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("namespaceURI", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("prefix", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("localName", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("baseURI", 14, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("textContent", 15, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), /*elementTraversal interface*/ - {"firstElementChild", 16, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"lastElementChild", 17, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"previousElementSibling", 18, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"nextElementSibling", 19, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("firstElementChild", 16, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("lastElementChild", 17, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("previousElementSibling", 18, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("nextElementSibling", 19, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(c, global, 0, &dom_rt->domNodeClass, 0, 0, nodeProps, nodeFuncs, 0, 0); if (!dom_rt->domNodeClass._proto) { @@ -3590,17 +3591,17 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) }; JSPropertySpec documentProps[] = { - {"doctype", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"implementation", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"documentElement", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"inputEncoding", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"xmlEncoding", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"xmlStandalone", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"xmlVersion", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"strictErrorChecking", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"documentURI", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"domConfig", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0}, + SMJS_PROPERTY_SPEC("doctype", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("implementation", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("documentElement", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("inputEncoding", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("xmlEncoding", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("xmlStandalone", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("xmlVersion", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("strictErrorChecking", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("documentURI", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("domConfig", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0), }; GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domDocumentClass, 0, 0, documentProps, documentFuncs, 0, 0); @@ -3633,9 +3634,9 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) }; JSPropertySpec elementProps[] = { - {"tagName", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"schemaTypeInfo", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0}, + SMJS_PROPERTY_SPEC("tagName", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("schemaTypeInfo", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0), }; GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domElementClass, 0, 0, elementProps, elementFuncs, 0, 0); GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] element class initialized\n")); @@ -3656,12 +3657,12 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec textProps[] = { - {"data", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"length", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("data", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("length", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*text*/ - {"isElementContentWhitespace", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"wholeText", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0}, + SMJS_PROPERTY_SPEC("isElementContentWhitespace", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("wholeText", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0), }; GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domTextClass, 0, 0, textProps, textFuncs, 0, 0); GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] text class initialized\n")); @@ -3680,59 +3681,58 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec eventProps[] = { - {"type", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"target", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"currentTarget", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"eventPhase", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"bubbles", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"cancelable", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"timeStamp", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"namespaceURI", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"defaultPrevented", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("type", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("target", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("currentTarget", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("eventPhase", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("bubbles", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("cancelable", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("timeStamp", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("namespaceURI", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("defaultPrevented", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*UIEvent*/ - {"detail", 20, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("detail", 20, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*text, connectionEvent*/ - {"data", 25, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("data", 25, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*MouseEvent*/ - {"screenX", 30, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"screenY", 31, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"clientX", 32, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"clientY", 33, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"button", 34, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"relatedTarget", 35, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("screenX", 30, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("screenY", 31, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("clientX", 32, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("clientY", 33, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("button", 34, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("relatedTarget", 35, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*wheelEvent*/ - {"wheelDelta", 36, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("wheelDelta", 36, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*keyboard*/ - {"keyIdentifier", 40, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"keyChar", 41, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"charCode", 42, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("keyIdentifier", 40, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("keyChar", 41, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("charCode", 42, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*progress*/ - {"lengthComputable",50, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"typeArg", 51, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"loaded", 52, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"total", 53, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"bufferLevelValid", 54, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"bufferLevel", 55, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"bufferRemainingTime", 56, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"status", 57, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("lengthComputable",50, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("typeArg", 51, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("loaded", 52, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("total", 53, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("bufferLevelValid", 54, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("bufferLevel", 55, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("bufferRemainingTime", 56, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("status", 57, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*used by vrml*/ - {"width", 60, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"height", 61, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"offset_x", 62, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"offset_y", 63, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"vp_width", 64, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"vp_height", 65, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"translation_x", 66, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"translation_y", 67, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"type3d", 68, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"error", 69, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - - - {0, 0, 0, 0, 0}, + SMJS_PROPERTY_SPEC("width", 60, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("height", 61, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("offset_x", 62, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("offset_y", 63, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("vp_width", 64, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("vp_height", 65, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("translation_x", 66, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("translation_y", 67, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("type3d", 68, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("error", 69, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0), }; GF_JS_InitClass(c, global, 0, &dom_rt->domEventClass, 0, 0, eventProps, eventFuncs, 0, 0); GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] Event class initialized\n")); @@ -3754,8 +3754,8 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec nodeListProps[] = { - {"length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("length", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(c, global, 0, &dom_rt->domNodeListClass, 0, 0, nodeListProps, nodeListFuncs, 0, 0); GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] nodeList class initialized\n")); @@ -3763,13 +3763,13 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) { JSPropertySpec xmlHTTPRequestClassProps[] = { - {"onreadystatechange", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"readyState", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"responseText", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"responseXML", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"status", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"statusText", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("onreadystatechange", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("readyState", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("responseText", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("responseXML", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("status", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("statusText", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec xmlHTTPRequestClassFuncs[] = { SMJS_FUNCTION_SPEC("open", xml_http_open, 2), @@ -3787,7 +3787,7 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) { JSPropertySpec storageClassProps[] = { - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec storageClassFuncs[] = { SMJS_FUNCTION_SPEC(0, 0, 0) @@ -3810,14 +3810,14 @@ void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global) if (dcci && dcci->RootNode) { JSPropertySpec DCCIClassProps[] = { - {"value", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"valueType", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"propertyType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"readOnly", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"DCCIMetadataInterfaceType", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"DCCIMetadataInterface", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"version", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("value", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("valueType", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("propertyType", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("readOnly", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("DCCIMetadataInterfaceType", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("DCCIMetadataInterface", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("version", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec DCCIClassFuncs[] = { SMJS_FUNCTION_SPEC("hasProperty", dcci_has_property, 3), diff --git a/src/scenegraph/svg_smjs.c b/src/scenegraph/svg_smjs.c index 1f3d7c7..ce87087 100644 --- a/src/scenegraph/svg_smjs.c +++ b/src/scenegraph/svg_smjs.c @@ -1523,8 +1523,8 @@ static JSBool svg_connection_close(JSContext *c, JSObject *obj, uintN argc, jsva } static JSPropertySpec connectionProps[] = { - {"connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; static JSFunctionSpec connectionFuncs[] = { /*eventTarget interface*/ @@ -2149,9 +2149,9 @@ static void svg_init_js_api(GF_SceneGraph *scene) SMJS_SET_PRIVATE(scene->svg_js->js_ctx, scene->svg_js->global, scene); { JSPropertySpec globalClassProps[] = { - {"connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"parent", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("connected", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("parent", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec globalClassFuncs[] = { SMJS_FUNCTION_SPEC("createConnection", svg_connection_create, 0), @@ -2180,9 +2180,8 @@ static void svg_init_js_api(GF_SceneGraph *scene) JSPropertySpec svgDocumentProps[] = { /*in our implementation, defaultView is just an alias to the global Window object*/ - {"defaultView", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("defaultView", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSObject *doc_proto = dom_js_get_document_proto(scene->svg_js->js_ctx); GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, doc_proto, &svg_rt->svgDocument, 0, 0, svgDocumentProps, 0, 0, 0); @@ -2248,21 +2247,21 @@ static void svg_init_js_api(GF_SceneGraph *scene) JSPropertySpec svgElementProps[] = { /*svgElement interface*/ - {"id", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, + SMJS_PROPERTY_SPEC("id", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), /*svgSVGElement interface*/ - {"currentScale", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"currentRotate", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"currentTranslate", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"viewport", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"currentTime", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, + SMJS_PROPERTY_SPEC("currentScale", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("currentRotate", 6, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("currentTranslate", 7, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("viewport", 8, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("currentTime", 9, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), /*timeControl interface*/ - {"isPaused", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("isPaused", 10, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*old SVG1.1 stuff*/ - {"ownerSVGElement", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, + SMJS_PROPERTY_SPEC("ownerSVGElement", 11, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), /*SVGElementInstance*/ - {"correspondingElement", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {"correspondingUseElement", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("correspondingElement", 12, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC("correspondingUseElement", 13, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSObject *elt_proto = dom_js_get_element_proto(scene->svg_js->js_ctx); GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, elt_proto, &svg_rt->svgElement, 0, 0, svgElementProps, svgElementFuncs, 0, 0); @@ -2272,30 +2271,30 @@ static void svg_init_js_api(GF_SceneGraph *scene) /*RGBColor class*/ { JSPropertySpec rgbClassProps[] = { - {"red", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"green", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"blue", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("red", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("green", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("blue", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->rgbClass, 0, 0, rgbClassProps, 0, 0, 0); } /*SVGRect class*/ { JSPropertySpec rectClassProps[] = { - {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"width", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"height", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("width", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("height", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->rectClass, 0, 0, rectClassProps, 0, 0, 0); } /*SVGPoint class*/ { JSPropertySpec pointClassProps[] = { - {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->pointClass, 0, 0, pointClassProps, 0, 0, 0); } @@ -2311,13 +2310,13 @@ static void svg_init_js_api(GF_SceneGraph *scene) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec matrixClassProps[] = { - {"a", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"b", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"c", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"d", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"e", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"f", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("a", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("b", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("c", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("d", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("e", 4, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("f", 5, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->matrixClass, 0, 0, matrixClassProps, matrixClassFuncs, 0, 0); } @@ -2334,8 +2333,8 @@ static void svg_init_js_api(GF_SceneGraph *scene) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec pathClassProps[] = { - {"numberOfSegments", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("numberOfSegments", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(scene->svg_js->js_ctx, scene->svg_js->global, 0, &svg_rt->pathClass, 0, 0, pathClassProps, pathClassFuncs, 0, 0); JS_DefineProperty(scene->svg_js->js_ctx, svg_rt->pathClass._proto, "MOVE_TO", INT_TO_JSVAL(77), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); diff --git a/src/scenegraph/vrml_smjs.c b/src/scenegraph/vrml_smjs.c index 45a6262..ffde778 100644 --- a/src/scenegraph/vrml_smjs.c +++ b/src/scenegraph/vrml_smjs.c @@ -291,6 +291,7 @@ static void gf_sg_load_script_modules(GF_SceneGraph *sg) if (!ext) continue; gf_list_add(js_rt->extensions, ext); } + GF_LOG(GF_LOG_INFO, GF_LOG_SCRIPT, ("[ECMAScript] found %d JS extensions for %d modules\n", gf_list_count(js_rt->extensions), count)); } static void gf_sg_unload_script_modules() @@ -3119,17 +3120,17 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) #else /*only used to debug JS_SETUP_CLASS at each of the numerous changes of JSAPI ............ */ memset(&js_rt->SFNodeClass, 0, sizeof(js_rt->SFNodeClass)); - js_rt->SFNodeClass.name = "SFNode"; - js_rt->SFNodeClass.flags = JSCLASS_HAS_PRIVATE; - js_rt->SFNodeClass.addProperty = JS_PropertyStub; - js_rt->SFNodeClass.delProperty = JS_PropertyStub; - js_rt->SFNodeClass.getProperty = node_getProperty; - js_rt->SFNodeClass.setProperty = node_setProperty; - js_rt->SFNodeClass.enumerate = JS_EnumerateStub; - js_rt->SFNodeClass.resolve = JS_ResolveStub; - js_rt->SFNodeClass.convert = JS_ConvertStub; - js_rt->SFNodeClass.finalize = node_finalize; - js_rt->SFNodeClass.hasInstance = gf_sg_js_has_instance; + js_rt->SFNodeClass._class.name = "SFNode"; + js_rt->SFNodeClass._class.flags = JSCLASS_HAS_PRIVATE; + js_rt->SFNodeClass._class.addProperty = JS_PropertyStub; + js_rt->SFNodeClass._class.delProperty = JS_PropertyStub; + js_rt->SFNodeClass._class.getProperty = node_getProperty; + js_rt->SFNodeClass._class.setProperty = node_setProperty; + js_rt->SFNodeClass._class.enumerate = JS_EnumerateStub; + js_rt->SFNodeClass._class.resolve = JS_ResolveStub; + js_rt->SFNodeClass._class.convert = JS_ConvertStub; + js_rt->SFNodeClass._class.finalize = node_finalize; + js_rt->SFNodeClass._class.hasInstance = gf_sg_js_has_instance; #endif JS_SETUP_CLASS(js_rt->SFVec2fClass , "SFVec2f", JSCLASS_HAS_PRIVATE, @@ -3244,16 +3245,16 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) SMJS_FUNCTION_SPEC(0, 0, 0) }; JSPropertySpec SFNodeProps[] = { - {"__dummy", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("__dummy", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFNodeClass, SFNodeConstructor, 1, SFNodeProps, SFNodeMethods, 0, 0); } { JSPropertySpec SFVec2fProps[] = { - {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec SFVec2fMethods[] = { SMJS_FUNCTION_SPEC("add", vec2f_add, 1), @@ -3271,10 +3272,10 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) } { JSPropertySpec SFVec3fProps[] = { - {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"z", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("z", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec SFVec3fMethods[] = { SMJS_FUNCTION_SPEC("add", vec3f_add, 1), @@ -3293,11 +3294,11 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) } { JSPropertySpec SFRotationProps[] = { - {"xAxis", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"yAxis", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"zAxis", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"angle", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("xAxis", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("yAxis", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("zAxis", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("angle", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec SFRotationMethods[] = { SMJS_FUNCTION_SPEC("getAxis", rot_getAxis, 1), @@ -3313,10 +3314,10 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) } { JSPropertySpec SFColorProps[] = { - {"r", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"g", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"b", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("r", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("g", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("b", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; JSFunctionSpec SFColorMethods[] = { SMJS_FUNCTION_SPEC("setHSV", color_setHSV, 3), @@ -3328,20 +3329,20 @@ void gf_sg_script_init_sm_api(GF_ScriptPriv *sc, GF_Node *script) } { JSPropertySpec SFImageProps[] = { - {"x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"comp", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {"array", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0}, - {0, 0, 0, 0, 0} + SMJS_PROPERTY_SPEC("x", 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("y", 1, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("comp", 2, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC("array", 3, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0), + SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) }; GF_JS_InitClass(sc->js_ctx, sc->js_obj, 0, &js_rt->SFImageClass, SFImageConstructor, 0, SFImageProps, 0, 0, 0); } { JSPropertySpec MFArrayProp[] = { - { "length", 0, JSPROP_PERMANENT | JSPROP_SHARED, array_getLength, array_setLength }, - { "assign", 1, JSPROP_PERMANENT | JSPROP_SHARED, array_getElement, array_setElement}, - { 0, 0, 0, 0, 0 } + SMJS_PROPERTY_SPEC( "length", 0, JSPROP_PERMANENT | JSPROP_SHARED, array_getLength, array_setLength ), + SMJS_PROPERTY_SPEC( "assign", 1, JSPROP_PERMANENT | JSPROP_SHARED, array_getElement, array_setElement), + SMJS_PROPERTY_SPEC( 0, 0, 0, 0, 0 ) }; JSFunctionSpec MFArrayMethods[] = { SMJS_FUNCTION_SPEC("toString", field_toString, 0), diff --git a/src/terminal/decoder.c b/src/terminal/decoder.c index f18a433..8c8f895 100644 --- a/src/terminal/decoder.c +++ b/src/terminal/decoder.c @@ -669,8 +669,12 @@ static GF_Err MediaCodec_Process(GF_Codec *codec, u32 TimeAvailable) if ( LockCompositionUnit(codec, codec->last_unit_cts+1, &CU, &unit_size) == GF_OUT_OF_MEM) return GF_OK; assert( CU ); + unit_size = 0; e = mdec->ProcessData(mdec, NULL, 0, 0, CU->data, &unit_size, 0, 0); - if (e==GF_OK) e = UnlockCompositionUnit(codec, CU, unit_size); + if (e==GF_OK) { + e = UnlockCompositionUnit(codec, CU, unit_size); + if (unit_size) return GF_OK; + } } gf_term_stop_codec(codec, 0); if (codec->CB) gf_cm_set_eos(codec->CB); @@ -939,7 +943,7 @@ drop: } Decoder_GetNextAU(codec, &ch, &AU); if (!ch || !AU) return GF_OK; - } + } return GF_OK; } diff --git a/src/terminal/media_manager.c b/src/terminal/media_manager.c index 21d782d..a09dd2e 100644 --- a/src/terminal/media_manager.c +++ b/src/terminal/media_manager.c @@ -140,15 +140,23 @@ void gf_term_add_codec(GF_Terminal *term, GF_Codec *codec) cd->dec = codec; if (!cd->dec->Priority) cd->dec->Priority = 1; - - cap.CapCode = GF_CODEC_WANTS_THREAD; - cap.cap.valueInt = 0; - gf_codec_get_capability(codec, &cap); - threaded = cap.cap.valueInt; + + /*we force audio codecs to be threaded in free mode, so that we avoid waiting in the audio renderer if another decoder is locking the main mutex + this can happen when the audio decoder is running late*/ + if (codec->type==GF_STREAM_AUDIO) { + threaded = 1; + } else { + cap.CapCode = GF_CODEC_WANTS_THREAD; + cap.cap.valueInt = 0; + gf_codec_get_capability(codec, &cap); + threaded = cap.cap.valueInt; + } + if (threaded) cd->flags |= GF_MM_CE_REQ_THREAD; + if (term->flags & GF_TERM_MULTI_THREAD) { - if ((codec->type==0x04) || (codec->type==0x05)) threaded = 1; + if ((codec->type==GF_STREAM_AUDIO) || (codec->type==GF_STREAM_VISUAL)) threaded = 1; } else if (term->flags & GF_TERM_SINGLE_THREAD) { threaded = 0; } diff --git a/src/terminal/media_memory.c b/src/terminal/media_memory.c index 3903b15..9101848 100644 --- a/src/terminal/media_memory.c +++ b/src/terminal/media_memory.c @@ -414,6 +414,7 @@ void gf_cm_resize(GF_CompositionMemory *cb, u32 newCapacity) if (!cb->no_allocation) { my_large_gf_free(cu->data); cu->data = (char*) my_large_alloc(newCapacity); + cu->dataLength = 0; } else { cu->data = NULL; if (cu->dataLength && cb->odm->raw_frame_sema) { @@ -429,9 +430,12 @@ void gf_cm_resize(GF_CompositionMemory *cb, u32 newCapacity) } else { cu->data = NULL; } + cu->dataLength = 0; cu = cu->next; - } - + } + + cb->UnitCount = 0; + cb->output = cb->input; gf_odm_lock(cb->odm, 0); } diff --git a/src/terminal/network_service.c b/src/terminal/network_service.c index 3b56074..1ba90cb 100644 --- a/src/terminal/network_service.c +++ b/src/terminal/network_service.c @@ -82,7 +82,7 @@ static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHA gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_SETUP_DONE); if (err) { char msg[5000]; - snprintf(msg, sizeof(msg), "Cannot open %s", service->url); + snprintf(msg, sizeof(msg)-1, "Cannot open %s", service->url); gf_term_message(term, service->url, msg, err); gf_term_service_media_event(service->owner, GF_EVENT_ERROR); diff --git a/src/terminal/object_manager.c b/src/terminal/object_manager.c index da429aa..49eebf4 100644 --- a/src/terminal/object_manager.c +++ b/src/terminal/object_manager.c @@ -314,9 +314,23 @@ void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *service_sub_url } desc = odm->net_service->ifce->GetServiceDescriptor(odm->net_service->ifce, od_type, sub_url); - if (odm->OD) return; + + /*entry point is already setup (bad design in GPAC, happens with BIFS TS and DASH*/ + if (odm->OD) { + if (!desc) return; + if (gf_list_count(odm->OD->ESDescriptors)) { + gf_odf_desc_del(desc); + return; + } + gf_odf_desc_del((GF_Descriptor *) odm->OD); + odm->OD=NULL; + odm->subscene->is_dynamic_scene = 0; + } if (!desc) { + if (odm->OD && !gf_list_count(odm->OD->ESDescriptors)) + return; + /*if desc is NULL for a media, the media will be declared later by the service (gf_term_media_add)*/ if (od_type != GF_MEDIA_OBJECT_SCENE) { return; diff --git a/src/terminal/scene.c b/src/terminal/scene.c index 64cfe1f..3540971 100644 --- a/src/terminal/scene.c +++ b/src/terminal/scene.c @@ -947,7 +947,6 @@ void gf_scene_register_extra_graph(GF_Scene *scene, GF_SceneGraph *extra_scene, } - static void gf_scene_get_video_size(GF_MediaObject *mo, u32 *w, u32 *h) { u32 pixel_ar; @@ -994,7 +993,7 @@ static void IS_UpdateVideoPos(GF_Scene *scene) if (scene->root_od->term->root_scene == scene) { //if (scene->graph_attached) gf_sc_set_scene(scene->root_od->term->compositor, NULL); - gf_sc_set_scene(scene->root_od->term->compositor, scene->graph); + //gf_sc_set_scene(scene->root_od->term->compositor, scene->graph); } } @@ -1372,7 +1371,20 @@ void gf_scene_force_size(GF_Scene *scene, u32 width, u32 height) gf_sg_set_scene_size_info(scene->graph, width, height, gf_sg_use_pixel_metrics(scene->graph)); if (scene->root_od->term->root_scene == scene) { - gf_sc_set_scene(scene->root_od->term->compositor, scene->graph); + GF_NetworkCommand com; + + gf_sc_set_scene_size(scene->root_od->term->compositor, width, height, 1); + + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.base.command_type = GF_NET_SERVICE_HAS_FORCED_VIDEO_SIZE; + gf_term_service_command(scene->root_od->net_service, &com); + if (com.par.width && com.par.height) { + gf_sc_set_size(scene->root_od->term->compositor, com.par.width, com.par.height); + } else { + /*need output resize*/ + gf_sc_set_scene(scene->root_od->term->compositor, scene->graph); + gf_sc_set_size(scene->root_od->term->compositor, width, height); + } } else if (scene->root_od->parentscene && scene->root_od->parentscene->is_dynamic_scene) { gf_sg_set_scene_size_info(scene->root_od->parentscene->graph, width, height, gf_sg_use_pixel_metrics(scene->root_od->parentscene->graph)); diff --git a/src/utils/base_encoding.c b/src/utils/base_encoding.c index cb92a1b..97afacb 100644 --- a/src/utils/base_encoding.c +++ b/src/utils/base_encoding.c @@ -174,6 +174,9 @@ u32 gf_base16_decode(char *in, u32 inSize, char *out, u32 outSize) return j; } + +#ifndef GPAC_DISABLE_ZLIB + #include #define ZLIB_COMPRESS_SAFE 4 @@ -266,3 +269,5 @@ GF_Err gf_gz_decompress_payload(char *data, u32 data_len, char **uncompressed_da } return e; } + +#endif /*GPAC_DISABLE_ZLIB*/ diff --git a/src/utils/cache.c b/src/utils/cache.c index 7b08685..1e84ea4 100644 --- a/src/utils/cache.c +++ b/src/utils/cache.c @@ -156,6 +156,17 @@ struct __DownloadedCacheEntryStruct /*start and end range of the cache*/ u64 range_start, range_end; + Bool continue_file; + Bool file_exists; + + u32 previousRangeContentLength; + + /** + * Set to 1 if file is not stored on disk + */ + Bool memory_stored; + u32 mem_allocated; + u8 *mem_storage; }; Bool delete_cache_files(void *cbck, char *item_name, char *item_path) { @@ -362,7 +373,7 @@ static const char * default_cache_file_suffix = ".dat"; static const char * cache_file_info_suffix = ".txt"; -DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char * cache_directory, const char * url , u64 start_range, u64 end_range) +DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char * cache_directory, const char * url , u64 start_range, u64 end_range, Bool mem_storage) { char tmp[_CACHE_TMP_SIZE]; u8 hash[_CACHE_HASH_SIZE]; @@ -408,14 +419,12 @@ DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("gf_cache_create_entry:%d : OUT of memory !\n", __LINE__)); return NULL; } - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, - ("[CACHE] gf_cache_create_entry:%d, entry=%p\n", __LINE__, entry)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[CACHE] gf_cache_create_entry:%d, entry=%p\n", __LINE__, entry)); entry->url = gf_strdup ( url ); entry->hash = gf_strdup ( tmp ); - /* Sizeof cache directory + hash + possible extension */ - entry->cache_filename = gf_malloc ( strlen ( cache_directory ) + strlen(cache_file_prefix) + strlen(tmp) + _CACHE_MAX_EXTENSION_SIZE + 1); + entry->memory_stored = mem_storage; entry->cacheSize = 0; entry->contentLength = 0; @@ -429,17 +438,26 @@ DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char entry->range_start = start_range; entry->range_end = end_range; +#ifdef ENABLE_WRITE_MX { char name[1024]; - snprintf(name, 1024, "CachedEntryWriteMx=%p, url=%s", (void*) entry, url); -#ifdef ENABLE_WRITE_MX + snprintf(name, sizeof(name)-1, "CachedEntryWriteMx=%p, url=%s", (void*) entry, url); entry->write_mutex = gf_mx_new(name); assert( entry->write_mutex); -#endif } +#endif + entry->deletableFilesOnDelete = 0; entry->write_session = NULL; entry->sessions = gf_list_new(); + + if (entry->memory_stored) { + entry->cache_filename = gf_malloc ( strlen ("gmem://") + 8 + strlen("@") + 16 + 1); + } else { + /* Sizeof cache directory + hash + possible extension */ + entry->cache_filename = gf_malloc ( strlen ( cache_directory ) + strlen(cache_file_prefix) + strlen(tmp) + _CACHE_MAX_EXTENSION_SIZE + 1); + } + if ( !entry->hash || !entry->url || !entry->cache_filename || !entry->sessions) { GF_Err err; @@ -449,6 +467,13 @@ DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char assert ( err == GF_OK ); return NULL; } + + if (entry->memory_stored) { + sprintf(entry->cache_filename, "gmem://%d@%p", entry->contentLength, entry->mem_storage); + return entry; + } + + tmp[0] = '\0'; strcpy ( entry->cache_filename, cache_directory ); strcat( entry->cache_filename, cache_file_prefix ); @@ -497,7 +522,13 @@ DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char entry->flags |= CORRUPTED; keyValue = gf_cfg_get_key(entry->properties, CACHE_SECTION_NAME, CACHE_SECTION_NAME_RANGE); - if (keyValue) sscanf(keyValue, LLD"-"LLD, &entry->range_start, &entry->range_end); + if (keyValue) { + u64 s, e; + sscanf(keyValue, LLD"-"LLD, &s, &e); + /*mark as corrupted if not same range (we don't support this for the time being ...*/ + if ((s!=entry->range_start) || (e!=entry->range_end)) + entry->flags |= CORRUPTED; + } } gf_cache_check_if_cache_file_is_corrupted(entry); @@ -506,7 +537,11 @@ DownloadedCacheEntry gf_cache_create_entry ( GF_DownloadManager * dm, const char GF_Err gf_cache_set_content_length( const DownloadedCacheEntry entry, u32 length ) { CHECK_ENTRY; - entry->contentLength = length; + if (entry->continue_file) { + entry->contentLength = entry->previousRangeContentLength + length; + } else { + entry->contentLength = length; + } return GF_OK; } @@ -557,10 +592,31 @@ GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_Dow gf_mx_p(entry->write_mutex); #endif entry->write_session = sess; - assert( ! entry->writeFilePtr); - GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, - ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url)); - entry->writeFilePtr = gf_f64_open(entry->cache_filename, "wb"); + if (!entry->continue_file) { + assert( ! entry->writeFilePtr); + + entry->written_in_cache = 0; + } + entry->flags &= ~CORRUPTED; + + + if (entry->memory_stored) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url)); + if (entry->mem_allocated <= entry->contentLength) { + if (entry->contentLength) entry->mem_allocated = entry->contentLength; + else if (!entry->mem_allocated) entry->mem_allocated = 1024; + entry->mem_storage = gf_realloc(entry->mem_storage, sizeof(char)* (entry->mem_allocated + 2) ); + } + if (!entry->mem_allocated) { + GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[CACHE] Failed to create memory storage for file %s\n", entry->url)); + return GF_OUT_OF_MEM; + } + sprintf(entry->cache_filename, "gmem://%d@%p", entry->contentLength, entry->mem_storage); + return GF_OK; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url)); + entry->writeFilePtr = gf_f64_open(entry->cache_filename, entry->continue_file ? "a+b" : "wb"); if (!entry->writeFilePtr) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[CACHE] Error while opening cache file %s for writting.\n", entry->cache_filename)); @@ -570,7 +626,9 @@ GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_Dow #endif return GF_IO_ERR; } - entry->written_in_cache = 0; + entry->file_exists = 1; + if (entry->continue_file ) + gf_f64_seek(entry->writeFilePtr, 0, SEEK_END); return GF_OK; } @@ -578,10 +636,24 @@ GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_Downl u32 readen; GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[CACHE] gf_cache_write_to_cache:%d\n", __LINE__)); CHECK_ENTRY; - if (!data || !entry->writeFilePtr || sess != entry->write_session) { - GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("Incorrect parameter : data=%p, entry->writeFilePtr=%p at "__FILE__, data, entry->writeFilePtr)); + + if (!data || (!entry->writeFilePtr && !entry->mem_storage) || sess != entry->write_session) { + GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("Incorrect parameter : data=%p, writeFilePtr=%p mem_storage=%p at "__FILE__, data, entry->writeFilePtr, entry->mem_storage)); return GF_BAD_PARAM; } + + if (entry->memory_stored) { + if (entry->written_in_cache + size > entry->mem_allocated) { + entry->mem_storage = gf_realloc(entry->mem_storage, (entry->mem_allocated+size+2)); + entry->mem_allocated += size; + sprintf(entry->cache_filename, "gmem://%d@%p", entry->contentLength, entry->mem_storage); + } + memcpy(entry->mem_storage + entry->written_in_cache, data, size); + entry->written_in_cache += size; + memset(entry->mem_storage + entry->written_in_cache, 0, 2); + return GF_OK; + } + readen = gf_fwrite(data, sizeof(char), size, entry->writeFilePtr); if (readen > 0) entry->written_in_cache+= readen; @@ -671,7 +743,7 @@ GF_Err gf_cache_delete_entry ( const DownloadedCacheEntry entry ) gf_mx_del(entry->write_mutex); } #endif - if (entry->deletableFilesOnDelete) { + if (entry->file_exists && entry->deletableFilesOnDelete) { GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] url %s cleanup, deleting %s...\n", entry->url, entry->cache_filename)); if (GF_OK != gf_delete_file(entry->cache_filename)) GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[CACHE] gf_cache_delete_entry:%d, failed to delete file %s\n", __LINE__, entry->cache_filename)); @@ -738,6 +810,9 @@ GF_Err gf_cache_delete_entry ( const DownloadedCacheEntry entry ) gf_list_del(entry->sessions); entry->sessions = NULL; } + + if (entry->mem_storage) + gf_free(entry->mem_storage); gf_free (entry); return GF_OK; } @@ -757,17 +832,15 @@ Bool gf_cache_check_if_cache_file_is_corrupted(const DownloadedCacheEntry entry) entry->contentLength = strtoul( keyValue, &endPtr, 10); if (*endPtr!='\0' || entry->contentLength != entry->cacheSize) { entry->flags |= CORRUPTED; - GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] gf_cache_create_entry:%d, cached file and cache info size mismatch.\n", __LINE__)); + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] gf_cache_create_entry:%d, Cache corrupted: file and cache info size mismatch.\n", __LINE__)); } - } else + } else { entry->flags |= CORRUPTED; - + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] gf_cache_create_entry:%d, CACHE is corrupted !\n", __LINE__)); + } } else { entry->flags |= CORRUPTED; } - if (entry->flags & CORRUPTED) - GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] gf_cache_create_entry:%d, CACHE is corrupted !\n", __LINE__)); - return entry->flags & CORRUPTED; } @@ -832,3 +905,20 @@ FILE *gf_cache_get_file_pointer(const DownloadedCacheEntry entry) if (entry) return entry->writeFilePtr; return NULL; } + + +void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end) +{ + entry->previousRangeContentLength = entry->contentLength; + entry->range_end = range_end; + entry->continue_file = 1; +} + +Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry) +{ + if (!entry) return 0; + if (entry->writeFilePtr) return 1; + if (entry->mem_storage && entry->written_in_cache && entry->contentLength && (entry->written_in_cachecontentLength) ) + return 1; + return 0; +} diff --git a/src/utils/configfile.c b/src/utils/configfile.c index c502d4f..ebcbe30 100644 --- a/src/utils/configfile.c +++ b/src/utils/configfile.c @@ -207,7 +207,8 @@ GF_Config *gf_cfg_new(const char *filePath, const char* file_name) } if (gf_cfg_parse_config_file(tmp, filePath, file_name)){ - gf_free( tmp ); + gf_cfg_clear(tmp); + gf_free(tmp); tmp = NULL; } return tmp; diff --git a/src/utils/downloader.c b/src/utils/downloader.c index bf8f1ff..ccdde23 100644 --- a/src/utils/downloader.c +++ b/src/utils/downloader.c @@ -108,6 +108,7 @@ struct __gf_download_session gf_user_credentials_struct * creds; char cookie[GF_MAX_PATH]; DownloadedCacheEntry cache_entry; + Bool reused_cache_entry; GF_Socket *sock; u32 num_retry, status; @@ -117,6 +118,9 @@ struct __gf_download_session u32 total_size, bytes_done, start_time, icy_metaint, icy_count, icy_bytes; u32 bytes_per_sec; + Bool is_range_continuation; + /*0: no cache reconfig before next GET request: 1: try to rematch the cache entry: 2: force to create a new cache entry (for byte-range cases*/ + u32 needs_cache_reconfig; /* Range information if needed for the download (cf flag) */ Bool needs_range; u64 range_start, range_end; @@ -145,6 +149,7 @@ struct __gf_download_session /*callback for data reception - may not be NULL*/ gf_dm_user_io user_proc; void *usr_cbk; + Bool reassigned; /*private extension*/ void *ext; @@ -241,6 +246,12 @@ GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_Dow */ FILE *gf_cache_get_file_pointer(const DownloadedCacheEntry entry) ; +/*modify end range when chaining byte-range requests*/ +void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end); + +/*returns 1 if cache is currently open for write*/ +Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry); + /** * Find a User's credentials for a given site */ @@ -455,8 +466,13 @@ DownloadedCacheEntry gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess) { url = gf_cache_get_url(e); assert( url ); if (strcmp(url, sess->orig_url)) continue; - if (sess->range_start != gf_cache_get_start_range(e)) continue; - if (sess->range_end != gf_cache_get_end_range(e)) continue; + if (sess->needs_cache_reconfig==2) + continue; + + if (! sess->is_range_continuation) { + if (sess->range_start != gf_cache_get_start_range(e)) continue; + if (sess->range_end != gf_cache_get_end_range(e)) continue; + } /*OK that's ours*/ gf_mx_v( sess->dm->cache_mx ); return e; @@ -471,7 +487,7 @@ DownloadedCacheEntry gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess) { * \param url The full URL * \return The DownloadedCacheEntry */ -DownloadedCacheEntry gf_cache_create_entry( GF_DownloadManager * dm, const char * cache_directory, const char * url, u64 start_range, u64 end_range); +DownloadedCacheEntry gf_cache_create_entry( GF_DownloadManager * dm, const char * cache_directory, const char * url, u64 start_range, u64 end_range, Bool mem_storage); /*! * Removes a session for a DownloadedCacheEntry @@ -528,13 +544,15 @@ void gf_dm_configure_cache(GF_DownloadSession *sess) gf_dm_remove_cache_entry_from_session(sess); entry = gf_dm_find_cached_entry_by_url(sess); if (!entry) { - entry = gf_cache_create_entry(sess->dm, sess->dm->cache_directory, sess->orig_url, sess->range_start, sess->range_end); + entry = gf_cache_create_entry(sess->dm, sess->dm->cache_directory, sess->orig_url, sess->range_start, sess->range_end, (sess->flags&GF_NETIO_SESSION_MEMORY_CACHE) ? 1 : 0); gf_mx_p( sess->dm->cache_mx ); gf_list_add(sess->dm->cache_entries, entry); gf_mx_v( sess->dm->cache_mx ); + sess->is_range_continuation = 0; } assert( entry ); sess->cache_entry = entry; + sess->reused_cache_entry = gf_cache_is_in_progress(entry); gf_cache_add_session_to_cache_entry(sess->cache_entry, sess); GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[CACHE] Cache setup to %p %s\n", sess, gf_cache_get_cache_filename(sess->cache_entry))); } @@ -598,7 +616,7 @@ static void gf_dm_disconnect(GF_DownloadSession *sess, Bool force_close) { assert( sess ); if (sess->status >= GF_NETIO_DISCONNECTED) - return; + return; GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess )); if (sess->mx) gf_mx_p(sess->mx); @@ -858,8 +876,8 @@ GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * bas /* builds orig_url */ /* We dont't want orig_url to contain user/passwords for security reasons or mismatch in cache hit */ { - char port[7]; - snprintf(port, 7, ":%d", info->port); + char port[8]; + snprintf(port, sizeof(port)-1, ":%d", info->port); info->canonicalRepresentation = gf_malloc(strlen(info->protocol)+strlen(info->server_name)+1+strlen(port)+strlen(info->remotePath)); strcpy(info->canonicalRepresentation, info->protocol); strcat(info->canonicalRepresentation, info->server_name); @@ -878,13 +896,17 @@ GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url) Bool socket_changed = 0; GF_Err e; GF_URL_Info info; + + if (!url) return GF_BAD_PARAM; + gf_dm_url_info_init(&info); - e = gf_dm_get_url_info(url, &info, sess->remote_path); - if (e) return e; if (!sess->sock) socket_changed = 1; else if (sess->status>GF_NETIO_DISCONNECTED) socket_changed = 1; + e = gf_dm_get_url_info(url, &info, sess->remote_path); + if (e) return e; + if (sess->port != info.port) { socket_changed = 1; sess->port = info.port; @@ -939,8 +961,7 @@ GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url) if (sess->sock && !socket_changed) { sess->status = GF_NETIO_CONNECTED; sess->num_retry = SESSION_RETRY_COUNT; - /*this should be done when building HTTP GET request in case we have range directives*/ - gf_dm_configure_cache(sess); + sess->needs_cache_reconfig = 1; } else { if (sess->sock) gf_sk_del(sess->sock); sess->sock = NULL; @@ -1039,8 +1060,10 @@ GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read) { +#ifdef GPAC_HAS_SSL GF_Err e; - if (!sess) +#endif + if (!sess) return GF_BAD_PARAM; #ifdef GPAC_HAS_SSL if (sess->ssl) { @@ -1325,11 +1348,34 @@ const char *gf_dm_sess_mime_type(GF_DownloadSession *sess) } GF_EXPORT -GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range) +GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache) { if (!sess) return GF_BAD_PARAM; - if (sess->cache_entry) return GF_BAD_PARAM; - if (sess->status != GF_NETIO_SETUP) return GF_BAD_PARAM; + if (sess->cache_entry) { + if (!discontinue_cache) { + if (gf_cache_get_end_range(sess->cache_entry) + 1 != start_range) + return GF_NOT_SUPPORTED; + } + if (!sess->sock) + return GF_BAD_PARAM; + if (sess->status != GF_NETIO_CONNECTED) { + if (sess->status != GF_NETIO_DISCONNECTED) { + return GF_BAD_PARAM; + } + } + sess->status = GF_NETIO_CONNECTED; + sess->num_retry = SESSION_RETRY_COUNT; + if (!discontinue_cache) { + gf_cache_set_end_range(sess->cache_entry, end_range); + /*remember this in case we get disconnected*/ + sess->is_range_continuation = 1; + } else { + sess->needs_cache_reconfig = 2; + sess->reused_cache_entry = 0; + } + } else { + if (sess->status != GF_NETIO_SETUP) return GF_BAD_PARAM; + } sess->range_start = start_range; sess->range_end = end_range; sess->needs_range = 1; @@ -1393,7 +1439,8 @@ GF_Err gf_dm_sess_process_headers(GF_DownloadSession *sess) gf_sleep(16); break; case GF_NETIO_WAIT_FOR_REPLY: - gf_sleep(16); +// gf_sleep(16); + gf_sleep(1); case GF_NETIO_CONNECTED: sess->do_requests(sess); break; @@ -1666,18 +1713,6 @@ static GFINLINE void gf_dm_data_received(GF_DownloadSession *sess, const char *d } } - if (sess->total_size && (sess->bytes_done == sess->total_size)) { - gf_dm_disconnect(sess, 0); - par.msg_type = GF_NETIO_DATA_TRANSFERED; - par.error = GF_OK; - gf_dm_sess_user_io(sess, &par); - if (sess->use_cache_file) { - gf_cache_close_write_cache(sess->cache_entry, sess, 1); - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, - ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry))); - } - return; - } /*update state if not done*/ if (rcv) { runtime = gf_sys_clock() - sess->start_time; @@ -1687,6 +1722,21 @@ static GFINLINE void gf_dm_data_received(GF_DownloadSession *sess, const char *d sess->bytes_per_sec = (1000 * sess->bytes_done) / runtime; } } + + if (sess->total_size && (sess->bytes_done == sess->total_size)) { + gf_dm_disconnect(sess, 0); + par.msg_type = GF_NETIO_DATA_TRANSFERED; + par.error = GF_OK; + + + gf_dm_sess_user_io(sess, &par); + if (sess->use_cache_file) { + gf_cache_close_write_cache(sess->cache_entry, sess, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, + ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry))); + } + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP] url %s downloaded in %d ms (%d kbps)\n", gf_cache_get_url(sess->cache_entry), gf_sys_clock() - sess->start_time, 8*sess->bytes_per_sec/1024 )); + } } @@ -1823,6 +1873,13 @@ static GF_Err http_send_headers(GF_DownloadSession *sess, char * sHTTP) { const char *param_string; Bool has_accept, has_connection, has_range, has_agent, has_language, send_profile, has_mime; assert (sess->status == GF_NETIO_CONNECTED); + + + if (sess->needs_cache_reconfig) { + gf_dm_configure_cache(sess); + sess->needs_cache_reconfig = 0; + } + /*setup authentification*/ strcpy(pass_buf, ""); sess->creds = gf_find_user_credentials_for_site( sess->dm, sess->server_name ); @@ -2619,6 +2676,15 @@ exit: void http_do_requests(GF_DownloadSession *sess) { char sHTTP[GF_DOWNLOAD_BUFFER_SIZE]; + + if (sess->reused_cache_entry) { + if (!gf_cache_is_in_progress(sess->cache_entry)) { + sess->status = GF_NETIO_DISCONNECTED; + sess->reused_cache_entry = 0; + } + return; + } + switch (sess->status) { case GF_NETIO_CONNECTED: http_send_headers(sess, sHTTP); @@ -2627,6 +2693,21 @@ void http_do_requests(GF_DownloadSession *sess) wait_for_header_and_parse(sess, sHTTP); break; case GF_NETIO_DATA_EXCHANGE: + /*session has been reassigned, resend data retrieved in first GET reply to user but don't write to cache*/ + if (sess->reassigned) { + + if (sess->icy_metaint > 0) { + gf_icy_skip_data(sess, sess->icy_metaint, sess->init_data, sess->init_data_size); + } else { + GF_NETIO_Parameter par; + par.msg_type = GF_NETIO_DATA_EXCHANGE; + par.error = GF_OK; + par.data = sess->init_data; + par.size = sess->init_data_size; + gf_dm_sess_user_io(sess, &par); + } + sess->reassigned = 0; + } http_parse_remaining_body(sess, sHTTP); break; } @@ -2932,7 +3013,7 @@ GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io us sess->flags = flags; sess->user_proc = user_io; sess->usr_cbk = cbk; - + sess->reassigned = sess->init_data ? 1 : 0; sess->num_retry = SESSION_RETRY_COUNT; if (sess->status==GF_NETIO_DISCONNECTED) diff --git a/src/utils/error.c b/src/utils/error.c index f6c57d1..939d773 100644 --- a/src/utils/error.c +++ b/src/utils/error.c @@ -547,7 +547,7 @@ static const u32 gf_crc_table[256] = { }; GF_EXPORT -u32 gf_crc_32(char *data, u32 len) +u32 gf_crc_32(const char *data, u32 len) { register u32 i; u32 crc = 0xffffffff; diff --git a/src/utils/os_divers.c b/src/utils/os_divers.c index 7c383fb..3813b0b 100644 --- a/src/utils/os_divers.c +++ b/src/utils/os_divers.c @@ -309,7 +309,7 @@ int gettimeofday(struct timeval *tp, struct timezone *tzp) } -#if 0 +#if _GPAC_UNUSED /* time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds FILETIME in Win32 is from jan 1, 1601 @@ -406,7 +406,11 @@ GF_Err gf_move_file(const char *fileName, const char *newFileName) return (MoveFile(fileName, newFileName) == 0 ) ? GF_IO_ERR : GF_OK; #else /* success is == 0 */ - return ( rename(fileName, newFileName) == 0) ? GF_OK : GF_IO_ERR; + char cmd[1024]; + if (!fileName || !newFileName) + return GF_IO_ERR; + snprintf(cmd, sizeof(cmd)-1, "mv %s %s > /dev/null 2>&1", fileName, newFileName); + return ( system(cmd) == 0) ? GF_OK : GF_IO_ERR; #endif } @@ -484,8 +488,13 @@ FILE *gf_temp_file_new() char tmp[MAX_PATH], t_file[100]; FILE *res = tmpfile(); if (res) return res; + { + u32 err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): %08x\n", err)); + } /*tmpfile() may fail under vista ...*/ - if (!GetEnvironmentVariable("TEMP",tmp,MAX_PATH)) return NULL; + if (!GetEnvironmentVariable("TEMP",tmp,MAX_PATH)) + return NULL; sprintf(t_file, "\\gpac_%08x.tmp", (u32) tmp); strcat(tmp, t_file); return gf_f64_open(tmp, "w+b"); @@ -804,7 +813,13 @@ GF_EXPORT FILE *gf_f64_open(const char *file_name, const char *mode) { #if defined(WIN32) - return fopen(file_name, mode); + FILE *res = fopen(file_name, mode); + if (res) return res; + if (strchr(mode, 'w') || strchr(mode, 'a')) { + u32 err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Win32] system failure for file opening of %s in mode %s: %08x\n", file_name, mode, err)); + } + return NULL; #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_ANDROID) return fopen64(file_name, mode); #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN)) @@ -1922,7 +1937,7 @@ GF_GlobalLock * gf_create_PID_file( const char * resourceName ) { int sz = 100; char * buf = gf_malloc( sz ); - sz = snprintf(buf, sz, "%ld\n", (long) getpid()); + sz = snprintf(buf, sz-1, "%ld\n", (long) getpid()); if (write(fd, buf, sz) != sz){ gf_free(buf); goto exit; diff --git a/src/utils/os_thread.c b/src/utils/os_thread.c index 9a1c990..0f7119f 100644 --- a/src/utils/os_thread.c +++ b/src/utils/os_thread.c @@ -700,6 +700,7 @@ u32 gf_sema_notify(GF_Semaphore *sm, u32 NbRelease) return (u32) prevCount; } +GF_EXPORT void gf_sema_wait(GF_Semaphore *sm) { #ifdef WIN32 diff --git a/src/utils/sha1.c b/src/utils/sha1.c index b78cdec..9711a86 100644 --- a/src/utils/sha1.c +++ b/src/utils/sha1.c @@ -693,6 +693,16 @@ s32 gf_sha1_file( const char *path, u8 output[20] ) GF_SHA1Context *ctx; u8 buf[1024]; + if (!strncmp(path, "gmem://", 7)) { + u32 size; + u8 *mem_address; + if (sscanf(path, "gmem://%d@%p", &size, &mem_address) != 2) { + return GF_IO_ERR; + } + gf_sha1_csum(mem_address, size, output); + return 0; + } + if( ( f = gf_f64_open( path, "rb" ) ) == NULL ) return( 1 ); diff --git a/src/utils/xml_parser.c b/src/utils/xml_parser.c index 45d742d..09220d4 100644 --- a/src/utils/xml_parser.c +++ b/src/utils/xml_parser.c @@ -26,12 +26,17 @@ #include #include + +#ifndef GPAC_DISABLE_ZLIB /*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/ #include #if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__) #pragma comment(lib, "zlib") #endif +#else +#define NO_GZIP +#endif static GF_Err gf_xml_sax_parse_intern(GF_SAXParser *parser, char *current); @@ -1178,6 +1183,40 @@ GF_Err gf_xml_sax_parse_file(GF_SAXParser *parser, const char *fileName, gf_xml_ #endif unsigned char szLine[6]; + parser->on_progress = OnProgress; + + if (!strncmp(fileName, "gmem://", 7)) { + u32 size; + u8 *xml_mem_address; + if (sscanf(fileName, "gmem://%d@%p", &size, &xml_mem_address) != 2) { + return GF_URL_ERROR; + } + parser->file_size = size; + + memcpy(szLine, xml_mem_address, 3); + szLine[4] = szLine[5] = 0; + e = gf_xml_sax_init(parser, szLine); + if (e) return e; + parser->file_pos = 4; + parser->elt_start_pos = 0; + parser->current_pos = 0; + + + e = gf_xml_sax_parse(parser, xml_mem_address+3); + if (parser->on_progress) parser->on_progress(parser->sax_cbck, parser->file_pos, parser->file_size); + + parser->elt_start_pos = parser->elt_end_pos = 0; + parser->elt_name_start = parser->elt_name_end = 0; + parser->att_name_start = 0; + parser->current_pos = 0; + parser->line_size = 0; + parser->att_sep = 0; + parser->file_pos = 0; + parser->file_size = 0; + parser->line_size = 0; + return e; + } + /*check file exists and gets its size (zlib doesn't support SEEK_END)*/ test = gf_f64_open(fileName, "rb"); if (!test) return GF_URL_ERROR; @@ -1186,7 +1225,6 @@ GF_Err gf_xml_sax_parse_file(GF_SAXParser *parser, const char *fileName, gf_xml_ parser->file_size = (u32) gf_f64_tell(test); fclose(test); - parser->on_progress = OnProgress; #ifdef NO_GZIP parser->f_in = gf_f64_open(fileName, "rt"); @@ -1294,7 +1332,11 @@ GF_EXPORT char *gf_xml_sax_peek_node(GF_SAXParser *parser, char *att_name, char *att_value, char *substitute, char *get_attr, char *end_pattern, Bool *is_substitute) { u32 state, att_len, alloc_size; +#ifdef NO_GZIP + u64 pos; +#else z_off_t pos; +#endif Bool from_buffer; Bool dobreak=0; char szLine1[XML_INPUT_SIZE+2], szLine2[XML_INPUT_SIZE+2], *szLine, *cur_line, *sep, *start, first_c, *result;