static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts ) { mp4_hnd_t *p_mp4 = handle; if( !p_mp4 ) return 0; if( p_mp4->p_config ) gf_odf_avc_cfg_del( p_mp4->p_config ); if( p_mp4->p_sample ) { if( p_mp4->p_sample->data ) free( p_mp4->p_sample->data ); p_mp4->p_sample->dataLength = 0; gf_isom_sample_del( &p_mp4->p_sample ); } if( p_mp4->p_file ) { if( p_mp4->i_track ) { /* The mdhd duration is defined as CTS[final] - CTS[0] + duration of last frame. * The mdhd duration (in seconds) should be able to be longer than the tkhd duration since the track is managed by edts. * So, if mdhd duration is equal to the last DTS or less, we give the last composition time delta to the last sample duration. * And then, the mdhd duration is updated, but it time-wise doesn't give the actual duration. * The tkhd duration is the actual track duration. */ uint64_t mdhd_duration = (2 * largest_pts - second_largest_pts) * p_mp4->i_time_inc; if( mdhd_duration != gf_isom_get_media_duration( p_mp4->p_file, p_mp4->i_track ) ) { uint64_t last_dts = gf_isom_get_sample_dts( p_mp4->p_file, p_mp4->i_track, p_mp4->i_numframe ); uint32_t last_duration = (uint32_t)( mdhd_duration > last_dts ? mdhd_duration - last_dts : (largest_pts - second_largest_pts) * p_mp4->i_time_inc ); gf_isom_set_last_sample_duration( p_mp4->p_file, p_mp4->i_track, last_duration ); } /* Write an Edit Box if the first CTS offset is positive. * A media_time is given by not the mvhd timescale but rather the mdhd timescale. * The reason is that an Edit Box maps the presentation time-line to the media time-line. * Any demuxers should follow the Edit Box if it exists. */ GF_ISOSample *sample = gf_isom_get_sample_info( p_mp4->p_file, p_mp4->i_track, 1, NULL, NULL ); if( sample && sample->CTS_Offset > 0 ) { uint32_t mvhd_timescale = gf_isom_get_timescale( p_mp4->p_file ); uint64_t tkhd_duration = (uint64_t)( mdhd_duration * ( (double)mvhd_timescale / p_mp4->i_time_res ) ); gf_isom_append_edit_segment( p_mp4->p_file, p_mp4->i_track, tkhd_duration, sample->CTS_Offset, GF_ISOM_EDIT_NORMAL ); } gf_isom_sample_del( &sample ); recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track ); } gf_isom_set_pl_indication( p_mp4->p_file, GF_ISOM_PL_VISUAL, 0x15 ); gf_isom_set_storage_mode( p_mp4->p_file, GF_ISOM_STORE_FLAT ); gf_isom_close( p_mp4->p_file ); } free( p_mp4 ); return 0; }
GF_Err ISOR_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) { char szURL[2048]; char *tmp; ISOMReader *read; if (!plug || !plug->priv || !serv) return GF_SERVICE_ERROR; read = (ISOMReader *) plug->priv; read->input = plug; read->service = serv; if (read->dnload) gf_term_download_del(read->dnload); read->dnload = NULL; read->base_track_id = 0; strcpy(szURL, url); tmp = strrchr(szURL, '.'); if (tmp) { tmp = strchr(tmp, '#'); if (tmp) { if (!strnicmp(tmp, "#trackID=", 9)) { read->base_track_id = atoi(tmp+9); } else { read->base_track_id = atoi(tmp+1); } tmp[0] = 0; } } if (isor_is_local(szURL)) { GF_Err e; u64 start_range, end_range; start_range = end_range = 0; if (plug->query_proxy) { GF_NetworkCommand param; param.command_type = GF_NET_SERVICE_QUERY_INIT_RANGE; if (read->input->query_proxy(read->input, ¶m)==GF_OK) { start_range = param.url_query.start_range; end_range = param.url_query.end_range; } } e = gf_isom_open_progressive(szURL, start_range, end_range, &read->mov, &read->missing_bytes); if (e != GF_OK) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] : error while opening %s, error=%s\n", szURL, gf_error_to_string(e))); gf_term_on_connect(serv, NULL, e); return GF_OK; } read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0; read->time_scale = gf_isom_get_timescale(read->mov); /*reply to user*/ gf_term_on_connect(serv, NULL, GF_OK); if (read->no_service_desc) isor_declare_objects(read); } else { /*setup downloader*/ isor_setup_download(plug, szURL); } return GF_OK; }
void isor_emulate_chapters(GF_ISOFile *file, GF_InitialObjectDescriptor *iod) { GF_Segment *prev_seg; u64 prev_start; u64 start; u32 i, count; if (!iod || gf_list_count(iod->OCIDescriptors)) return; count = gf_isom_get_chapter_count(file, 0); if (!count) return; prev_seg = NULL; start = prev_start = 0; for (i=0; i<count; i++) { const char *name; GF_Segment *seg; gf_isom_get_chapter(file, 0, i+1, &start, &name); seg = (GF_Segment *) gf_odf_desc_new(GF_ODF_SEGMENT_TAG); seg->startTime = (Double) (s64) start; seg->startTime /= 1000; seg->SegmentName = gf_strdup(name); gf_list_add(iod->OCIDescriptors, seg); if (prev_seg) { prev_seg->Duration = (Double) (s64) (start - prev_start); prev_seg->Duration /= 1000; } else if (start) { prev_seg = (GF_Segment *) gf_odf_desc_new(GF_ODF_SEGMENT_TAG); prev_seg->startTime = 0; prev_seg->Duration = (Double) (s64) (start); prev_seg->Duration /= 1000; gf_list_insert(iod->OCIDescriptors, prev_seg, 0); } prev_seg = seg; prev_start = start; } if (prev_seg) { start = 1000*gf_isom_get_duration(file); start /= gf_isom_get_timescale(file); if (start>prev_start) { prev_seg->Duration = (Double) (s64) (start - prev_start); prev_seg->Duration /= 1000; } } }
static void UpdateODCommand(GF_ISOFile *mp4, GF_ODCom *com) { u32 i, j; const char *szName; char szPath[2048]; szName = gf_isom_get_filename(mp4); if (com->tag == GF_ODF_OD_UPDATE_TAG) { GF_ObjectDescriptor *od; GF_ODUpdate *odU = (GF_ODUpdate *)com; i=0; while ((od = (GF_ObjectDescriptor *)gf_list_enum(odU->objectDescriptors, &i))) { GF_ESD *esd; j=0; while ((esd = (GF_ESD *)gf_list_enum(od->ESDescriptors, &j))) { Bool import = 1; if (esd->URLString) continue; switch (esd->decoderConfig->streamType) { case GF_STREAM_OD: import = 0; break; case GF_STREAM_SCENE: if ((esd->decoderConfig->objectTypeIndication != GPAC_OTI_SCENE_AFX) && (esd->decoderConfig->objectTypeIndication != GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE) ) { import = 0; } break; /*dump the OCR track duration in case the OCR is used by media controls & co*/ case GF_STREAM_OCR: { u32 track; Double dur; GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); gf_list_add(esd->extensionDescriptors, mi); track = gf_isom_get_track_by_id(mp4, esd->ESID); dur = (Double) (s64) gf_isom_get_track_duration(mp4, track); dur /= gf_isom_get_timescale(mp4); mi->duration = (u32) (dur * 1000); import = 0; } break; default: break; } if (import) { GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); gf_list_add(esd->extensionDescriptors, mi); sprintf(szPath, "%s#%d", szName, esd->ESID); mi->file_name = gf_strdup(szPath); mi->streamFormat = gf_strdup("MP4"); } } } return; } if (com->tag == GF_ODF_ESD_UPDATE_TAG) { GF_ESD *esd; GF_ESDUpdate *esdU = (GF_ESDUpdate *)com; i=0; while ((esd = (GF_ESD *)gf_list_enum(esdU->ESDescriptors, &i))) { Bool import = 1; if (esd->URLString) continue; switch (esd->decoderConfig->streamType) { case GF_STREAM_OD: import = 0; break; case GF_STREAM_SCENE: if ((esd->decoderConfig->objectTypeIndication != GPAC_OTI_SCENE_AFX) && (esd->decoderConfig->objectTypeIndication != GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE) ) { import = 0; } break; /*dump the OCR track duration in case the OCR is used by media controls & co*/ case GF_STREAM_OCR: { u32 track; Double dur; GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); gf_list_add(esd->extensionDescriptors, mi); track = gf_isom_get_track_by_id(mp4, esd->ESID); dur = (Double) (s64) gf_isom_get_track_duration(mp4, track); dur /= gf_isom_get_timescale(mp4); mi->duration = (u32) (dur * 1000); import = 0; } break; default: break; } if (import) { GF_MuxInfo *mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); gf_list_add(esd->extensionDescriptors, mi); sprintf(szPath, "%s#%d", szName, esd->ESID); mi->file_name = gf_strdup(szPath); mi->streamFormat = gf_strdup("MP4"); } } return; } }
static GF_Err gf_sm_load_run_isom(GF_SceneLoader *load) { GF_Err e; FILE *logs; u32 i, j, di, nbBifs, nbLaser, nb_samp, samp_done, init_offset; GF_StreamContext *sc; GF_ESD *esd; GF_ODCodec *od_dec; #ifndef GPAC_DISABLE_BIFS GF_BifsDecoder *bifs_dec; #endif #ifndef GPAC_DISABLE_LASER GF_LASeRCodec *lsr_dec; #endif if (!load || !load->isom) return GF_BAD_PARAM; nbBifs = nbLaser = 0; e = GF_OK; #ifndef GPAC_DISABLE_BIFS bifs_dec = gf_bifs_decoder_new(load->scene_graph, 1); gf_bifs_decoder_set_extraction_path(bifs_dec, load->localPath, load->fileName); #endif od_dec = gf_odf_codec_new(); logs = NULL; #ifndef GPAC_DISABLE_LASER lsr_dec = gf_laser_decoder_new(load->scene_graph); #endif esd = NULL; /*load each stream*/ nb_samp = 0; for (i=0; i<gf_isom_get_track_count(load->isom); i++) { u32 type = gf_isom_get_media_type(load->isom, i+1); switch (type) { case GF_ISOM_MEDIA_SCENE: case GF_ISOM_MEDIA_OD: nb_samp += gf_isom_get_sample_count(load->isom, i+1); break; default: break; } } samp_done = 1; gf_isom_text_set_streaming_mode(load->isom, 1); for (i=0; i<gf_isom_get_track_count(load->isom); i++) { u32 type = gf_isom_get_media_type(load->isom, i+1); switch (type) { case GF_ISOM_MEDIA_SCENE: case GF_ISOM_MEDIA_OD: break; default: continue; } esd = gf_isom_get_esd(load->isom, i+1, 1); if (!esd) continue; if ((esd->decoderConfig->objectTypeIndication == GPAC_OTI_SCENE_AFX) || (esd->decoderConfig->objectTypeIndication == GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE) ) { nb_samp += gf_isom_get_sample_count(load->isom, i+1); continue; } sc = gf_sm_stream_new(load->ctx, esd->ESID, esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); sc->streamType = esd->decoderConfig->streamType; sc->ESID = esd->ESID; sc->objectType = esd->decoderConfig->objectTypeIndication; sc->timeScale = gf_isom_get_media_timescale(load->isom, i+1); /*we still need to reconfig the BIFS*/ if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { #ifndef GPAC_DISABLE_BIFS /*BIFS*/ if (esd->decoderConfig->objectTypeIndication<=2) { if (!esd->dependsOnESID && nbBifs && !i) mp4_report(load, GF_OK, "several scene namespaces used or improper scene dependencies in file - import may be incorrect"); if (!esd->decoderConfig->decoderSpecificInfo) { /* Hack for T-DMB non compliant streams */ e = gf_bifs_decoder_configure_stream(bifs_dec, esd->ESID, NULL, 0, esd->decoderConfig->objectTypeIndication); } else { e = gf_bifs_decoder_configure_stream(bifs_dec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); } if (e) goto exit; nbBifs++; } #endif #ifndef GPAC_DISABLE_LASER /*LASER*/ if (esd->decoderConfig->objectTypeIndication==0x09) { if (!esd->dependsOnESID && nbBifs && !i) mp4_report(load, GF_OK, "several scene namespaces used or improper scene dependencies in file - import may be incorrect"); e = gf_laser_decoder_configure_stream(lsr_dec, esd->ESID, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); if (e) goto exit; nbLaser++; } #endif } init_offset = 0; /*dump all AUs*/ for (j=0; j<gf_isom_get_sample_count(load->isom, i+1); j++) { GF_AUContext *au; GF_ISOSample *samp = gf_isom_get_sample(load->isom, i+1, j+1, &di); if (!samp) { mp4_report(load, gf_isom_last_error(load->isom), "Unable to fetch sample %d from track ID %d - aborting track import", j+1, gf_isom_get_track_id(load->isom, i+1)); break; } /*check if track has initial offset*/ if (!j && gf_isom_get_edit_segment_count(load->isom, i+1)) { u64 EditTime, dur, mtime; u8 mode; gf_isom_get_edit_segment(load->isom, i+1, 1, &EditTime, &dur, &mtime, &mode); if (mode==GF_ISOM_EDIT_EMPTY) { init_offset = (u32) (dur * sc->timeScale / gf_isom_get_timescale(load->isom) ); } } samp->DTS += init_offset; au = gf_sm_stream_au_new(sc, samp->DTS, ((Double)(s64) samp->DTS) / sc->timeScale, (samp->IsRAP==RAP) ? 1 : 0); if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { #ifndef GPAC_DISABLE_BIFS if (esd->decoderConfig->objectTypeIndication<=2) e = gf_bifs_decode_command_list(bifs_dec, esd->ESID, samp->data, samp->dataLength, au->commands); #endif #ifndef GPAC_DISABLE_LASER if (esd->decoderConfig->objectTypeIndication==0x09) e = gf_laser_decode_command_list(lsr_dec, esd->ESID, samp->data, samp->dataLength, au->commands); #endif } else { e = gf_odf_codec_set_au(od_dec, samp->data, samp->dataLength); if (!e) e = gf_odf_codec_decode(od_dec); if (!e) { while (1) { GF_ODCom *odc = gf_odf_codec_get_com(od_dec); if (!odc) break; /*update ESDs if any*/ UpdateODCommand(load->isom, odc); gf_list_add(au->commands, odc); } } } gf_isom_sample_del(&samp); if (e) { mp4_report(load, gf_isom_last_error(load->isom), "decoding sample %d from track ID %d failed", j+1, gf_isom_get_track_id(load->isom, i+1)); goto exit; } samp_done++; gf_set_progress("MP4 Loading", samp_done, nb_samp); } gf_odf_desc_del((GF_Descriptor *) esd); esd = NULL; } gf_isom_text_set_streaming_mode(load->isom, 0); exit: #ifndef GPAC_DISABLE_BIFS gf_bifs_decoder_del(bifs_dec); #endif gf_odf_codec_del(od_dec); #ifndef GPAC_DISABLE_LASER gf_laser_decoder_del(lsr_dec); #endif if (esd) gf_odf_desc_del((GF_Descriptor *) esd); if (logs) gf_fclose(logs); return e; }
void isor_net_io(void *cbk, GF_NETIO_Parameter *param) { GF_Err e; u32 size = 0; char *local_name; ISOMReader *read = (ISOMReader *) cbk; /*handle service message*/ gf_term_download_update_stats(read->dnload); if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { e = GF_EOS; } else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) { e = GF_OK; size = param->size; } else { e = param->error; } if (e<GF_OK) { /*error opening service*/ if (!read->mov) gf_term_on_connect(read->service, NULL, e); return; } /*open file if not done yet (bad interleaving)*/ if (e==GF_EOS) { const char *local_name; if (read->mov) return; local_name = gf_dm_sess_get_cache_name(read->dnload); if (!local_name) { gf_term_on_connect(read->service, NULL, GF_SERVICE_ERROR); return; } e = GF_OK; read->mov = gf_isom_open(local_name, GF_ISOM_OPEN_READ, NULL); if (!read->mov) e = gf_isom_last_error(NULL); else read->time_scale = gf_isom_get_timescale(read->mov); gf_term_on_connect(read->service, NULL, GF_OK); if (read->no_service_desc) isor_declare_objects(read); } if (!size) return; /*service is opened, nothing to do*/ if (read->mov) return; /*try to open the service*/ local_name = (char *)gf_dm_sess_get_cache_name(read->dnload); if (!local_name) { gf_term_on_connect(read->service, NULL, GF_SERVICE_ERROR); return; } /*not enogh data yet*/ if (read->missing_bytes && (read->missing_bytes > size) ) { read->missing_bytes -= size; return; } e = gf_isom_open_progressive(local_name, 0, 0, &read->mov, &read->missing_bytes); switch (e) { case GF_ISOM_INCOMPLETE_FILE: return; case GF_OK: break; default: gf_term_on_connect(read->service, NULL, e); return; } read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0; /*ok let's go*/ read->time_scale = gf_isom_get_timescale(read->mov); gf_term_on_connect(read->service, NULL, GF_OK); if (read->no_service_desc) isor_declare_objects(read); }
int main(int argc, char **argv) { /********************/ /* declarations */ /********************/ char *input, *output, tmpstr[GF_MAX_PATH]; GF_ISOFile *isom_file_in; GF_MediaImporter import; AdobeHDSCtx ctx; GF_Err e; u32 i; /*****************/ /* gpac init */ /*****************/ gf_sys_init(GF_MemTrackerNone); gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_WARNING); /***********************/ /* initialisations */ /***********************/ input = NULL; output = NULL; isom_file_in = NULL; memset(&import, 0, sizeof(GF_MediaImporter)); e = GF_OK; memset(&ctx, 0, sizeof(ctx)); ctx.curr_time = 0; ctx.segnum = 1; /*********************************************/ /* parse arguments and build HDS context */ /*********************************************/ if (GF_OK != parse_args(argc, argv, &input, &output, &ctx.curr_time, &ctx.segnum)) { usage(argv[0]); goto exit; } ctx.multirate_manifest = adobe_alloc_multirate_manifest(output); #if 0 /*'moov' conversion tests*/ { char metamoov64[GF_MAX_PATH]; u32 metamoov64_len; unsigned char metamoov[GF_MAX_PATH]; u32 metamoov_len=GF_MAX_PATH; FILE *f = gf_fopen("metamoov64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); metamoov64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(metamoov64, metamoov64_len, 1, f); metamoov_len = gf_base64_decode(metamoov64, metamoov64_len, metamoov, metamoov_len); gf_fclose(f); f = gf_fopen("metamoov", "wb"); fwrite(metamoov, metamoov_len, 1, f); gf_fclose(f); return 0; } #endif #if 0 /*'abst'conversion tests*/ { char bootstrap64[GF_MAX_PATH]; u32 bootstrap64_len; unsigned char bootstrap[GF_MAX_PATH]; u32 bootstrap_len=GF_MAX_PATH; GF_AdobeBootstrapInfoBox *abst = (GF_AdobeBootstrapInfoBox *)abst_New(); GF_BitStream *bs; #if 1 //64 FILE *f = gf_fopen("bootstrap64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); bootstrap64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap64, bootstrap64_len, 1, f); bootstrap_len = gf_base64_decode(bootstrap64, bootstrap64_len, bootstrap, bootstrap_len); #else //binary bootstrap FILE *f = gf_fopen("bootstrap.bin"/*input*/, "rb"); gf_fseek(f, 0, SEEK_END); bootstrap_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap, bootstrap_len, 1, f); #endif bs = gf_bs_new(bootstrap+8, bootstrap_len-8, GF_BITSTREAM_READ); abst->size = bootstrap[2]*256+bootstrap[3]; assert(abst->size<GF_MAX_PATH); abst_Read((GF_Box*)abst, bs); gf_bs_del(bs); //then rewrite with just one 'afrt' memset(bootstrap, 0, bootstrap_len); bs = gf_bs_new(bootstrap, bootstrap_len, GF_BITSTREAM_WRITE); abst_Write((GF_Box*)abst, bs); bootstrap_len = (u32)gf_bs_get_position(bs); gf_bs_del(bs); gf_fclose(f); f = gf_fopen("bootstrap", "wt"); bootstrap64_len = gf_base64_encode(bootstrap, bootstrap_len, bootstrap64, GF_MAX_PATH); fwrite(bootstrap64, bootstrap64_len, 1, f); fprintf(f, "\n\n"); abst_dump((GF_Box*)abst, f); gf_fclose(f); abst_del((GF_Box*)abst); return 0; } #endif /*****************/ /* main loop */ /*****************/ import.trackID = 0; import.in_name = input; import.flags = GF_IMPORT_PROBE_ONLY; //create output or open when recovering from a saved state sprintf(tmpstr, "%s_import.mp4", input); isom_file_in = gf_isom_open(tmpstr, GF_ISOM_WRITE_EDIT, NULL); if (!isom_file_in) { fprintf(stderr, "Error opening output file %s: %s\n", tmpstr, gf_error_to_string(e)); assert(0); goto exit; } import.dest = isom_file_in; //probe input e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing input file %s: %s\n", input, gf_error_to_string(e)); assert(0); goto exit; } //import input data import.flags = 0; for (i=0; i<import.nb_tracks; i++) { import.trackID = import.tk_info[i].track_num; e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing track number %u, input file %s: %s\n", import.trackID, input, gf_error_to_string(e)); assert(0); goto exit; } } //Adobe specific stuff e = adobize_segment(isom_file_in, &ctx); if (e) { fprintf(stderr, "Couldn't turn the ISOM fragmented file into an Adobe f4v segment: %s\n", gf_error_to_string(e)); assert(0); goto exit; } //interleave data and remove imported file //FIXME: set multiple fragments: sprintf(tmpstr, "%s_HD_100_Seg%u-Frag1", output, ctx.segnum); //FIXME: "HD", "100" and fragnum: pass as arg //e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0); e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0+gf_isom_get_duration(isom_file_in)/gf_isom_get_timescale(isom_file_in)); if (e) { fprintf(stderr, "Error while fragmenting file to output %s: %s\n", output, gf_error_to_string(e)); assert(0); goto exit; } gf_isom_delete(isom_file_in); isom_file_in = NULL; e = adobe_gen_multirate_manifest(ctx.multirate_manifest, ctx.bootstrap, ctx.bootstrap_size); if (e) { fprintf(stderr, "Couldn't generate Adobe f4m manifest: %s\n", gf_error_to_string(e)); assert(0); goto exit; } exit: //delete intermediate mp4 file if (isom_file_in) gf_isom_delete(isom_file_in); if (ctx.multirate_manifest) adobe_free_multirate_manifest(ctx.multirate_manifest); if (ctx.bootstrap) { gf_free(ctx.bootstrap); //ctx.bootstrap = NULL; //ctx.bootstrap_size = 0; } gf_sys_close(); return !e ? 0 : 1; }
void isor_net_io(void *cbk, GF_NETIO_Parameter *param) { GF_Err e; u32 size = 0; char *local_name; ISOMReader *read = (ISOMReader *) cbk; /*handle service message*/ if (!read->buffering) gf_service_download_update_stats(read->dnload); if (param->msg_type==GF_NETIO_DATA_TRANSFERED) { e = GF_EOS; } else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) { e = GF_OK; size = param->size; } else { e = param->error; } if (e<GF_OK) { /*error opening service*/ if (!read->mov) { /* if there is an intermediate between this module and the terminal, report to it */ if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, e, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, e); } } return; } /*open file if not done yet (bad interleaving)*/ if (e==GF_EOS) { const char *local_name; if (read->mov) return; local_name = gf_dm_sess_get_cache_name(read->dnload); if (!local_name) { if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, GF_SERVICE_ERROR, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, GF_SERVICE_ERROR); } return; } e = GF_OK; read->mov = gf_isom_open(local_name, GF_ISOM_OPEN_READ, NULL); if (!read->mov) e = gf_isom_last_error(NULL); else read->time_scale = gf_isom_get_timescale(read->mov); read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0; if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, GF_OK, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, GF_OK); } if (read->no_service_desc) isor_declare_objects(read); } if (!size) return; /*service is opened, nothing to do*/ if (read->mov) { isor_check_buffer_level(read); /*end of chunk*/ if (read->frag_type && (param->reply==1) ) { u64 bytesMissing = 0; gf_mx_p(read->segment_mutex); e = gf_isom_refresh_fragmented(read->mov, &bytesMissing, NULL); gf_mx_v(read->segment_mutex); } return; } /*try to open the service*/ local_name = (char *)gf_dm_sess_get_cache_name(read->dnload); if (!local_name) { if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, GF_SERVICE_ERROR, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, GF_SERVICE_ERROR); } return; } /*not enogh data yet*/ if (read->missing_bytes && (read->missing_bytes > size) ) { read->missing_bytes -= size; return; } e = gf_isom_open_progressive(local_name, 0, 0, &read->mov, &read->missing_bytes); switch (e) { case GF_ISOM_INCOMPLETE_FILE: return; case GF_OK: break; default: if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, e, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, e); } return; } read->frag_type = gf_isom_is_fragmented(read->mov) ? 1 : 0; /*ok let's go, we can setup the decoders */ read->time_scale = gf_isom_get_timescale(read->mov); if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) { send_proxy_command(read, GF_FALSE, GF_FALSE, GF_OK, NULL, NULL); } else { gf_service_connect_ack(read->service, NULL, GF_OK); } if (read->no_service_desc) isor_declare_objects(read); }
void isor_check_buffer_level(ISOMReader *read) { Double dld_time_remaining, mov_rate; GF_NetworkCommand com; u32 i, total, done, Bps; u64 dur; GF_NetIOStatus status; Bool do_buffer = GF_FALSE; if (!read->dnload) return; if (!read->mov) return; gf_dm_sess_get_stats(read->dnload, NULL, NULL, &total, &done, &Bps, &status); if (!Bps) return; gf_mx_p(read->segment_mutex); dld_time_remaining = total-done; dld_time_remaining /= Bps; //we add 30 seconds to smooth out bitrate variations ..; dld_time_remaining += 30; mov_rate = total; dur = gf_isom_get_duration(read->mov); if (dur) { mov_rate /= dur; mov_rate *= gf_isom_get_timescale(read->mov); } for (i=0; i<gf_list_count(read->channels); i++) { ISOMChannel *ch = gf_list_get(read->channels, i); Double time_remain_ch = (Double) gf_isom_get_media_duration(read->mov, ch->track); u32 buffer_level=0; if (total==done) { time_remain_ch = 0; do_buffer = GF_FALSE; } else if (ch->last_state == GF_EOS) { time_remain_ch = 0; do_buffer = GF_TRUE; } else { u64 data_offset; u32 di, sn = ch->sample_num ? ch->sample_num : 1; GF_ISOSample *samp = gf_isom_get_sample_info(read->mov, ch->track, sn, &di, &data_offset); if (!samp) continue; data_offset += samp->dataLength; //we only send buffer on/off based on remainging playback time in channel #if 0 //we don't have enough data if (((data_offset + ch->buffer_min * mov_rate/1000 > done))) { do_buffer = GF_TRUE; } //we have enough buffer else if ((data_offset + ch->buffer_max * mov_rate/1000 <= done)) { do_buffer = GF_FALSE; } #endif time_remain_ch -= (samp->DTS + samp->CTS_Offset); if (time_remain_ch<0) time_remain_ch=0; gf_isom_sample_del(&samp); time_remain_ch /= ch->time_scale; if (time_remain_ch && (time_remain_ch < dld_time_remaining)) { do_buffer = GF_TRUE; if (!read->remain_at_buffering_start || (read->remain_at_buffering_start < dld_time_remaining)) { buffer_level = 0; read->remain_at_buffering_start = dld_time_remaining; } else { buffer_level = (u32) (100 * (read->remain_at_buffering_start - dld_time_remaining) / (read->remain_at_buffering_start - time_remain_ch) ); } } else { do_buffer = GF_FALSE; } } if (do_buffer != ch->buffering) { GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[IsoMedia] Buffering %s at %d: %g sec still to download and %g sec still to play on track %d (movie rate %g - download rate %g kbps)\n", do_buffer ? "on" : "off", gf_sys_clock(), dld_time_remaining , time_remain_ch, ch->track_id, mov_rate*8/1000, Bps*8.0/1000)); memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = do_buffer ? GF_NET_CHAN_PAUSE : GF_NET_CHAN_RESUME; com.buffer.on_channel = ch->channel; com.buffer.min = ch->buffer_min; com.buffer.max = ch->buffer_max; gf_service_command(read->service, &com, GF_OK); ch->buffering = do_buffer; read->buffering = do_buffer; } else if (ch->buffering) { memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = GF_NET_CHAN_BUFFER; com.buffer.on_channel = ch->channel; com.buffer.min = ch->buffer_min; com.buffer.max = ch->buffer_max; com.buffer.occupancy = buffer_level; gf_service_command(read->service, &com, GF_OK); } } gf_mx_v(read->segment_mutex); }
GF_Err adobize_segment(GF_ISOFile *isom_file, AdobeHDSCtx *ctx) { GF_Err e; GF_BitStream *bs; GF_AdobeFragRandomAccessBox *afra = (GF_AdobeFragRandomAccessBox*) afra_New(); GF_AfraEntry *ae = (GF_AfraEntry*) gf_calloc(1, sizeof(GF_AfraEntry)); GF_AdobeBootstrapInfoBox *abst = (GF_AdobeBootstrapInfoBox*) abst_New(); GF_AdobeSegmentRunTableBox *asrt = (GF_AdobeSegmentRunTableBox*) asrt_New(); GF_AdobeSegmentRunEntry *asre = (GF_AdobeSegmentRunEntry*) gf_calloc(1, sizeof(GF_AdobeSegmentRunEntry)); GF_AdobeFragmentRunTableBox *afrt = (GF_AdobeFragmentRunTableBox*) afrt_New(); GF_AdobeFragmentRunEntry *afre = (GF_AdobeFragmentRunEntry*) gf_calloc(1, sizeof(GF_AdobeFragmentRunEntry)); u64 init_seg_time = ctx->curr_time; u32 seg_duration = (u32)gf_isom_get_duration(isom_file); //update context ctx->curr_time += seg_duration; //Adobe specific boxes //Random Access afra->type = GF_4CC('a', 'f', 'r', 'a'); afra->version = 0; afra->flags = 0; afra->long_ids = 1; afra->long_offsets = 1; afra->global_entries = 0; afra->time_scale = gf_isom_get_timescale(isom_file); afra->entry_count = 1; ae->time = init_seg_time; ae->offset = 3999; gf_list_add(afra->local_access_entries, ae); afra->global_entries = 0; afra->global_entry_count = 0; e = gf_list_add(isom_file->TopBoxes, afra); if (e) { fprintf(stderr, "Impossible to write AFRA box: %s\n", gf_error_to_string(e)); assert(0); return e; } //Bootstrap Info abst->type = GF_4CC('a', 'b', 's', 't'); abst->version = 0; abst->flags = 0; abst->bootstrapinfo_version = 1; abst->profile = 0; abst->live = 1; abst->update = 0; abst->time_scale = gf_isom_get_timescale(isom_file); abst->current_media_time = init_seg_time+seg_duration; abst->smpte_time_code_offset = 0; abst->movie_identifier = NULL; abst->drm_data = NULL; abst->meta_data = NULL; abst->server_entry_count = 0; abst->quality_entry_count = 0; abst->segment_run_table_count = 1; { //Segment Run asrt->type = GF_4CC('a', 's', 'r', 't'); asrt->version = 0; asrt->flags = 0; asrt->segment_run_entry_count = 1; { asre->first_segment = ctx->segnum; asre->fragment_per_segment = 1; } e = gf_list_add(asrt->segment_run_entry_table, asre); if (e) { fprintf(stderr, "Impossible to write ASR Entry: %s\n", gf_error_to_string(e)); assert(0); return e; } } e = gf_list_add(abst->segment_run_table_entries, asrt); if (e) { fprintf(stderr, "Impossible to write ASRT box: %s\n", gf_error_to_string(e)); assert(0); return e; } abst->fragment_run_table_count = 1; { //Fragment Run afrt->type = GF_4CC('a', 'f', 'r', 't'); afrt->version = 0; afrt->flags = 0; afrt->timescale = gf_isom_get_timescale(isom_file); afrt->fragment_run_entry_count = 1; { afre->first_fragment = 1; afre->first_fragment_timestamp = 0; afre->fragment_duration = seg_duration; } e = gf_list_add(afrt->fragment_run_entry_table, afre); if (e) { fprintf(stderr, "Impossible to write AFR Entry: %s\n", gf_error_to_string(e)); assert(0); return e; } } e = gf_list_add(abst->fragment_run_table_entries, afrt); if (e) { fprintf(stderr, "Impossible to write AFRT box: %s\n", gf_error_to_string(e)); assert(0); return e; } e = gf_list_add(isom_file->TopBoxes, abst); if (e) { fprintf(stderr, "Impossible to write ABST box: %s\n", gf_error_to_string(e)); assert(0); return e; } e = abst_Size((GF_Box*)abst); if (e) { fprintf(stderr, "Impossible to compute ABST box size: %s\n", gf_error_to_string(e)); assert(0); return e; } ctx->bootstrap_size = (size_t)abst->size; ctx->bootstrap = gf_malloc(ctx->bootstrap_size); bs = gf_bs_new(ctx->bootstrap, ctx->bootstrap_size, GF_BITSTREAM_WRITE); e = abst_Write((GF_Box*)abst, bs); if (e) { fprintf(stderr, "Impossible to code the ABST box: %s\n", gf_error_to_string(e)); assert(0); gf_bs_del(bs); return e; } gf_bs_del(bs); //set brands as reversed engineered from f4v files /*e = gf_isom_reset_alt_brands(isom_file); if (e) { fprintf(stderr, "Warning: couldn't reset ISOM brands: %s\n", gf_error_to_string(e)); assert(0); }*/ gf_isom_set_brand_info(isom_file, GF_4CC('f','4','v',' '), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('i','s','o','m'), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('m','p','4','2'), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('m','4','v',' '), 1); return GF_OK; }