视频解码

功能约束

视频解码功能的功能约束参见下表。

表 视频解码功能约束

约束项

属性

参数

输入

分辨率

  • 最大:4096×4096

  • 最小:16×16

  • 宽高2对齐

格式

  • H.264 Baseline/Main/High/High 10 Profile

  • H.265 Main/Main 10 Profile

缩放

支持2倍缩小和4倍缩小,缩小后的宽高需要2对齐

输入内存

输入内存是Client侧内存

输入方式

  • 只支持按帧输入的码流进行视频解码

  • 只支持逐行扫描方式编码出来的码流解码

输出

输出格式

  • YUV420SP NV12

  • YUV420SP NV21

  • YUV420P YU12

  • NV12 10bit

分辨率

  • 最大:4096×4096

  • 最小:16×16

  • 宽高2对齐

输出内存

只能调用lynMalloc和lynFree进行视频解码输出的内存申请和释放

视频解码

用户可以从LynSDK提供的示例代码中查看完整样例,在示例代码中,调用各接口后都添加了异常判断和处理,以下是关键步骤代码示例,仅供参考,不可以直接拷贝编译。

// 预定义解码器输出buffer管理类
class DecRecvBufManager {
public:
    int Init(int size, int num) {
        void *newBuf;
        int ret;
        for (int i = 0; i < num; i++) {
            ret = lynMalloc(&newBuf, size);
            m_bufAll.push_back(newBuf);
            m_freeBuf.push(newBuf);
        }
    }
    void *GetNewBuf(uint32_t timeout = 300 * g_channelNum) {
        std::unique_lock<std::mutex> lock(m_queueMux);
        if (m_cvOutbufNum.wait_for(lock, std::chrono::seconds(timeout),
        [this]{return this->m_freeBuf.size() > 0;}) != true) {
            return nullptr;
        }
        void *buf = m_freeBuf.front();
        m_freeBuf.pop();
        return buf;
    }
    void FreeBuf(void *buf) {
        std::unique_lock<std::mutex> lock(m_queueMux);
        m_freeBuf.push(buf);
        lock.unlock();
        m_cvOutbufNum.notify_one();
    }
    void Destory() {
        for (auto it : m_bufAll) {
            lynFree(it);
        }
        m_bufAll.clear();
    }
    ~DecRecvBufManager() {
        Destory();
    }
private:
    std::queue<void *> m_freeBuf;
    std::vector<void *> m_bufAll;
    std::mutex m_queueMux;
    std::condition_variable m_cvOutbufNum;
};

// 解码器输出信息
struct VdecBufInfo {
    int id;
    uint8_t *data;
    uint32_t size;
    uint32_t num;
    DecRecvBufManager *manager;
};

// 解码器工作线程函数
int DecodeUrl(const char *url)
{
    lynError_t ret = 0;
    //runtime资源申请
    lynContext_t context = nullptr;
    ret = lynCreateContext(&context, 0);
    //解封装初始化
    lynDemuxHandle_t demuxHdl;
    ret = lynDemuxOpen(&demuxHdl, url, nullptr);
    lynCodecPara_t codecPara;
    //获取解码参数
    ret = lynDemuxGetCodecPara(demuxHdl, &codecPara);
    int bufSize = codecPara.width * codecPara.height * 3 / 2;

    lynPacket_t pkt;
    int num = 0;
    int totalSize = 0;

    //打开解码器,配置解码参数
    lynVdecAttr_t attr;
    attr.codecId = codecPara.codecId;
    attr.outputFmt = LYN_PIX_FMT_YUV420P;
    lynVdecHandle_t vdecHdl;
    ret = lynVdecOpen(&vdecHdl, &attr);
    int vdecId = ((VdecHandle *)vdecHdl)->id;

    //为视频解码创建发送Stream和接收Stream
    lynStream_t sendStream = nullptr;
    ret = lynCreateStream(&sendStream);
    lynStream_t recvStream = nullptr;
    ret = lynCreateStream(&recvStream);

    //初始化解码器输出buffer
    DecRecvBufManager recvBufManager;
    recvBufManager.Init(bufSize, 5);

    //循环送包过程
    while (1) {
        ret = lynDemuxReadPacket(demuxHdl, &pkt);
        if (ret == EAGAIN) {
            continue;
        } else if (ret != 0) {
            //解码完成,解封装去初始化,并发送eos标识
            lynDemuxClose(demuxHdl);

            /* set eos */
            pkt.eos = true;
            lynVdecSendPacketAsync(sendStream, vdecHdl, &pkt);
            break;
        }

        num++;
        totalSize += pkt.size;

        // 在发送Stream中发送待解码数据
        lynVdecSendPacketAsync(sendStream, vdecHdl, &pkt);
        //同步等待并释放待解码数据内存(可选)
        lynSynchronizeStream(sendStream);
        lynDemuxFreePacket(&pkt);

        //从解码器输出池中获取新的输出buffer并在接收Stream中接收解码之后的图像帧数据
        lynFrame_t frame = {0};
        frame.data = (uint8_t *)recvBufManager.GetNewBuf();
        frame.size = bufSize;
        lynVdecRecvFrameAsync(recvStream, vdecHdl, &frame);

        VdecBufInfo *outBuf = (VdecBufInfo *)malloc(sizeof(VdecBufInfo));
        outBuf->id = vdecId;
        outBuf->data = frame.data;
        outBuf->size = frame.size;
        outBuf->num = num;
        outBuf->manager = &recvBufManager;
        //添加接收Stream异步回调函数,用户可自行选择在回调函数中进行结果保存、内存释放等逻辑
        lynStreamAddAsyncCallback(recvStream, vdec_buffer_callback, outBuf);
    }
    //同步等待直至所有数据解码完成
    lynSynchronizeStream(recvStream);
    //关闭解码器
    lynVdecClose(vdecHdl);
    //runtime资源销毁
    lynDestroyStream(sendStream);
    lynDestroyStream(recvStream);

    lynDestroyContext(context);
    return 0;
}

//主函数调用流程
std::vector<std::thread> decThreads;
for (int i = 0; i < g_channelNum; i++) {
    decThreads.push_back(std::thread(DecodeUrlThread, g_url.c_str()));
}
for (auto &it : decThreads) {
    it.join();
}