FFmpeg开发实战(四):FFmpeg 抽取音视频的音频数据
使用FFmpeg抽取音频数据为ACC文件之前,先了解一下AAC的相关概念及基本的文件封装格式方式。
学习链接为: 音视频编解码技术(二):AAC 音频编码技术。
使用FFmpeg抽取音视频的音频数据为ACC文件,代码如下:
#include "iostream" #include "string" extern "C" { #include "dshow.h" #include "libavutil/opt.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/mathematics.h" #include "libavutil/samplefmt.h" #include "libavutil/time.h" #include "libavutil/fifo.h" #include "libavdevice/avdevice.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" } void adts_header(char *szAdtsHeader, int dataLen); // 使用FFmpeg从视频中抽取音频 void extractAudio() { // 设置日志输出等级 av_log_set_level(AV_LOG_INFO); AVFormatContext *fmt_ctx = NULL; AVPacket pkt; av_register_all(); int ret; int len; int audio_index = -1; // 打开输入文件 ret = avformat_open_input(&fmt_ctx, "http://vfx.mtime.cn/Video/2019/03/17/mp4/190317150237409904.mp4", NULL, NULL); // 检查打开输入文件是否成功 if (ret < 0) { printf("cant open file,error message = %d\n", ret); return; } // 打开输出文件 FILE *dst_fd = fopen("D:/111.aac", "wb"); // w 写入 b 二进制文件 // 检查输出文件打开是否成功,如果失败,就输出日志,并关闭输出文件的引用 if (!dst_fd) { av_log(NULL, AV_LOG_ERROR, "Can't Open Out File!\n"); avformat_close_input(&fmt_ctx); } // 获取到音频流 av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); for (int i = 0; i < fmt_ctx->nb_streams; i++) { if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { audio_index = i; break; } } printf("Audio Stream Index = %d", audio_index); // 检查发现音频流的结果 if (audio_index < 0) { av_log(NULL, AV_LOG_ERROR, "Can't find Best Audio Stream!\n"); //printf("Reason = %s", av_err2str(ret)); // 关闭输出文件和输出文件的引用 avformat_close_input(&fmt_ctx); fclose(dst_fd); return; } while (av_read_frame(fmt_ctx, &pkt) >= 0) { if (pkt.stream_index == audio_index) { printf("Has Read An Audio Packet\n"); char adts_header_buf[7]; adts_header(adts_header_buf, pkt.size); fwrite(adts_header_buf, 1, 7, dst_fd); len = fwrite(pkt.data, 1, pkt.size, dst_fd); if (len != pkt.size) { av_log(NULL, AV_LOG_WARNING, "Waring! Length of data not equal size of pkt!\n"); } } // 将引用基数减一 av_packet_unref(&pkt); //av_free_packet(&pkt); } // 关闭文件(输入/输出) avformat_close_input(&fmt_ctx); if (dst_fd) { fclose(dst_fd); } } void adts_header(char *header, int dataLen) { // aac级别 int aac_type = 2 - 1; // 采样率下标 /** 0: 96000 Hz 1: 88200 Hz 2: 64000 Hz 3: 48000 Hz 4: 44100 Hz 5: 32000 Hz 6: 24000 Hz 7: 22050 Hz 8: 16000 Hz 9: 12000 Hz 10: 11025 Hz 11: 8000 Hz 12: 7350 Hz */ // 采样率下标:LC格式的为正常索引,HE格式的索引为除2后对应的采样索引 // 因为:HE使用了SBR技术,即 Spectral Band Replication(频段复制),所以存储同样的音频内容,HE文件较小。使用时采样率为LC的一半。 int sampling_frequency_index = 4; // 声道数 int channel_config = 2; // ADTS帧长度,包括ADTS长度和AAC声音数据长度的和。 int adtsLen = dataLen + 7; // syncword,标识一个帧的开始,固定为0xFFF,占12bit(byte0占8位,byte1占前4位) header[0] = 0xff; header[1] = 0xf0; // ID,MPEG 标示符。0表示MPEG-4,1表示MPEG-2。占1bit(byte1第5位) header[1] |= (0 << 3); // layer,固定为0,占2bit(byte1第6、7位) header[1] |= (0 << 1); // protection_absent,标识是否进行误码校验。0表示有CRC校验,1表示没有CRC校验。占1bit(byte1第8位) header[1] |= 1; // profile,标识使用哪个级别的AAC。1: AAC Main 2:AAC LC 3:AAC SSR 4:AAC LTP。占2bit(byte2第1、2位) header[2] = aac_type << 6; // sampling_frequency_index,采样率的下标。占4bit(byte2第3、4、5、6位) header[2] |= (sampling_frequency_index & 0x0f) << 2; // private_bit,私有位,编码时设置为0,解码时忽略。占1bit(byte2第7位) header[2] |= (0 << 1); // channel_configuration,声道数。占3bit(byte2第8位和byte3第1、2位) header[2] |= (channel_config & 0x04) >> 2; header[3] = (channel_config & 0x03) << 6; // original_copy,编码时设置为0,解码时忽略。占1bit(byte3第3位) header[3] |= (0 << 5); // home,编码时设置为0,解码时忽略。占1bit(byte3第4位) header[3] |= (0 << 4); // copyrighted_id_bit,编码时设置为0,解码时忽略。占1bit(byte3第5位) header[3] |= (0 << 3); // copyrighted_id_start,编码时设置为0,解码时忽略。占1bit(byte3第6位) header[3] |= (0 << 2); // aac_frame_length,ADTS帧长度,包括ADTS长度和AAC声音数据长度的和。占13bit(byte3第7、8位,byte4全部,byte5第1-3位) header[3] |= ((adtsLen & 0x1800) >> 11); header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); header[5] = (uint8_t)((adtsLen & 0x7) << 5); // adts_buffer_fullness,固定为0x7FF。表示是码率可变的码流 。占11bit(byte5后5位,byte6前6位) header[5] |= 0x1f; header[6] = 0xfc; // number_of_raw_data_blocks_in_frame,值为a的话表示ADST帧中有a+1个原始帧,(一个AAC原始帧包含一段时间内1024个采样及相关数据)。占2bit(byte6第7、8位)。 header[6] |= 0; } int main(int args, char *argv[]) { extractAudio(); }