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; }
static void recompute_bitrate_mp4( GF_ISOFile *p_file, int i_track ) { u32 count, di, timescale, time_wnd, rate; u64 offset; Double br; GF_ESD *esd; esd = gf_isom_get_esd( p_file, i_track, 1 ); if( !esd ) return; esd->decoderConfig->avgBitrate = 0; esd->decoderConfig->maxBitrate = 0; rate = time_wnd = 0; timescale = gf_isom_get_media_timescale( p_file, i_track ); count = gf_isom_get_sample_count( p_file, i_track ); for( u32 i = 0; i < count; i++ ) { GF_ISOSample *samp = gf_isom_get_sample_info( p_file, i_track, i+1, &di, &offset ); if( !samp ) { x264_cli_log( "mp4", X264_LOG_ERROR, "failure reading back frame %u\n", i ); break; } if( esd->decoderConfig->bufferSizeDB < samp->dataLength ) esd->decoderConfig->bufferSizeDB = samp->dataLength; esd->decoderConfig->avgBitrate += samp->dataLength; rate += samp->dataLength; if( samp->DTS > time_wnd + timescale ) { if( rate > esd->decoderConfig->maxBitrate ) esd->decoderConfig->maxBitrate = rate; time_wnd = samp->DTS; rate = 0; } gf_isom_sample_del( &samp ); } br = (Double)(s64)gf_isom_get_media_duration( p_file, i_track ); br /= timescale; esd->decoderConfig->avgBitrate = (u32)(esd->decoderConfig->avgBitrate / br); /*move to bps*/ esd->decoderConfig->avgBitrate *= 8; esd->decoderConfig->maxBitrate *= 8; gf_isom_change_mpeg4_description( p_file, i_track, 1, esd ); gf_odf_desc_del( (GF_Descriptor*)esd ); }
void gf_media_get_sample_average_infos(GF_ISOFile *file, u32 Track, u32 *avgSize, u32 *MaxSize, u32 *TimeDelta, u32 *maxCTSDelta, u32 *const_duration, u32 *bandwidth) { u32 i, count, ts_diff; u64 prevTS, DTS, tdelta; Double bw; GF_ISOSample *samp; *avgSize = *MaxSize = 0; *TimeDelta = 0; *maxCTSDelta = 0; bw = 0; prevTS = 0; DTS = 0; tdelta = 0; count = gf_isom_get_sample_count(file, Track); *const_duration = 0; for (i=0; i<count; i++) { samp = gf_isom_get_sample_info(file, Track, i+1, NULL, NULL); //get the size *avgSize += samp->dataLength; if (*MaxSize < samp->dataLength) *MaxSize = samp->dataLength; ts_diff = (u32) (samp->DTS+samp->CTS_Offset - prevTS); //get the time tdelta += ts_diff; if (i==1) { *const_duration = ts_diff; } else if ( (i<count-1) && (*const_duration != ts_diff) ) { *const_duration = 0; } prevTS = samp->DTS+samp->CTS_Offset; bw += 8*samp->dataLength; //get the CTS delta if (samp->CTS_Offset > *maxCTSDelta) *maxCTSDelta = samp->CTS_Offset; gf_isom_sample_del(&samp); } if (count>1) *TimeDelta = (u32) (tdelta/ (count-1) ); else *TimeDelta = (u32) tdelta; *avgSize /= count; bw *= gf_isom_get_media_timescale(file, Track); bw /= (s64) gf_isom_get_media_duration(file, Track); bw /= 1000; (*bandwidth) = (u32) (bw+0.5); //delta is NOT an average, we need to know exactly how many bits are //needed to encode CTS-DTS for ANY samples }
GF_Err get_laser_track_size(GF_ISOFile *mp4, u32 *size) { GF_Err e = GF_OK; u32 j; u32 track_id, trackNum; *size = 0; track_id = gf_isom_get_track_id(mp4, 1); trackNum = gf_isom_get_track_by_id(mp4, track_id); for (j=0; j<gf_isom_get_sample_count(mp4, trackNum); j++) { GF_ISOSample *samp = gf_isom_get_sample_info(mp4, trackNum, j+1, NULL, NULL); *size += samp->dataLength; gf_isom_sample_del(&samp); } return e; }
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); }