赞
踩
编译环境:Ubuntu16.04 64位
交叉编译工具:arm-himix200-linux-gcc
我这里使用的是ffmpeg-5.1.2.tar.gz,下载地址点击下载地址。
cd /root/
tar zxvf ffmpeg-5.1.2.tar.gz
cd ffmpeg-5.1.2
mkdir output
./configure --cross-prefix=arm-himix200-linux- --enable-cross-compile --target-os=linux --cc=arm-himix200-linux-gcc --arch=arm --prefix=/root/ffmpeg-5.1.2/output --disable-x86asm --disable-debug --disable-doc --disable-zlib --disable-v4l2-m2m --disable-iconv --disable-network --disable-ffplay --disable-ffprobe --disable-symver --disable-indevs --disable-outdevs --disable-parsers --disable-bsfs --disable-filters --disable-protocols --disable-hwaccels --disable-muxers --disable-demuxers --disable-encoders --disable-decoders --enable-demuxer=aac --enable-demuxer=mp3 --enable-demuxer=wav --enable-decoder=mp3 --enable-decoder=aac --enable-decoder=pcm_alaw --enable-decoder=pcm_bluray --enable-decoder=pcm_dvd --enable-decoder=pcm_f16le --enable-decoder=pcm_f24le --enable-decoder=pcm_f32be --enable-decoder=pcm_f32le --enable-decoder=pcm_f64be --enable-decoder=pcm_f64le --enable-decoder=pcm_lxf --enable-decoder=pcm_mulaw --enable-decoder=pcm_s16be --enable-decoder=pcm_s16be_planar --enable-decoder=pcm_s16le --enable-decoder=pcm_s16le_planar --enable-decoder=pcm_s24be --enable-decoder=pcm_s24daud --enable-decoder=pcm_s24le --enable-decoder=pcm_s24le_planar --enable-decoder=pcm_s32be --enable-decoder=pcm_s32le --enable-decoder=pcm_s32le_planar --enable-decoder=pcm_s64be --enable-decoder=pcm_s64le --enable-decoder=pcm_s8 --enable-decoder=pcm_s8_planar --enable-decoder=pcm_u16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u24be --enable-decoder=pcm_u24le --enable-decoder=pcm_u32be --enable-decoder=pcm_u32le --enable-decoder=pcm_u8 --enable-decoder=pcm_vidc --enable-decoder=pcm_zork --enable-protocol=file --enable-small --enable-muxer=pcm_s16le --extra-cflags="-ffunction-sections -fdata-sections -fsigned-char -Wformat"
make
make install
这样,/root/ffmpeg-5.1.2/output下面就是咱们要的程序,bin目录下ffmpeg可以在开发板上运行,include下是需要的头文件,lib下是需要的静态库,share/ffmpeg/examples是一些可以参考的示例代码。
注意,./configure配置命令可以根据实际的需要进行裁剪,我的项目只需要将mp3、aac和wav解码,因此,只配置了相应的demuxer和decoder。
CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections
GCC链接操作是以section作为最小的处理单元,只要一个section中的某个符号被引用,该section就会被加入到可执行程序中去。因此,GCC在编译时可以使用 -ffunction-sections 和 -fdata-sections 将每个函数或符号创建为一个sections,其中每个sections名与function或data名保持一致。而在链接阶段, -Wl,–gc-sections 指示链接器去掉不用的section(其中-wl, 表示后面的参数 -gc-sections 传递给链接器),这样就能减少最终的可执行程序的大小了。
LIBS += libavdevice.a \
libavfilter.a \
libavformat.a \
libavcodec.a \
libavutil.a \
libswresample.a \
libswscale.a
注意,顺序不要乱,否则会报找不到函数,因为有依赖关系。
不需要用到libavdevice.a(读设备,摄像头或录屏等)、libavfilter.a(加特效,如水印等)和libswscale.a(图像拉伸,像素格式转换等)。
LIBS += libavformat.a \
libavcodec.a \
libavutil.a \
libswresample.a
#ifndef __AUDIO_DECODER__ #define __AUDIO_DECODER__ #ifdef __cplusplus extern "C" { #endif #include "libavformat/avformat.h" #include "libavformat/avio.h" #include "libavcodec/avcodec.h" #include "libavutil/audio_fifo.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/frame.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libswresample/swresample.h" #ifdef __cplusplus } #endif class CAudioDecoder { public: CAudioDecoder(); virtual ~CAudioDecoder(void); int DecodeOpen(uint codec); int DecodeClose(void); int DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize); private: void Decode(uchar *pOutBuf, int nBufferSize, int &nSize); public: static int transcode_pcm(const char* inputFile, const char* outputFile); private: AVCodec *m_Codec; AVCodecContext *m_Context; AVCodecParserContext *m_Parser; AVPacket *m_Packet; AVFrame *m_Frame; struct SwrContext* m_converCtx; AVChannelLayout m_ChannelLayout; int m_outBytesPerSample; uint8_t* m_pData; int m_nSize; };
不知道ffmpeg的头文件为什么没有extern “C”,C++和C在链接时的差异。
#define TRANSCODE_SAMPLERATE 8000 #define TRANSCODE_FORMAT AV_SAMPLE_FMT_S16 #define TRANSCODE_CHANNELS 1 #define TRANSCODE_NBSAMPLES 4096 /* * inputFile input file name * outputFile output file name * return decode the length of the data */ int CAudioDecoder::transcode_pcm(const char* inputFile, const char* outputFile) { CFile file; bool bRet = file.Open(outputFile, CFile::modeCreate | CFile::modeWrite); if (!bRet) return -1; int ret = -1; AVCodec* codec = NULL; AVCodecContext* codecContext = NULL; AVCodecParameters* codecpar = NULL; AVFrame* frame = NULL; AVPacket *pPacket = NULL; struct SwrContext* converCtx = NULL; uint8_t** data = NULL; int streamindex = 0; int outBytesPerSample = 0; int allocsize = 0; AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_MONO; AVFormatContext* frameContext = avformat_alloc_context(); if (frameContext == NULL) { goto cleanup; } if (avformat_open_input(&frameContext, inputFile, NULL, NULL) != 0) { goto cleanup; } if (avformat_find_stream_info(frameContext, NULL) < 0) { goto cleanup; } streamindex = av_find_best_stream(frameContext, AVMEDIA_TYPE_AUDIO, -1, -1, (const AVCodec**)&codec, -1); if (!codec) { goto cleanup; } codecContext = avcodec_alloc_context3(codec); codecpar = frameContext->streams[streamindex]->codecpar; avcodec_parameters_to_context(codecContext, codecpar); if (avcodec_open2(codecContext, codec, NULL) < 0) { goto cleanup; } outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT); frame = av_frame_alloc(); swr_alloc_set_opts2(&converCtx, &out_ch_layout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &codecContext->ch_layout, codecContext->sample_fmt, codecContext->sample_rate, 0, NULL); swr_init(converCtx); data = (uint8_t**)av_calloc(1, sizeof(*data)); allocsize = av_samples_alloc(data, NULL, TRANSCODE_CHANNELS, TRANSCODE_NBSAMPLES, TRANSCODE_FORMAT, 0); pPacket = av_packet_alloc(); while (av_read_frame(frameContext, pPacket) >= 0) { if (pPacket->stream_index != streamindex) continue; if (avcodec_send_packet(codecContext, pPacket) < 0) { goto cleanup; } while (avcodec_receive_frame(codecContext, frame) >= 0) { ret = swr_convert(converCtx, data, allocsize, (const uint8_t**)frame->data, frame->nb_samples); if (ret > 0) file.Write(data[0], ret * outBytesPerSample); } av_packet_unref(pPacket); } while ((ret = swr_convert(converCtx, data, allocsize, NULL, 0)) > 0) { file.Write(data[0], ret * outBytesPerSample); } cleanup: if (frameContext) { avformat_close_input(&frameContext); avformat_free_context(frameContext); } if (codecContext) avcodec_free_context(&codecContext); if (pPacket) av_packet_free(&pPacket); if (data) av_freep(&data[0]); av_freep(&data); if (frame) av_frame_free(&frame); if (converCtx) swr_free(&converCtx); if (file.IsOpened()) { ret = file.GetLength(); file.Close(); } return ret; }
注意,CFile是我的项目中的文件类,可以替换成文件IO的API。
以aac为例,由于用到了aac的parser,在交叉编译ffmpeg时选项需要添加选项:
--enable-parser=aac
类似,如果需要支持mp3的解码,在交叉编译ffmpeg时选项需要添加选项:
--enable-parser=mp3
其他格式类似。
#include "AudioDecoder.h" CAudioDecoder::CAudioDecoder() { m_Codec = NULL; m_Context = NULL; m_Parser = NULL; m_Packet = NULL; m_Frame = NULL; m_converCtx = NULL; m_ChannelLayout = AV_CHANNEL_LAYOUT_MONO; m_outBytesPerSample = 0; m_pData = NULL; m_nSize = 0; } CAudioDecoder::~CAudioDecoder(void) { DecodeClose(); } int CAudioDecoder::DecodeOpen(uint codec) { int ret = -1; switch (codec) { case CODEC_AAC: m_Codec = (AVCodec *)avcodec_find_decoder(AV_CODEC_ID_AAC); break; default: m_Codec = NULL; break; } if (!m_Codec) { fprintf(stderr, "Codec not found\n"); goto error; } m_Context = avcodec_alloc_context3(m_Codec); if (!m_Context) { fprintf(stderr, "Could not allocate audio codec context\n"); goto error; } ret = avcodec_open2(m_Context, m_Codec, NULL); if (ret < 0) { fprintf(stderr, "Could not open codec\n"); goto error; } m_Parser = av_parser_init(m_Codec->id); if (!m_Parser) { fprintf(stderr, "Parser not found\n"); goto error; } m_Packet = av_packet_alloc(); if (!m_Packet) { fprintf(stderr, "Could not allocate audio packet\n"); goto error; } m_Frame = av_frame_alloc(); if (!m_Frame) { fprintf(stderr, "Could not allocate audio frame\n"); goto error; } m_outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT); m_nSize = TRANSCODE_NBSAMPLES * m_outBytesPerSample; m_pData = (uint8_t *)av_malloc(m_nSize); return 0; error: DecodeClose(); return ret; } int CAudioDecoder::DecodeClose(void) { av_freep(&m_pData); if (m_Frame) av_frame_free(&m_Frame); if (m_converCtx) swr_free(&m_converCtx); if (m_Packet) av_packet_free(&m_Packet); if (m_Parser) av_parser_close(m_Parser); if (m_Context) avcodec_free_context(&m_Context); return 0; } int CAudioDecoder::DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize) { if (pFrame == NULL || nFrameSize == 0 || pOutBuf == NULL || nBufferSize == 0) { return -1; } int nSize = 0; uint8_t *data = pFrame; int data_size = nFrameSize; while (data_size > 0) { int ret = av_parser_parse2(m_Parser, m_Context, &m_Packet->data, &m_Packet->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { fprintf(stderr, "Error while parsing\n"); break; } if (m_Packet->size > 0) { data += ret; data_size -= ret; Decode(pOutBuf, nBufferSize, nSize); } else { break; } } return nSize; } void CAudioDecoder::Decode(uchar *pOutBuf, int nBufferSize, int &nSize) { int ret = avcodec_send_packet(m_Context, m_Packet); if (ret < 0) { return ; } swr_alloc_set_opts2(&m_converCtx, &m_ChannelLayout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &m_Context->ch_layout, m_Context->sample_fmt, m_Context->sample_rate, 0, NULL); swr_init(m_converCtx); while (avcodec_receive_frame(m_Context, m_Frame) >= 0) { ret = swr_convert(m_converCtx, &m_pData, m_nSize, (const uint8_t**)m_Frame->data, m_Frame->nb_samples); if (ret > 0) { if (nBufferSize < nSize + ret * m_outBytesPerSample) { fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize); break; } memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample); nSize += ret * m_outBytesPerSample; } } while ((ret = swr_convert(m_converCtx, &m_pData, m_nSize, NULL, 0)) > 0) { if (nBufferSize < nSize + ret * m_outBytesPerSample) { fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize); break; } memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample); nSize += ret * m_outBytesPerSample; } return ; }
注意:CODEC_AAC是我的项目中的aac格式宏,替换成自己的aac/mp3,AV_CODEC_ID_AAC换成相应的枚举即可完成其他格式的解码。
int nPcmSize = CAudioDecoder::transcode_pcm(inputFile, outputFile);
inputFile :需要解码的文件名
outputFile :解码后的文件名
nPcmSize :解码后的文件长度,<=0 失败,>0 成功
创建AAC解码器
CAudioDecoder *decoder = new CAudioDecoder();
decoder->DecodeOpen(CODEC_AAC);
解码
int OutSize = decoder->DecodeFrame(pFrame, nFrameSize, pOutBuf, nBufferSize);
销毁解码器
delete decoder;
decoder = NULL;
解码数据,可以传输m.n帧,m>=0 n>=0
pFrame 输入,待解码的数据
nFrameSize 输入,待解码的数据长度
pOutBuf 输入输出,解码后数据,缓冲区由调用者分配
nBufferSize 输入,传入的缓冲区pOutBuf长度
return 返回解码后缓冲区已使用的数据长度,如果传入数据小于1帧,本地调用会返回0,直到下一次输入的数据达到1帧
注意:对于一组连续的音频数据,不要重复调用创建和销毁解码器。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。