目录

视频硬解码CPU使用率高

问题现象

在播放同一个m3u8视频时,分别使用AVCodec硬件视频解码器和ffmpeg软件解码器播放,发现使用硬件解码器时的CPU使用率要比软件的高很多。在进行视频解码的过程中,硬解码过程的CPU使用率预期比软解码过程要低。在使用ijkplayer进行测试时,CPU的使用率出现相反的情况。

CPU测试结果如下图:

硬解码:

ffmpeg软解码:

从CPU测试结果图来看硬件视频解码CPU使用率维持在7%到8%左右,使用ffmpeg软件解码播放时平均维持在3%左右。这与硬解码和软解码的CPU占用不符合。硬解码调用设备相关硬件进行解码,软解码调用软件(调用CPU)进行解码,预期结果应当为硬解码占用更少的CPU。

背景知识

问题定位

解码代码中AVCodecOnNeedInputBuffer的代码段:

void AVCodecOnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
{
    OHOSVDecoder *decoder = static_cast<OHOSVDecoder *>(userData);
    if (decoder == nullptr) {
        return;
    }
    decoder->OnNeedInputBuffer(codec, index, buffer);
}
void OHOSVDecoder::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer)
{
    if (codec == nullptr) {
        return;
    }
    std::shared_ptr<Frame> frame;
    {
        std::unique_lock<std::mutex> auto_lock(_frame_mutex);
        if (_input_frames.size() > 0) {
            frame = _input_frames.front();
            _input_frames.pop_front();
        }
    }
    if (frame == nullptr) {
        OH_AVCodecBufferAttr info;
        info.pts = 0;
        info.size = 0;
        info.offset = 0;
        info.flags = AVCODEC_BUFFER_FLAGS_DISCARD;
        OH_AVBuffer_SetBufferAttr(buffer, &info);
        OH_VideoDecoder_PushInputBuffer(codec, index);
        return;
    }
    std::shared_ptr<VideoFrame> video_frame = std::dynamic_pointer_cast<VideoFrame>(frame);
    OH_AVCodecBufferAttr info;
    info.pts = video_frame->pts();
    info.size = frame->Size();
    info.offset = 0;
    info.flags = AVCODEC_BUFFER_FLAGS_NONE;
    int32_t ret = OH_AVBuffer_SetBufferAttr(buffer, &info);
    if (ret != AV_ERR_OK) {
        return;
    }
    uint8_t *data = OH_AVBuffer_GetAddr(buffer);
    if (data == nullptr) {
        return;
    }
    memcpy(data, frame->Data(), frame->Size());
    ret = OH_VideoDecoder_PushInputBuffer(codec, index);
    if (ret != AV_ERR_OK) {
        return;
    }
}

分析结论

可以看见OnNeedInputBuffer函数实现中由自定义类型Frame去存储相关数据,在后续读取其中的信息到结构体OH_AVBuffer和OH_AVCodecBufferAttr中以调用函数PushInputBuffer往视频解码队列输入数据,等待解码器解码,但在最后使用OH_AVBuffer_GetAddr获取的是AVBuffer的地址,使用memcpy拷贝的是地址而非实际数据,且在使用PushInputBuffer填充数据时只传入了解码器和索引,Buffer的数据没有被传递。

在视频Buffer和音频Buffer都为nullptr时,在视频源有数据不断传入时,解码器会保持空载。

当传入解码队列的音频Buffer不为空,视频Buffer为nullptr时,解码器运行,只有音频输出。

修改建议

确保使用PushInputBuffer传入给解码队列的Buffer不为空,详细过程参考视频解码开发指导。