音视频学习二: android平台下ffmpeg编解码简记-编解码

               编解码

1.主要函数

  • avcodec_register_all(); 注册所有解码器
  • AVCodec *avcodec_find_decoder(enum AVCodecID id); 软件码:根据解码器id找到解码器
  • AVCodec *avcodec_find_decoder_by_name(const char *name); 硬解码:根据解码器名称找到解码器
  • AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) 申请AVCodecContext空间.需要传递一个编码器,也可以不传,但不会包含编码器
  • void avcodec_free_context(AVCodecContext **avctx); 清理释放AVCodecContext空间
  • int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 打开视频解码器。如果在 avcode_alloc_context3 的时候没有传解码器,则在此需要进行传递,后面的options是可选参数。参见:libavcodec/options_table.h。
  • avcodec_parameters_to_context(codec,p); 该函数用于将流里面的参数,也就是AVStream里面的参数直接复制到AVCodecContext的上下文中。

2.结构体

  • AVCodecContext 

位于avcodec.h,关键的变量(这里只考虑解码)

enum AVMediaType codec_type:编解码器的类型(视频,音频...)

struct AVCodec  *codec:采用的解码器AVCodec(H.264,MPEG2...)

int bit_rate:平均比特率

uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)

AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)

int width, height:如果是视频的话,代表宽和高

int refs:运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)

int sample_rate:采样率(音频)

int channels:声道数(音频)

enum AVSampleFormat sample_fmt:采样格式

int profile:型(H.264里面就有,其他编码标准应该也有)

int level:级(和profile差不太多)

在这里需要注意:AVCodecContext中很多的参数是编码的时候使用的,而不是解码的时候使用的。

其实这些参数都比较容易理解。就不多费篇幅了。在这里看一下以下几个参数:

①codec_type

编解码器类型有以下几种:

enum AVMediaType {  
    AVMEDIA_TYPE_UNKNOWN = -1,  ///< Usually treated as AVMEDIA_TYPE_DATA  
    AVMEDIA_TYPE_VIDEO,  
    AVMEDIA_TYPE_AUDIO,  
    AVMEDIA_TYPE_DATA,          ///< Opaque data information usually continuous  
    AVMEDIA_TYPE_SUBTITLE,  
    AVMEDIA_TYPE_ATTACHMENT,    ///< Opaque data information usually sparse  
    AVMEDIA_TYPE_NB  
}; 

②sample_fmt

在FFMPEG中音频采样格式有以下几种:

enum AVSampleFormat {  
    AV_SAMPLE_FMT_NONE = -1,  
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits  
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits  
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits  
    AV_SAMPLE_FMT_FLT,         ///< float  
    AV_SAMPLE_FMT_DBL,         ///< double  
  
    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar  
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar  
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar  
    AV_SAMPLE_FMT_FLTP,        ///< float, planar  
    AV_SAMPLE_FMT_DBLP,        ///< double, planar  
  
    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically  
};  

③profile

在FFMPEG中型有以下几种,可以看出AAC,MPEG2,H.264,VC-1,MPEG4都有型的概念。

#define FF_PROFILE_UNKNOWN -99  
#define FF_PROFILE_RESERVED -100  
  
#define FF_PROFILE_AAC_MAIN 0  
#define FF_PROFILE_AAC_LOW  1  
#define FF_PROFILE_AAC_SSR  2  
#define FF_PROFILE_AAC_LTP  3  
#define FF_PROFILE_AAC_HE   4  
#define FF_PROFILE_AAC_HE_V2 28  
#define FF_PROFILE_AAC_LD   22  
#define FF_PROFILE_AAC_ELD  38  
  
#define FF_PROFILE_DTS         20  
#define FF_PROFILE_DTS_ES      30  
#define FF_PROFILE_DTS_96_24   40  
#define FF_PROFILE_DTS_HD_HRA  50  
#define FF_PROFILE_DTS_HD_MA   60  
  
#define FF_PROFILE_MPEG2_422    0  
#define FF_PROFILE_MPEG2_HIGH   1  
#define FF_PROFILE_MPEG2_SS     2  
#define FF_PROFILE_MPEG2_SNR_SCALABLE  3  
#define FF_PROFILE_MPEG2_MAIN   4  
#define FF_PROFILE_MPEG2_SIMPLE 5  
  
#define FF_PROFILE_H264_CONSTRAINED  (1<<9)  // 8+1; constraint_set1_flag  
#define FF_PROFILE_H264_INTRA        (1<<11) // 8+3; constraint_set3_flag  
  
#define FF_PROFILE_H264_BASELINE             66  
#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)  
#define FF_PROFILE_H264_MAIN                 77  
#define FF_PROFILE_H264_EXTENDED             88  
#define FF_PROFILE_H264_HIGH                 100  
#define FF_PROFILE_H264_HIGH_10              110  
#define FF_PROFILE_H264_HIGH_10_INTRA        (110|FF_PROFILE_H264_INTRA)  
#define FF_PROFILE_H264_HIGH_422             122  
#define FF_PROFILE_H264_HIGH_422_INTRA       (122|FF_PROFILE_H264_INTRA)  
#define FF_PROFILE_H264_HIGH_444             144  
#define FF_PROFILE_H264_HIGH_444_PREDICTIVE  244  
#define FF_PROFILE_H264_HIGH_444_INTRA       (244|FF_PROFILE_H264_INTRA)  
#define FF_PROFILE_H264_CAVLC_444            44  
  
#define FF_PROFILE_VC1_SIMPLE   0  
#define FF_PROFILE_VC1_MAIN     1  
#define FF_PROFILE_VC1_COMPLEX  2  
#define FF_PROFILE_VC1_ADVANCED 3  
  
#define FF_PROFILE_MPEG4_SIMPLE                     0  
#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE            1  
#define FF_PROFILE_MPEG4_CORE                       2  
#define FF_PROFILE_MPEG4_MAIN                       3  
#define FF_PROFILE_MPEG4_N_BIT                      4  
#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE           5  
#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION      6  
#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE     7  
#define FF_PROFILE_MPEG4_HYBRID                     8  
#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME         9  
#define FF_PROFILE_MPEG4_CORE_SCALABLE             10  
#define FF_PROFILE_MPEG4_ADVANCED_CODING           11  
#define FF_PROFILE_MPEG4_ADVANCED_CORE             12  
#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13  
#define FF_PROFILE_MPEG4_SIMPLE_STUDIO             14  
#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE           15  

3.解码流程

①找到解码器并打开 avcodec_open2()

bool FFDecode::Open(XParameter xParameter, XDecodeFormat format) {
    Close();
    if (!xParameter.para) {
        XLOGE("FFDecode Open failed!");
        return false;
    }
    AVCodecParameters *avCodecParameters = xParameter.para;
    //①查找解码器
    AVCodec *avCodec;
    switch (format) {
        case DECODE_HARD:  //硬解码
            avCodec = avcodec_find_decoder_by_name("h264_mediacodec");
            XLOGE("DECODE_HARD format: %d", format);
            break;
        case DECODE_SOFT: // 软解码
            avCodec = avcodec_find_decoder(avCodecParameters->codec_id);
            XLOGE("DECODE_SOFT format: %d", format);
            break;
    }
    //查找失败
    if (!avCodec) {
        XLOGE("avcodec_find_decoder %d failed! %d", avCodecParameters->codec_id, format);
        return false;
    }
    mux.lock();
    //②.创建解码上下文,并复制参数
    avCodecContext = avcodec_alloc_context3(avCodec);
    avcodec_parameters_to_context(avCodecContext, avCodecParameters);
    //开启8个线程
    avCodecContext->thread_count = 8;
    //③打开解码器
    int ret = avcodec_open2(avCodecContext, 0, 0);
    if (ret != 0) {
        XLOGE("avcodec_open2 failed!");
        char errBuf[1024] = {0};
        av_strerror(ret, errBuf, sizeof(errBuf) - 1);
        XLOGE("%s", errBuf);
        mux.unlock();
        return false;
    }
    if (avCodecContext->codec_type == AVMEDIA_TYPE_VIDEO) {
        this->decodeType = DECODE_TYPE_VIDEO;
    } else if (avCodecContext->codec_type == AVMEDIA_TYPE_AUDIO) {
        this->decodeType = DECODE_TYPE_AUDIO;
    } else {
        this->decodeType = DECODE_TYPE_UNKNOWN;
    }
    isOpen = true;
    mux.unlock();
    XLOGE("FFDecode::Open success! %d", avCodecParameters->codec_id);
    return true;
}

② 对从解封装发送的过来的文件数据进行解码 avcodec_send_packet();  avcodec_receive_frame();

void IDecode::Main() {
    while (!isExit) {
        if (IsPausing()) {
            XSleep(2);
            continue;
        }
        packetMutex.lock();
        //判断音视频同步 根据音频播放位置synPts控制视频解码位置
        if (decodeType == DECODE_TYPE_VIDEO) {
            // 音频播放的位置 小于当前视频解码位置,视频解码暂停
            if (synPts > 0 && synPts < pts) {
                packetMutex.unlock();
                XSleep(1);
                continue;
            }
        }
        if (packs.empty()) {
            packetMutex.unlock();
            XSleep(1);
            continue;
        }
        XData packet = packs.front();
        packs.pop_front();
        if (this->SendPacket(packet)) { //发送数据到解码线程成功

            while (!isExit) { // SendPacket发送一次,ReceivedFrame多次读取
                //获取解码数据
                XData frame = this->ReceivedFrame();
                if (!frame.data) { //数据读取不到了,就退出循环
                    break;
                }
                frameCount++;
                pts = frame.pts;
//                XLOGE("======>> %d", frame.size);
                //发送解码结果(发送给编码显示对象)
                observable->Notify(frame);
            }
        }
        packet.Drop();
        packetMutex.unlock();
    }
}

到这里已经对解封装发送过来的数据进行了解码处理,需要将解码的数据发送给渲染窗口,进行处理显示.

视频:使用opengl ES 直接绘制yuv (EGL opengles shader glsl)

音频:使用opensl ES 进行重采样播放

猜你喜欢

转载自blog.csdn.net/qq_33635618/article/details/91437036