编解码
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 进行重采样播放