ffmpeg解复用aac
[[AAC ADTS格式分析]]
目标:从 MP4 格式的视频格式中抽取 aac 音频单独保存。
采样率
根据采样率和对应编号整理为一个数组:
int samplingFrequency[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,
};
组织 ADTS 头
void encode_adts_header(char* buf, const int data_length, int profile, int samplerate, int channels) {int sampling_frequency_index = 3; // 默认48000频率int adtsLen = data_length + 7; // 包长+头长=总长int frequencies_size = sizeof(samplingFrequency) / sizeof(samplingFrequency[0]);int i = 0;// 匹配采样评率for (i = 0; i < frequencies_size; i++) {if (samplingFrequency[i] == samplerate) {sampling_frequency_index = i;break;}}if (i >= frequencies_size) {printf("unsupport samplerate: %d\n", samplerate);return -1;}buf[0] = 0xFF; // syncword 高8位buf[1] = 0xF0; // syncword 第8位buf[1] |= (0 << 3); // ID 0表示MPEG-4 buf[1] |= (0 << 2); // layer 00buf[1] |= (0 << 1); buf[1] |= 1; // protection_absent 1 表示没有校验buf[2] = (profile << 6); // profile 支持哪个级别的AACbuf[2] |= (sampling_frequency_index & 0x0F) << 2; // 采样频率buf[2] |= (0 << 1); // private 0buf[2] |= (channels & 0x04) >> 2; // channels 声道 高1位buf[3] = (channels & 0x03) << 6; // channels 声道 低2位buf[3] |= (0 << 5);buf[3] |= (0 << 4);buf[3] |= (0 << 3);buf[3] |= (0 << 2);buf[3] |= (adtsLen & 0x1800) >> 11; // 包长高2位buf[4] = (uint8_t)((adtsLen & 0x7F8) >> 3); // 包长3-10位buf[5] = (uint8_t)((adtsLen & 0x7) << 5); // 包长最后三位// 0x7FF 可变码流buf[5] |= 0x1F;buf[6] = 0xFC;return 0;
}
读取 packet
// 运行带两个参数 分别时候输入文件和输出文件
if (argc < 3) {return -1;}const char* in_filepath = argv[1];const char* out_filepath = argv[2];int errbuf[1024] = {0};FILE* acc_fp = NULL;int ret = -1;AVFormatContext* ifmt_ctx = NULL; // 解码器上下文AVPacket pkt;av_log_set_level(AV_LOG_DEBUG); // 设置日志级别acc_fp = fopen(out_filepath, "wb"); // 打开文件准备写入if (!acc_fp) {av_log(NULL, AV_LOG_ERROR, "Open out_file error\n");return ret;}ret = avformat_open_input(&ifmt_ctx, in_filepath, NULL, NULL); // 输入源if (ret < 0) {av_strerror(ret, errbuf, 1024);av_log(NULL, AV_LOG_ERROR, "Could not open source file: %s, %d(%s)", in_filepath, ret, errbuf);return ret;}ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0) {av_strerror(ret, errbuf, 1024);av_log(NULL, AV_LOG_ERROR, "Find stream error: %d(%s)\n", ret, errbuf);return ret;}av_dump_format(ifmt_ctx, NULL, in_filepath, NULL); // dump文件到上下文// pkt = av_packet_alloc();av_init_packet(&pkt);// 找到音频流int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);if(audio_index < 0){av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",av_get_media_type_string(AVMEDIA_TYPE_AUDIO),in_filepath);return AVERROR(EINVAL);}// 判断音频流是不是aacif (ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC) {printf("The media file no contain AAC stream, it's codec_id is %d\n", ifmt_ctx->streams[audio_index]->codecpar->codec_id);goto failed;}
写入文件
while (av_read_frame(ifmt_ctx, &pkt) >= 0) {/* code */if (pkt.stream_index == audio_index) {char adts_header_buf[7] = {0};// adts_headerencode_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);fwrite(adts_header_buf, 1, 7, acc_fp);int len = fwrite(pkt.data, 1, pkt.size, acc_fp);if (len != pkt.size) {av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal\n");}}av_packet_unref(&pkt);}
完整代码
#include <stdio.h>
#include <libavformat/avformat.h>int samplingFrequency[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,
};void encode_adts_header(char* buf, const int data_length, int profile, int samplerate, int channels) {int sampling_frequency_index = 3;int adtsLen = data_length + 7;int frequencies_size = sizeof(samplingFrequency) / sizeof(samplingFrequency[0]);int i = 0;for (i = 0; i < frequencies_size; i++) {if (samplingFrequency[i] == samplerate) {sampling_frequency_index = i;break;}}if (i >= frequencies_size) {printf("unsupport samplerate: %d\n", samplerate);return -1;}buf[0] = 0xFF;buf[1] = 0xF0;buf[1] |= (0 << 3);buf[1] |= (0 << 2);buf[1] |= (0 << 1);buf[1] |= 1;buf[2] = (profile << 6);buf[2] |= (sampling_frequency_index & 0x0F) << 2;buf[2] |= (0 << 1);buf[2] |= (channels & 0x04) >> 2;buf[3] = (channels & 0x03) << 6;buf[3] |= (0 << 5);buf[3] |= (0 << 4);buf[3] |= (0 << 3);buf[3] |= (0 << 2);buf[3] |= (adtsLen & 0x1800) >> 11;buf[4] = (uint8_t)((adtsLen & 0x7F8) >> 3);buf[5] = (uint8_t)((adtsLen & 0x7) << 5);buf[5] |= 0x1F;buf[6] = 0xFC;return 0;
}int main(int argc, char* argv[]) {if (argc < 3) {return -1;}const char* in_filepath = argv[1];const char* out_filepath = argv[2];int errbuf[1024] = {0};FILE* acc_fp = NULL;int ret = -1;AVFormatContext* ifmt_ctx = NULL;AVPacket pkt;av_log_set_level(AV_LOG_DEBUG);acc_fp = fopen(out_filepath, "wb");if (!acc_fp) {av_log(NULL, AV_LOG_ERROR, "Open out_file error\n");return ret;}ret = avformat_open_input(&ifmt_ctx, in_filepath, NULL, NULL);if (ret < 0) {av_strerror(ret, errbuf, 1024);av_log(NULL, AV_LOG_ERROR, "Could not open source file: %s, %d(%s)", in_filepath, ret, errbuf);return ret;}ret = avformat_find_stream_info(ifmt_ctx, NULL);if (ret < 0) {av_strerror(ret, errbuf, 1024);av_log(NULL, AV_LOG_ERROR, "Find stream error: %d(%s)\n", ret, errbuf);return ret;}av_dump_format(ifmt_ctx, NULL, in_filepath, NULL);// pkt = av_packet_alloc();av_init_packet(&pkt);int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);if(audio_index < 0){av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",av_get_media_type_string(AVMEDIA_TYPE_AUDIO),in_filepath);return AVERROR(EINVAL);}if (ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC) {printf("The media file no contain AAC stream, it's codec_id is %d\n", ifmt_ctx->streams[audio_index]->codecpar->codec_id);goto failed;}while (av_read_frame(ifmt_ctx, &pkt) >= 0) {/* code */if (pkt.stream_index == audio_index) {char adts_header_buf[7] = {0};// adts_headerencode_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);fwrite(adts_header_buf, 1, 7, acc_fp);int len = fwrite(pkt.data, 1, pkt.size, acc_fp);if (len != pkt.size) {av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal\n");}}av_packet_unref(&pkt);}failed:if (ifmt_ctx) {avformat_close_input(&ifmt_ctx);}if (acc_fp) {fclose(acc_fp);}
}
参考资料:https://github.com/0voice