上一篇文章,是分离视频文件里的mp3音频码流,分离出来后是可以直接播放的。但是对于aac编码的音频,直接分离aac码流是不一定能播放的。如果视频复用格式是TS,则直接存储AVPacket的data是可以播放的。如果是mp4/flv/mkv等格式则不行,因为调用av_read_frame()后得到的AVPacket里面的内容是AAC纯数据,就是那种不包含ADTS文件头的AAC数据。
本以为仿照分离h264的方法,用 “aac_adtstoasc” bitstream filter会起作用,没想到还是不能播放,官方文档对aac_adtstoasc的解释:
aac_adtstoasc只是把带ADTS头的AAC流封装进MOV/MP4等格式时,创建MPEG-4 AudioSpecificConfig(asc),并去掉ADTS header。并没有相反的过程。所以只能手动添加7字节的ADTS头。
下面代码输入是一个ts格式的视频,分离aac数据,直接可播放。
void demux_ts(void)
{
const char *src_filename = "/Users/zhw/Desktop/resource/sintel_h264_aac.ts";
const char *audio_dst_filename = "/Users/zhw/Desktop/sintel.aac";
int audio_index;
AVFormatContext *ifmt_ctx = NULL;
AVPacket pkt;
/* open input file, and allocate format context */
if (avformat_open_input(&ifmt_ctx, src_filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", src_filename);
exit(1);
}
/* retrieve stream information */
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
exit(1);
}
audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audio_index < 0) {
fprintf(stderr, "Could not find %s stream\n",
av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
return;
}
FILE *out_file = fopen(audio_dst_filename, "wb");
if (!out_file) {
fprintf(stderr, "open out_file error");
return;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_index) {
fwrite(pkt.data, 1, pkt.size, out_file);
}
av_packet_unref(&pkt);
}
printf("finish\n");
}
下面代码的输入是mkv/mp4/flv格式的视频文件, 分离aac码流,并添加ADTS header后,可以播放。
void demux_other(void)
{
const char *src_filename = "/Users/zhw/Desktop/resource/sintel_h264_aac.flv";
const char *audio_dst_filename = "/Users/zhw/Desktop/sintel.aac";
int audio_index;
AVFormatContext *ifmt_ctx = NULL;
AVPacket pkt;
/* open input file, and allocate format context */
if (avformat_open_input(&ifmt_ctx, src_filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", src_filename);
exit(1);
}
/* retrieve stream information */
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
exit(1);
}
audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audio_index < 0) {
fprintf(stderr, "Could not find %s stream\n",
av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
return;
}
FILE *out_file = fopen(audio_dst_filename, "wb");
if (!out_file) {
fprintf(stderr, "open out_file error");
return;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
AVStream *stream = ifmt_ctx->streams[audio_index];
AVCodecParameters *params = stream->codecpar;
//从文件读取数据
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == audio_index) {
char bits[7] = {0};
int aac_frame_length = 7 + pkt.size;
int sample_index = find_sample_index(params->sample_rate);
int channels = params->channels;
if (channels == 8) {
channels = 7;
}
bits[0] = 0xff;
bits[1] = 0xf9;
bits[2] = (params->profile << 6);
bits[2] |= (sample_index << 2);
bits[2] |= (channels >> 2);
bits[3] |= ((channels << 6) & 0xC0);
bits[3] |= (aac_frame_length >> 11);
bits[4] = ((aac_frame_length >> 3) & 0xFF);
bits[5] = ((aac_frame_length << 5) & 0xE0);
bits[5] |= (0x7FF >> 6);
bits[6] = 0xfc;
fwrite(bits, 1, 7, out_file);
fwrite(pkt.data, 1, pkt.size, out_file);
}
av_packet_unref(&pkt);
}
printf("finish\n");
}
int find_sample_index(int samplerate)
{
int adts_sample_rates[] = {96000,882000,64000,48000,441000,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0};
int i;
for(i=0; i < 16;i++)
{
if(samplerate == adts_sample_rates[i])
return i;
}
return 16 - 1;
}