.. _video_encoding: 视频编码 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. _functional_constraints_of_video_encoding: 功能约束 """"""""""""""""""""""""""""""""""""""""""""""""""""" 视频编码的功能约束详见下表。 .. _table_13_video_encoding_constraints: 表 视频编码功能约束 """"""""""""""""""""""""""""""""""""""""""""""""""""" +----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | 约束项 | 属性 | 参数 | +==========+===========+========================================================================================================================================+ | 输入 | 分辨率 | - 最大:3840×2160 | | | | - 最小:114×114 | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | 码率控制 | 支持CBR,VBR与FixQP三种 | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | 最大码率 | 400M | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | GopType | 目前仅支持GopType为Low Delay的B帧编码,且bFramesNum最大为5 | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | 格式 | 支持NV12 8bit与NV12 10bit,对应的bitDepth需设置为8与10 | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | 输入内存 | 只能调用lynMalloc和lynFree进行视频编码输入的内存申请和释放 | +----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | 输出 | 输出格式 | - H.264 Baseline/Main/High/High 10 Profile,分辨率,level与frameRate设定需遵守H.264标准。 | | | | - H.265 Main/Main 10 Profile,分辨率,level与frameRate设定需遵守H.265标准。 | | | | - level设定为-1时为auto模式,该模式下,编码器会根据用户输入图像分辨率与帧率,按照H.264或H.265标准计算得到可支持的最小level来进行编码; | | | | - H.264 Baseline profile 不支持ecm的CABAC模式; | | | | - 当level小于4.0时,H.265不支持High Tier。 | + +-----------+----------------------------------------------------------------------------------------------------------------------------------------+ | | 输出内存 | 只能调用lynMalloc和lynFree进行视频编码输出的内存申请和释放 | +----------+-----------+----------------------------------------------------------------------------------------------------------------------------------------+ .. _video_encoding_process: 视频编码 """"""""""""""""""""""""""""""""""""""""""""""""""""" 用户可以从LynSDK提供的示例代码中查看完整样例,在示例代码中,调用各接口后都添加了异常判断和处理,以下是关键步骤代码示例,仅供参考,不可以直接拷贝编译。 .. code-block:: c++ lynCodecBuf_t GetInputFromFile(std::string srcFileName) { lynCodecBuf_t buf = {0}; FILE *fp = fopen(srcFileName.c_str(), "r"); if (fp) { fseek(fp, 0, SEEK_END); buf.size = (int)ftell(fp); buf.data = (uint8_t *)malloc(buf.size); fseek(fp, 0, SEEK_SET); fread(buf.data, 1, buf.size, fp); fclose(fp); } else { LOG(ERROR) << "Open file failed, path: " << srcFileName; } return buf; } static vector resourceList; void ReadAllResource(vector &resource) { static once_flag flag; std::call_once(flag, [&] { for (size_t i = 0; i < 10; i++) { stringstream ss; ss << "../source/encode_yuv_src/dump_1_00" << setw(2) << setfill('0') << i + 1 << ".yuv"; resource.push_back(GetInputFromFile(ss.str())); } }) } // 准备输入frame void FillInputFrame(lynFrame_t *frame, size_t i, lynStream_t stream) { size_t index = i % 10; if (lynMemcpyAsync(stream, frame->data, resourceList[index].data, resourceList[index].size, ClientToServer) != 0) { LOG(ERROR) << "lynMemcpyAsync error!!!!!"; } } template class Queue { public: Queue(uint32_t bufNum, int bufSize) { for (size_t i = 0; i < bufNum; i++) { // 准备输入frame T *frame = new T; void *buf = nullptr; assert(lynMalloc(&buf, bufSize) == 0); frame->data = (uint8_t *)buf; frame->size = bufSize; frame->eos = false; m_map[frame] = false; } it = m_map.begin(); } ~Queue() { for (auto it = m_map.begin(); it != m_map.end(); ++it) { lynFree(it->first->data); delete(it->first); } m_map.clear(); } void Push(void *x) { std::lock_guard locker(m_mutex); for (auto it = m_map.begin(); it != m_map.end(); ++it) { if ((void*)it->first->data == x) { LOG(INFO) << "Restore " << it->first; it->second = false; break; } } } T *Pop() { loop: { std::lock_guard locker(m_mutex); for (; it != m_map.end(); ++it) { if (it->second == false) { LOG(INFO) << "Pop " << it->first; it->second = true; return it->first; } } } if (it == m_map.end()) { it = m_map.begin(); goto loop; } return nullptr; } private: std::unordered_map m_map; typename std::unordered_map::iterator it; //游标 std::mutex m_mutex; } typedef struct inData { std::shared_ptr> pQueue; lynFrame_t *frame; } inData; lynError_t SendFrameCallback(void *userData) { inData *pData = (inData *)userData; pData->pQueue->Push(pData->frame->data); delete pData; return 0; } typedef struct rcvData { std::shared_ptr> pQueue; lynPacket_t *packet; lynCodecBuf_t *localBuf; std::string dstFile; //输出目标文件 } rcvData; lynError_t WriteRemotePacketToFile(const lynPacket_t *packet, lynCodecBuf_t *localBuf, const std::string &fileName) { uint32_t data_size = 0; lynError_t ret = lynEncGetRemotePacketValidSize(packet, &data_size); if (ret != 0) { LOG(ERROR) << "lynEncGetRemotePacketValidSize error:" << ret; return ret; } ret = lynMemcpy(localBuf->data, packet->data, data_size, ServerToClient); if (ret != 0) { LOG(ERROR) << "lynMemcpy data_size error:" << ret; return ret; } ofstream dump_file; dump_file.open(fileName, ios::binary | ios::app); dump_file.write((char *)localBuf->data, data_size); dump_file.close(); return 0; } lynError_t RecvPacketCallback(void *userData) { rcvData *pData = (rcvData *)userData; if (WriteRemotePacketToFile(pData->packet, pData->localBuf, pData->dstFile) != 0) { LOG(ERROR) << "WriteRemotePacketToFile failed."; return -1; } if (pData->pQueue) { pData->pQueue->Push(pData->packet->data); } delete pData; return 0; } int frameNum = 10; //编码帧数 uint32_t width = 1920; uint32_t height = 1080; uint8_t bNums = 0; uint8_t pNums = 0; uint8_t codecType = LYN_CODEC_ID_H264; uint8_t inputFormat = LYN_PIX_FMT_NV12; uint8_t bitDepth = 8; void MultiChannelThreadFunc(std::string outputFileName) { //删除已有文件 FileManager::deleteFile(outputFileName); lynContext_t context = nullptr; EXPECT_EQ(lynCreateContext(&context, 0), 0); EXPECT_NE(context, nullptr); lynVencAttr_t attr; attr.codecType = LYN_CODEC_ID_H264; lynVencSetDefaultParams(&attr); attr.width = width; attr.height = height; attr.bitdepth = bitDepth; attr.bframesNum = bNums; attr.pframesNum = pNums; attr.codecType = (lynCodecId_t)codecType; attr.inputFormat = (lynPixelFormat_t)inputFormat; int bufSize = attr.width * attr.height * 3 / 2; //打开编码器 lynVencHandle_t vencHdl; EXPECT_EQ(lynVencOpen(&vencHdl, &attr), 0); EXPECT_NE(vencHdl, nullptr); //创建发送流 lynStream_t sendStream = nullptr; EXPECT_EQ(lynCreateStream(&sendStream), 0); EXPECT_NE(sendStream, nullptr); //创建接收流 lynStream_t recvStream = nullptr; EXPECT_EQ(lynCreateStream(&recvStream), 0); EXPECT_NE(recvStream, nullptr); std::shared_ptr> pQueue = std::make_shared>(inBufNum, bufSize); std::shared_ptr> pOutQueue = std::make_shared>(outBufNum, bufSize); lynFrame_t *frame = nullptr; lynPacket_t *packet = nullptr; lynCodecBuf_t *outbuf = new lynCodecBuf_t; outbuf->data = (uint8_t *)malloc(bufSize); outbuf->size = bufSize; packet = pOutQueue->Pop(); EXPECT_EQ(lynVencGetParamsSetAsync(recvStream, vencHdl, packet), 0); rcvData *params_data = new rcvData; params_data->pQueue = pOutQueue; params_data->packet = packet; params_data->localBuf = outbuf; params_data->dstFile = outputFileName; EXPECT_EQ(lynStreamAddAsyncCallback(recvStream, RecvPacketCallback, params_data), 0); for (size_t i = 0; i <= frameNum; i++) { frame = pQueue->Pop(); if (i == frameNum) { frame->eos = true; //尾帧标记 } FillInputFrame(frame, i, sendStream); EXPECT_EQ(lynVencSendFrameAsync(sendStream, vencHdl, frame), 0); inData *data = new inData; data->pQueue = pQueue; data->frame = frame; EXPECT_EQ(lynStreamAddAsyncCallback(sendStream, SendFrameCallback, data), 0); packet = pOutQueue->Pop(); EXPECT_EQ(lynVencRecvPacketAsync(recvStream, vencHdl, packet), 0); rcvData *out_data = new rcvData; out_data->pQueue = pOutQueue; out_data->packet = packet; out_data->localBuf = outbuf; out_data->dstFile = outputFileName; EXPECT_EQ(lynStreamAddAsyncCallback(recvStream, RecvPacketCallback, out_data), 0); } LOG(INFO) << "send cmd finish"; EXPECT_EQ(lynSynchronizeStream(sendStream), 0); EXPECT_EQ(lynSynchronizeStream(recvStream), 0); EXPECT_EQ(lynVencClose(vencHdl), 0); EXPECT_EQ(lynDestroyStream(sendStream), 0); EXPECT_EQ(lynDestroyStream(recvStream), 0); free(outbuf->data); delete outbuf; pQueue = nullptr; pOutQueue = nullptr; EXPECT_EQ(lynDestroyContext(context), 0); LOG(INFO) << "destory context finish"; } const uint32_t threadNum = 3; TEST(lynEncoderPATest, MultiChannelTest) { std::string outputFileName; std::thread threads[threadNum]; for (size_t i = 0; i < threadNum; i++) { outputFileName = "./output/MultiChannelTest_" + to_string(i) + ".h264"; threads[i] = std::thread(MultiChannelThreadFunc, outputFileName); } for (thread &th : threads) { th.join(); } }