cuvid解码的几个注意点

直接用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);

@

Show Comments