MP4 MP4同样是一种容器格式,是由一个一个Box组成,每个Box又分为Header与Data,Data又包含很多子Box,具体的MP4文件结构也看过,内部Box结构比较复杂,一般不写MP4解释器的话,Box结构不用了解太细,对MP4封装有MP4V2库可以使用,当然也可以使用FFMPEG库。 本文从代码的角度上来分析使用FFMPEG对MP4文件解复用。 完整代码如下:
const int sampling_frequencies[ ] = {
96000 ,
88200 ,
64000 ,
48000 ,
44100 ,
32000 ,
24000 ,
22050 ,
16000 ,
12000 ,
11025 ,
8000
} ;
int adts_header( char * const p_adts_header, const int data_length,
const int profile, const int samplerate,
const int channels)
{
int sampling_frequency_index = 3 ;
int adtsLen = data_length + 7 ;
int frequencies_size = sizeof( sampling_frequencies) / sizeof( sampling_frequencies[ 0 ] ) ;
for( int i = 0 ; i < frequencies_size; i++)
{
if( sampling_frequencies[ i] == samplerate)
{
sampling_frequency_index = i;
break ;
}
}
p_adts_header[ 0 ] = 0xff;
p_adts_header[ 1 ] = 0xf0;
p_adts_header[ 1 ] | = ( 0 << 3 ) ;
p_adts_header[ 1 ] | = ( 0 << 1 ) ;
p_adts_header[ 1 ] | = 1 ;
p_adts_header[ 2 ] = ( profile) << 6 ;
p_adts_header[ 2 ] | = ( sampling_frequency_index & 0x0f) << 2 ;
p_adts_header[ 2 ] | = ( 0 << 1 ) ;
p_adts_header[ 2 ] | = ( channels & 0x04) >> 2 ;
p_adts_header[ 3 ] = ( channels & 0x03) << 6 ;
p_adts_header[ 3 ] | = ( 0 << 5 ) ;
p_adts_header[ 3 ] | = ( 0 << 4 ) ;
p_adts_header[ 3 ] | = ( 0 << 3 ) ;
p_adts_header[ 3 ] | = ( 0 << 2 ) ;
p_adts_header[ 3 ] | = (( adtsLen & 0x1800) >> 11 ) ;
p_adts_header[ 4 ] = ( uint8_t) (( adtsLen & 0x7f8) >> 3 ) ;
p_adts_header[ 5 ] = ( uint8_t) (( adtsLen & 0x7) << 5 ) ;
p_adts_header[ 5 ] | = 0x1f;
p_adts_header[ 6 ] = 0xfc;
return 0 ;
}
int main( int argc, char **argv)
{
char *in_filename = "/home/yx/media_file/believe.mp4" ;
char *h264_filename = "/home/yx/media_file/believe.h264" ;
char *aac_filename = "/home/yx/media_file/believe.aac" ;
FILE *aac_fd = NULL;
FILE *h264_fd = NULL;
h264_fd = fopen( h264_filename, "wb" ) ;
aac_fd = fopen( aac_filename, "wb" ) ;
AVFormatContext *ifmt_ctx = NULL;
int video_index = -1;
int audio_index = -1;
AVPacket *pkt = NULL;
ifmt_ctx = avformat_alloc_context( ) ;
avformat_open_input( & ifmt_ctx, in_filename, NULL, NULL) ; // 将媒体流与解复用器相关联
video_index = av_find_best_stream( ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0 ) ; // 查找视频流标签
audio_index = av_find_best_stream( ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0 ) ; // 查找音频流标签
const AVBitStreamFilter *bsfilter = av_bsf_get_by_name( "h264_mp4toannexb" ) ; // 查找H264过滤器
AVBSFContext *bsf_ctx = NULL;
av_bsf_alloc( bsfilter, & bsf_ctx) ;
avcodec_parameters_copy( bsf_ctx-> par_in, ifmt_ctx-> streams[ video_index] -> codecpar) ;
av_bsf_init( bsf_ctx) ; // 初始化过滤器
pkt = av_packet_alloc( ) ;
av_init_packet( pkt) ;
while ( 1 )
{
if( av_read_frame( ifmt_ctx, pkt) < 0 ) // 逐个包读取
break ;
if( pkt-> stream_index == video_index)
{
av_bsf_send_packet( bsf_ctx, pkt) ; // 视频包就将包发给过滤器
while ( 1 )
{
if( av_bsf_receive_packet( bsf_ctx, pkt) != 0 ) // 从过滤器接收包
break ;
size_t size = fwrite( pkt-> data, 1 , pkt-> size, h264_fd) ; // 将包写到输出文件
av_packet_unref( pkt) ;
}
}
else if( pkt-> stream_index == audio_index)
{
char adts_header_buf[ 7 ] = { 0 } ;
adts_header( adts_header_buf, pkt-> size,
ifmt_ctx-> streams[ audio_index] -> codecpar-> profile,
ifmt_ctx-> streams[ audio_index] -> codecpar-> sample_rate,
ifmt_ctx-> streams[ audio_index] -> codecpar-> channels) ; // 音频包构建ADTS帧头部
fwrite( adts_header_buf, 1 , 7 , aac_fd) ; // 将头部写到输出文件
size_t size = fwrite( pkt-> data, 1 , pkt-> size, aac_fd) ; // 将AAC包写到输出文件
av_packet_unref( pkt) ; // 引用计数--
}
else
{
av_packet_unref( pkt) ;
}
}
printf( "while finish\n " ) ;
printf( "Hello World!\n " ) ;
return 0 ;
}