FFmpeg - 音频解码

来源:互联网 发布:淘宝模特拍摄动作 编辑:程序博客网 时间:2024/05/21 22:45

因公司项目需求。简单学习了FFMPEG。在这里做个记录。


音频解码的流程如下:




先介绍一下AVFormatContext里面的几个成员:

  /**
     * Maximum size of the data read from input for determining
     * the input container format.
     * Demuxing only, set by the caller before avformat_open_input().
     */
    int64_t probesize;

 /**
     * Maximum duration (in AV_TIME_BASE units) of the data read
     * from input in avformat_find_stream_info().
     * Demuxing only, set by the caller before avformat_find_stream_info().
     * Can be set to 0 to let avformat choose using a heuristic.
     */
    int64_t max_analyze_duration;


 /**
     * Custom interrupt callbacks for the I/O layer.
     *
     * demuxing: set by the user before avformat_open_input().
     * muxing: set by the user before avformat_write_header()
     * (mainly useful for AVFMT_NOFILE formats). The callback
     * should also be passed to avio_open2() if it's used to
     * open the file.
     */
    AVIOInterruptCB interrupt_callback;


/**
     * I/O context.
     *
     * - demuxing: either set by the user before avformat_open_input() (then
     *             the user must close it manually) or set by avformat_open_input().
     * - muxing: set by the user before avformat_write_header(). The caller must
     *           take care of closing / freeing the IO context.
     *
     * Do NOT set this field if AVFMT_NOFILE flag is set in
     * iformat/oformat.flags. In such a case, the (de)muxer will handle
     * I/O in some other way and this field will be NULL.
     */
    AVIOContext *pb;


输入为内存的时候,我们需要设置一个AVIOContext。

而直接用于拉流时,可以直接调用avformat_open_input。第二个参数传url。


ffmpeg也提供了停止拉流的方法。设置AVFormatContext的interrupt_callback。自己写一个回调。当它返回1时,拉流停止。


这里还有一个延迟的问题。ffmpeg解码时,会先猜测解码的数据格式。通过设置AVFormatContext的probeSize和max_analyze_duration。可以明显的降低延迟。



直接上代码:

#ifndef _IDECODE_H_#define _IDECODE_H_#include "util.h"extern "C" {#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>};//中断int decode_interrupt_cb(void *obj);class IDecode{public:int init();int run();//数据输出回调void setWritePacketFuncCB(write_packetFunc func);//回调输出对象void setOutputObj(void *outObj);//设置猜测缓存,ff默认为32KB,太小可能导致无法解码void setProbeSize(int64_t probeSize);//设置猜测时间,ff默认为5s,太小可能导致无法解码void setMaxAnalyzeDuration(int64_t maxAnalyzeDuration);//暂停void pause();//继续解码void play();//停止,用于销毁之前void stop();//用于获取编码器信息void setGetSampleRateCB(getParamsCallBack cb);void setGetBitRateCB(getParamsCallBack cb);void setGetSampleFmtCB(getParamsCallBack cb);void setGetCodecidCB(getParamsCallBack cb);virtual ~IDecode();protected:virtual int constructAVIO() = 0;int constructCodec();virtual void release() = 0;bool isStop;bool isPause;void* outPutObj;AVFormatContext *fmt_ctx;AVCodecContext *pInCodecCtx;int64_t probeSize;int64_t maxAnalyzeDuration;getParamsCallBack getSampleRateCB;getParamsCallBack getBitRateCB;getParamsCallBack getSampleFmtCB;getParamsCallBack getCodecidCB;write_packetFunc writePacket;explicit IDecode();int writeFrame(AVFrame *decoded_frame, AVPacket &pkt, AVCodecContext *pInCodecCtx);public://用于中断bool getStop()const;};#endif // _IDECODE_H_


#ifndef _IDECODE_MEM_H_#define _IDECODE_MEM_H_#include "IDecode.h"class IDecode_mem : public IDecode{const int AV_IO_BUFF_SIZE = 1024;public:typedef int(*read_packetFunc)(void *opaque, uint8_t *buf, int buf_size);public:explicit IDecode_mem();void setInputObj(void* obj);void setReadPacketFuncCallback(read_packetFunc func);~IDecode_mem();private:void release();int constructAVIO() override;AVIOContext *avio_ctx;read_packetFunc readPacket;void* inputObj;};#endif // _IDECODE_MEM_H_


#ifndef _IDECODE_URL_H_#define _IDECODE_URL_H_#include "IDecode.h"#include <string>class IDecode_url : public IDecode{public:explicit IDecode_url();//设置拉流URLvoid setUrl(const std::string& url);~IDecode_url();private:int constructAVIO() override;void release() override;std::string url;};#endif // !_IDECODE_MEM_H_

#include "IDecode.h"#include "error.h"IDecode::IDecode():writePacket(nullptr), outPutObj(nullptr),isStop(false), getSampleRateCB(nullptr),getBitRateCB(nullptr), getSampleFmtCB(nullptr),getCodecidCB(nullptr), fmt_ctx(nullptr),pInCodecCtx(nullptr), isPause(false){av_register_all();}IDecode::~IDecode(){}void IDecode::setWritePacketFuncCB(write_packetFunc func){this->writePacket = func;}void IDecode::setOutputObj(void *outObj){this->outPutObj = outObj;}void IDecode::setGetSampleRateCB(getParamsCallBack cb){this->getSampleFmtCB = cb;}void IDecode::setGetBitRateCB(getParamsCallBack cb){this->getBitRateCB = cb;}void IDecode::setGetSampleFmtCB(getParamsCallBack cb){this->getSampleFmtCB = cb;}void IDecode::setGetCodecidCB(getParamsCallBack cb){this->getCodecidCB = cb;}void IDecode::stop(){this->isStop = true;}bool IDecode::getStop()const{return isStop;}void IDecode::pause(){isPause = true;}void IDecode::play(){isPause = false;}void IDecode::setProbeSize(int64_t probeSize){this->probeSize = probeSize;}void IDecode::setMaxAnalyzeDuration(int64_t maxAnalyzeDuration){this->maxAnalyzeDuration = maxAnalyzeDuration;}int IDecode::run(){Error error;AVPacket pkt;int got_frame = 0;AVFrame *decoded_frame = nullptr;while (av_read_frame(fmt_ctx, &pkt) >= 0){if (!isPause) {av_init_packet(&pkt);error = (Error)writeFrame(decoded_frame, pkt, pInCodecCtx);if (Error::ERROR_NONE != error){av_packet_unref(&pkt);if (decoded_frame)av_frame_free(&decoded_frame);release();return error;}av_packet_unref(&pkt);}}if (decoded_frame)av_frame_free(&decoded_frame);avformat_close_input(&fmt_ctx);return Error::ERROR_NONE;}int IDecode::writeFrame(AVFrame *decoded_frame, AVPacket &pkt, AVCodecContext *pInCodecCtx){int got_frame = 0;int len = 0;while (pkt.size > 0){if (!decoded_frame){if (!(decoded_frame = av_frame_alloc())){decoded_frame = nullptr;return Error::ERROR_FRAME_ALLOCATE;}}int got_frame = 0;len = avcodec_decode_audio4(pInCodecCtx, decoded_frame, &got_frame, &pkt);if (len < 0) {return Error::ERROR_DECODING;}if (got_frame){int data_size = av_get_bytes_per_sample(pInCodecCtx->sample_fmt);if (data_size < 0){return Error::ERROR_CALCULATE_DATA_SIZE;}int frame_buf_size = decoded_frame->nb_samples * data_size * pInCodecCtx->channels;uint8_t* frame_buf = new uint8_t[frame_buf_size];for (int i = 0; i < decoded_frame->nb_samples; i++){for (int ch = 0; ch < pInCodecCtx->channels; ch++){memcpy(frame_buf + i*data_size*(ch + 1), decoded_frame->data[ch] + data_size*i, data_size);}}if (writePacket) {writePacket(outPutObj, frame_buf, frame_buf_size);}delete []frame_buf;}pkt.size -= len;pkt.data += len;pkt.dts = pkt.pts = AV_NOPTS_VALUE;}return Error::ERROR_NONE;}int IDecode::constructCodec(){int ret = 0;ret = avformat_find_stream_info(fmt_ctx, nullptr);if (ret < 0){release();return Error::ERROR_FORMAT_FIND_STREAM_INFO;}int audio_index = -1;for (size_t i = 0; i < fmt_ctx->nb_streams; i++){if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audio_index = i;}}if (-1 == audio_index){release();return Error::ERROR_NONE_AUDIO;}AVCodec *codec = avcodec_find_decoder(fmt_ctx->streams[audio_index]->codecpar->codec_id);if (!codec){release();return Error::ERROR_NO_CODEC_SUPPORT;}pInCodecCtx = fmt_ctx->streams[audio_index]->codec;if (getSampleRateCB)getSampleRateCB(pInCodecCtx->sample_rate);if (getBitRateCB)getBitRateCB(pInCodecCtx->bit_rate);if (getSampleFmtCB)getSampleFmtCB(pInCodecCtx->sample_fmt);if (getCodecidCB)getCodecidCB(pInCodecCtx->codec_id);if ((ret = avcodec_open2(pInCodecCtx, codec, nullptr)) < 0){release();return Error::ERROR_AVCODEC_OPEN2;}return Error::ERROR_NONE;}int IDecode::init(){Error error;error = (Error)constructAVIO();if (Error::ERROR_NONE != error){return error;}error = (Error)constructCodec();return error;}int decode_interrupt_cb(void *obj){//线程不安全if (obj != nullptr){IDecode *decode = (IDecode *)obj;if (decode->getStop()){return 1;}}return 0;}


#include "IDecode_mem.h"#include "error.h"IDecode_mem::IDecode_mem() :inputObj(nullptr), readPacket(nullptr), avio_ctx(nullptr){}IDecode_mem::~IDecode_mem(){}void IDecode_mem::setReadPacketFuncCallback(read_packetFunc func){this->readPacket = func;}int IDecode_mem::constructAVIO(){uint8_t *avio_ctx_buffer = nullptr;int ret = 0;avio_ctx_buffer = (uint8_t*)av_malloc(AV_IO_BUFF_SIZE);if (!avio_ctx_buffer){return Error::ERROR_ALLOC;}avio_ctx = avio_alloc_context(avio_ctx_buffer,AV_IO_BUFF_SIZE,0,inputObj, readPacket,nullptr,nullptr);if (!avio_ctx){avio_ctx = nullptr;return Error::ERROR_AVIO_ALLOC_CONTEXT;}if (!(fmt_ctx = avformat_alloc_context())){release();return Error::ERROR_ALLOC_CONTEXT;}const AVIOInterruptCB int_cb = { decode_interrupt_cb, this };if (-1 != probeSize)fmt_ctx->probesize = probeSize;if (-1 != maxAnalyzeDuration)fmt_ctx->max_analyze_duration = maxAnalyzeDuration;fmt_ctx->interrupt_callback = int_cb;fmt_ctx->pb = avio_ctx;ret = avformat_open_input(&fmt_ctx, nullptr, nullptr, nullptr);if (ret < 0) {release();return Error::ERROR_FORMAT_OPEN_INPUT;}return Error::ERROR_NONE;}void IDecode_mem::setInputObj(void* obj){this->inputObj = obj;}void IDecode_mem::release(){if (avio_ctx){av_freep(&avio_ctx->buffer);av_freep(&avio_ctx);}avformat_close_input(&fmt_ctx);}


#include "IDecode_url.h"#include "error.h"IDecode_url::IDecode_url() : url(""){avformat_network_init();}IDecode_url::~IDecode_url(){}void IDecode_url::setUrl(const std::string& url){this->url = url;}int IDecode_url::constructAVIO(){int ret = 0;if (!(fmt_ctx = avformat_alloc_context())){return Error::ERROR_ALLOC_CONTEXT;}const AVIOInterruptCB int_cb = { decode_interrupt_cb, this };if (-1 != probeSize)fmt_ctx->probesize = probeSize;if (-1 != maxAnalyzeDuration)fmt_ctx->max_analyze_duration = maxAnalyzeDuration;fmt_ctx->interrupt_callback = int_cb;ret = avformat_open_input(&fmt_ctx, url.c_str(), nullptr, nullptr);if (ret < 0){release();return Error::ERROR_FORMAT_OPEN_INPUT;}return Error::ERROR_NONE;}void IDecode_url::release(){if(fmt_ctx)avformat_close_input(&fmt_ctx);}


 使用方法:

FILE *f2 = fopen(outFileName, "wb");int writeData(void*p, uint8_t* data, int size) {cout << "asdasd" << endl;fwrite(data, 1, size, f2);return 1;}void thread_run(IDecode_url *decode,int &ret){decode->init();ret = decode->run();}void testDecoder_url(){int ret = -1;IDecode_url *decode = new IDecode_url;decode->setUrl("http://XXXXX:XXXX/1_1.mp3");decode->setOutputObj(nullptr);decode->setProbeSize(1024);decode->setMaxAnalyzeDuration(2000);decode->setWritePacketFuncCB(writeData);thread _thread(thread_run, decode, std::ref(ret));_thread.detach();_sleep(3000);decode->stop();_sleep(10000);delete decode;decode = nullptr;fclose(f2);}




1 0
原创粉丝点击