📄 mp4.c
字号:
} if( i_track_selected <= 0 ) { msg_Warn( p_input, "no track selected, exiting..." ); return 0; } /* first wait for the good time to read a packet */ input_ClockManageRef( p_input, p_input->stream.p_selected_program, p_sys->i_pcr ); /* update pcr XXX in mpeg scale so in 90000 unit/s */ p_sys->i_pcr = MP4_GetMoviePTS( p_sys ) * 9 / 100; /* we will read 100ms for each stream so ...*/ p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 ); /* Check if we need to send the audio data to decoder */ b_play_audio = !p_input->stream.control.b_mute; for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { mp4_track_t *tk = &p_sys->track[i_track]; if( !tk->b_ok || !tk->b_selected ) { continue; } while( MP4_TrackGetPTS( p_input, tk ) < MP4_GetMoviePTS( p_sys ) ) {#if 0 msg_Dbg( p_input, "tk=%lld mv=%lld", MP4_TrackGetPTS( p_input, tk ), MP4_GetMoviePTS( p_sys ) );#endif if( MP4_TrackSampleSize( tk ) > 0 && ( b_play_audio || tk->fmt.i_cat != AUDIO_ES ) ) { block_t *p_block; /* go,go go ! */ if( stream_Seek( p_input->s, MP4_TrackGetPos( tk ) ) ) { msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); MP4_TrackUnselect( p_input, tk ); break; } /* now read pes */ if( ( p_block = stream_Block( p_input->s, MP4_TrackSampleSize( tk ) ) ) == NULL ) { msg_Warn( p_input, "track[0x%x] will be disabled (eof?)", tk->i_track_ID ); MP4_TrackUnselect( p_input, tk ); break; } if( tk->b_drms && tk->p_drms ) { drms_decrypt( tk->p_drms, (uint32_t*)p_block->p_buffer, p_block->i_buffer ); } p_block->i_dts = input_ClockGetTS( p_input, p_input->stream.p_selected_program, MP4_TrackGetPTS( p_input, tk ) * 9/100 ); p_block->i_pts = tk->fmt.i_cat == VIDEO_ES ? 0 : p_block->i_dts; if( !tk->b_drms || ( tk->b_drms && tk->p_drms ) ) { es_out_Send( p_input->p_es_out, tk->p_es, p_block ); } } /* Next sample */ if( MP4_TrackNextSample( p_input, tk ) ) { break; } } } return 1;}/***************************************************************************** * Seek: Got to i_date ******************************************************************************/static int Seek ( input_thread_t *p_input, mtime_t i_date ){ demux_sys_t *p_sys = p_input->p_demux_data; unsigned int i_track; /* First update update global time */ p_sys->i_time = i_date * p_sys->i_timescale / 1000000; p_sys->i_pcr = i_date* 9 / 100; /* Now for each stream try to go to this time */ for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { mp4_track_t *tk = &p_sys->track[i_track]; if( tk->b_ok && tk->b_selected ) { MP4_TrackSeek( p_input, tk, i_date ); } } return( 1 );}/***************************************************************************** * Control: *****************************************************************************/static int Control( input_thread_t *p_input, int i_query, va_list args ){ demux_sys_t *p_sys = p_input->p_demux_data; double f, *pf; int64_t i64, *pi64; switch( i_query ) { case DEMUX_GET_POSITION: pf = (double*)va_arg( args, double * ); if( p_sys->i_duration > 0 ) { *pf = (double)p_sys->i_time / (double)p_sys->i_duration; } else { *pf = 0.0; } return VLC_SUCCESS; case DEMUX_SET_POSITION: f = (double)va_arg( args, double ); if( p_sys->i_timescale > 0 ) { i64 = (int64_t)( f * (double)1000000 * (double)p_sys->i_duration / (double)p_sys->i_timescale ); return Seek( p_input, i64 ); } else return VLC_SUCCESS; case DEMUX_GET_TIME: pi64 = (int64_t*)va_arg( args, int64_t * ); if( p_sys->i_timescale > 0 ) { *pi64 = (mtime_t)1000000 * (mtime_t)p_sys->i_time / (mtime_t)p_sys->i_timescale; } else *pi64 = 0; return VLC_SUCCESS; case DEMUX_SET_TIME: i64 = (int64_t)va_arg( args, int64_t ); return Seek( p_input, i64 ); case DEMUX_GET_LENGTH: pi64 = (int64_t*)va_arg( args, int64_t * ); if( p_sys->i_timescale > 0 ) { *pi64 = (mtime_t)1000000 * (mtime_t)p_sys->i_duration / (mtime_t)p_sys->i_timescale; } else *pi64 = 0; return VLC_SUCCESS; case DEMUX_GET_FPS: msg_Warn( p_input, "DEMUX_GET_FPS unimplemented !!" ); return VLC_EGENERIC; case DEMUX_GET_META: return VLC_EGENERIC; default: msg_Err( p_input, "control query unimplemented !!!" ); return demux_vaControlDefault( p_input, i_query, args ); }}/***************************************************************************** * Close: frees unused data *****************************************************************************/static void Close ( vlc_object_t * p_this ){ unsigned int i_track; input_thread_t * p_input = (input_thread_t *)p_this; demux_sys_t *p_sys = p_input->p_demux_data; msg_Dbg( p_input, "freeing all memory" ); MP4_BoxFree( p_input, p_sys->p_root ); for( i_track = 0; i_track < p_sys->i_tracks; i_track++ ) { MP4_TrackDestroy( p_input, &p_sys->track[i_track] ); } FREE( p_sys->track ); free( p_sys );}/**************************************************************************** * Local functions, specific to vlc ****************************************************************************//* now create basic chunk data, the rest will be filled by MP4_CreateSamplesIndex */static int TrackCreateChunksIndex( input_thread_t *p_input, mp4_track_t *p_demux_track ){ MP4_Box_t *p_co64; /* give offset for each chunk, same for stco and co64 */ MP4_Box_t *p_stsc; unsigned int i_chunk; unsigned int i_index, i_last; if( ( !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "stco" ) )&& !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "co64" ) ) )|| ( !(p_stsc = MP4_BoxGet( p_demux_track->p_stbl, "stsc" ) ) )) { return( VLC_EGENERIC ); } p_demux_track->i_chunk_count = p_co64->data.p_co64->i_entry_count; if( !p_demux_track->i_chunk_count ) { msg_Warn( p_input, "no chunk defined" ); return( VLC_EGENERIC ); } p_demux_track->chunk = calloc( p_demux_track->i_chunk_count, sizeof( mp4_chunk_t ) ); /* first we read chunk offset */ for( i_chunk = 0; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { p_demux_track->chunk[i_chunk].i_offset = p_co64->data.p_co64->i_chunk_offset[i_chunk]; } /* now we read index for SampleEntry( soun vide mp4a mp4v ...) to be used for the sample XXX begin to 1 We construct it begining at the end */ i_last = p_demux_track->i_chunk_count; /* last chunk proceded */ i_index = p_stsc->data.p_stsc->i_entry_count; if( !i_index ) { msg_Warn( p_input, "cannot read chunk table or table empty" ); return( VLC_EGENERIC ); } while( i_index ) { i_index--; for( i_chunk = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1; i_chunk < i_last; i_chunk++ ) { p_demux_track->chunk[i_chunk].i_sample_description_index = p_stsc->data.p_stsc->i_sample_description_index[i_index]; p_demux_track->chunk[i_chunk].i_sample_count = p_stsc->data.p_stsc->i_samples_per_chunk[i_index]; } i_last = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1; } p_demux_track->chunk[0].i_sample_first = 0; for( i_chunk = 1; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { p_demux_track->chunk[i_chunk].i_sample_first = p_demux_track->chunk[i_chunk-1].i_sample_first + p_demux_track->chunk[i_chunk-1].i_sample_count; } msg_Dbg( p_input, "track[Id 0x%x] read %d chunk", p_demux_track->i_track_ID, p_demux_track->i_chunk_count ); return( VLC_SUCCESS );}static int TrackCreateSamplesIndex( input_thread_t *p_input, mp4_track_t *p_demux_track ){ MP4_Box_t *p_stts; /* makes mapping between sample and decoding time, ctts make same mapping but for composition time, not yet used and probably not usefull */ MP4_Box_t *p_stsz; /* gives sample size of each samples, there is also stz2 that uses a compressed form FIXME make them in libmp4 as a unique type */ /* TODO use also stss and stsh table for seeking */ /* FIXME use edit table */ int64_t i_sample; int64_t i_chunk; int64_t i_index; int64_t i_index_sample_used; int64_t i_last_dts; p_stts = MP4_BoxGet( p_demux_track->p_stbl, "stts" ); p_stsz = MP4_BoxGet( p_demux_track->p_stbl, "stsz" ); /* FIXME and stz2 */ if( ( !p_stts )||( !p_stsz ) ) { msg_Warn( p_input, "cannot read sample table" ); return( VLC_EGENERIC ); } p_demux_track->i_sample_count = p_stsz->data.p_stsz->i_sample_count; /* for sample size, there are 2 case */ if( p_stsz->data.p_stsz->i_sample_size ) { /* 1: all sample have the same size, so no need to construct a table */ p_demux_track->i_sample_size = p_stsz->data.p_stsz->i_sample_size; p_demux_track->p_sample_size = NULL; } else { /* 2: each sample can have a different size */ p_demux_track->i_sample_size = 0; p_demux_track->p_sample_size = calloc( p_demux_track->i_sample_count, sizeof( uint32_t ) ); for( i_sample = 0; i_sample < p_demux_track->i_sample_count; i_sample++ ) { p_demux_track->p_sample_size[i_sample] = p_stsz->data.p_stsz->i_entry_size[i_sample]; } } /* we have extract all information from stsz, now use stts */ /* if we don't want to waste too much memory, we can't expand the box !, so each chunk will contain an "extract" of this table for fast research */ i_last_dts = 0; i_index = 0; i_index_sample_used =0; /* create and init last data for each chunk */ for(i_chunk = 0 ; i_chunk < p_demux_track->i_chunk_count; i_chunk++ ) { int64_t i_entry, i_sample_count, i; /* save last dts */ p_demux_track->chunk[i_chunk].i_first_dts = i_last_dts; /* count how many entries needed for this chunk for p_sample_delta_dts and p_sample_count_dts */ i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count; i_entry = 0; while( i_sample_count > 0 ) { i_sample_count -= p_stts->data.p_stts->i_sample_count[i_index+i_entry]; if( i_entry == 0 ) { i_sample_count += i_index_sample_used; /* don't count already used sample int this entry */ } i_entry++; } /* allocate them */ p_demux_track->chunk[i_chunk].p_sample_count_dts = calloc( i_entry, sizeof( uint32_t ) ); p_demux_track->chunk[i_chunk].p_sample_delta_dts = calloc( i_entry, sizeof( uint32_t ) ); /* now copy */ i_sample_count = p_demux_track->chunk[i_chunk].i_sample_count; for( i = 0; i < i_entry; i++ ) { int64_t i_used; int64_t i_rest; i_rest = p_stts->data.p_stts->i_sample_count[i_index] - i_index_sample_used; i_used = __MIN( i_rest, i_sample_count ); i_index_sample_used += i_used; i_sample_count -= i_used; p_demux_track->chunk[i_chunk].p_sample_count_dts[i] = i_used; p_demux_track->chunk[i_chunk].p_sample_delta_dts[i] = p_stts->data.p_stts->i_sample_delta[i_index]; i_last_dts += i_used * p_demux_track->chunk[i_chunk].p_sample_delta_dts[i]; if( i_index_sample_used >= p_stts->data.p_stts->i_sample_count[i_index] ) { i_index++; i_index_sample_used = 0; } } } msg_Dbg( p_input, "track[Id 0x%x] read %d samples length:"I64Fd"s", p_demux_track->i_track_ID, p_demux_track->i_sample_count, i_last_dts / p_demux_track->i_timescale ); return( VLC_SUCCESS );}/* * TrackCreateES: * Create ES and PES to init decoder if needed, for a track starting at i_chunk */static int TrackCreateES ( input_thread_t *p_input, mp4_track_t *p_track, unsigned int i_chunk, es_out_id_t **pp_es ){ MP4_Box_t *p_sample; MP4_Box_t *p_esds; *pp_es = NULL; if( !p_track->chunk[i_chunk].i_sample_description_index ) { msg_Warn( p_input, "invalid SampleEntry index (track[Id 0x%x])", p_track->i_track_ID ); return VLC_EGENERIC; } p_sample = MP4_BoxGet( p_track->p_stsd, "[%d]", p_track->chunk[i_chunk].i_sample_description_index - 1 ); if( !p_sample || !p_sample->data.p_data ) { msg_Warn( p_input, "cannot find SampleEntry (track[Id 0x%x])", p_track->i_track_ID ); return( VLC_EGENERIC ); } p_track->p_sample = p_sample; if( p_track->i_sample_size == 1 ) { MP4_Box_data_sample_soun_t *p_soun;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -