直接用libav全家桶不好吗
好啊。有现成的东西用当然好。最早就是libav全家桶,avformat读数据,解包后h264数据直接丢解码器解码。
用了好长时间都没出什么大问题。
不过用avcodec封装的cuvid解码,在解码器设置参数一直都不生效。实际使用环境会有多张显卡,是需要配置具体解码使用哪些卡的。
所以最后还是要直接用cuvid的api去解码。
AVC1和H264
直接调用cuvid解码时发现,有的视频是能够正常解出图像的,而有的一直输入数据,解码器却没有输出。
后来才知道,原来H264还有两种格式。一种是AVC1另一种是H264。
区别在于,AVC1不带0x00000001或0x000001起始码,而H264则带了这些信息,所以H264形式的数据包直接丢解码器是可以解码的。
对于AVC1来说,还需要再处理一下才能解码。
使用bit stream filter可以方便的处理这种情况。
AVBitStreamFilter *pFilter = av_bsf_get_by_name("h264_mp4toannexb");
// 对于HEVC就是hevc_mp4toannexb
av_bsf_alloc(pFilter, pFilterCtx);
avcodec_parameters_copy(pFilterCtx->par_in, pFormatCtx->streams[nId]->codecpar);
av_bsf_init(pFilterCtx);
// 使用时
av_bsf_send_packet(pFilterCtx, &pkt);
av_bsf_receive_packet(pFilterCtx, &pktAnnexb);
NV12
网上看到好多关于NV12格式的文章都是错的,一开始对着那些文章撸结果一直不对。
现在总算是搞明白了。
为了优化,之前是把解码器解出的数据丢libyuv做yuv转rgb。只需要取data[0]做Y数据地址,
data[1]做uv数据地址。而长度直接取linesize[0]和linesize[1]就行。
现在另外解码,这个大小就要自己计算了。
// NV12: YYYYYYYYUVUV
//
// Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 <- 对Y数据一行 width+padding (pitch) 个数据
// Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
// Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
// Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32 <- Y结尾,共height行,接下来是UV数据
// U1 V1 U2 V2 U3 V3 U4 V4 <- 一行U和V数据个数都是Y的一半,UV加起来和Y一样
// U5 V5 U6 V6 U7 V7 U8 V8
//
int nPitch = pDecoder->GetDeviceFramePitch(); // 这里GPU解码的Pitch和with一样
uint8_t *pY = ppFrame[i]; // 起始地址就是Y地址
uint8_t pUV = pY + nPitch*height; // Y是完整一个像素对应一个的,一个大小1byte
size_t linesize1 = nPitch;
size_t linesize2 = nPitch; // U和V每行都有Y一半个,加起来是1个Y个。
libyuv::NV12ToRGB24(pY, linesize1, pUV, linesize2,
pBufferBGR, width*3, width, height);
@