VENC

This section describes the API call sequence of VENC, and sample code is also provided to help you better understand the sequence.

The video encoder (VENC) encodes YUV420SP images into H.264/H.265 video streams. For details about the VENC function and restrictions, see DVPP Media Acceleration Library.

API Call Sequence

Figure 1 Video encoding workflow

This module implements video encoding. The key APIs are described as follows:

  1. Call aclvencCreateChannel to create a VENC channel.
    • Perform the following steps before creating a video encoding channel.
      1. Call aclvencCreateChannelDesc to create channel description.
      2. Call aclvencSetChannelDescParam to set attributes of the channel description, including the thread, callback function, video encoding protocol, and input image format.
        1. The callback function needs to be created by the user in advance. It is used to obtain the encoded data and release related resources in a timely manner after video encoding. For details about the prototype of the callback function, see aclvencCallback.

          After the encoding is complete, free the input buffer and destroy the image description in the callback function in a timely manner. The output buffer is managed by the system, and therefore does not need to be freed by the user.

        2. The user needs to create a thread in advance and customize a thread function. When aclrtProcessReport is called in the thread function, the callback function in 1.b.i is triggered after a specified period of time.

        You are advised to call aclvencSetChannelDescParam to set the attributes of the channel description. You can set the value of an attribute by calling this API.

        To be compatible with earlier versions, you can also call the aclvencSetChannelDesc series APIs to set the attributes of the channel description. Each attribute corresponds to a set API.

    • The following APIs are encapsulated in aclvencCreateChannel and do not need to be called separately:
      1. aclrtCreateStream: explicitly creates a stream. This API is internally used by VENC.
      2. aclrtSubscribeReport: specifies the thread for processing the callback function in the stream. The callback function and thread are specified by calling aclvencSetChannelDescParam.
  2. Call aclvencSendFrame to encode YUV420SP images into H.264/H.265 video streams.
    • Perform the following steps before encoding a video stream:
      • Call <cf id="Bold">acldvppCreatePicDesc</cf> to create the description of the input image, and call <cf id="Bold">acldvppSetPicDesc</cf> series to configure the input image, such as the buffer address, buffer size, and image format.
      • Call aclvencCreateFrameConfig to create the single-frame encoding configuration data, and call aclvencSetFrameConfig to set whether to forcibly restart the I-frame interval and whether to end the frame.
    • During video encoding, aclrtLaunchCallback is encapsulated in aclvencSendFrame to add a callback function to be executed to the stream task queue. Therefore, <cf id="Bold">aclrtLaunchCallback</cf> does not need to be called separately.
    • After video encoding, use the callback function to obtain the result:
  3. Call aclvencDestroyChannel to destroy the video processing channel.
    • The channel is destroyed only after the transmitted frames are encoded and the callback function is processed.
    • The following APIs are encapsulated in aclvencDestroyChannel and do not need to be called separately:
      • aclrtUnSubscribeReport: Deregisters a thread. (The callback function in the stream is no longer processed by the specified thread.)
      • <cf id="Bold">aclrtDestroyStream</cf>: destroys a stream.
    • After the channel is destroyed, call aclvencDestroyChannelDesc to destroy the channel description.
    • You can destroy the thread created in 1.b.ii only after destroying the channel description.

Sample Code

The following is a code example of key steps of the VENC video encoding function. It is for reference only and cannot be directly copied for compilation and running. Following the API calls, add exception handling branches and specify log printing of error and information levels.

You can click venc_image to obtain the sample.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
// 1. Create a thread for executing the callback function and the thread function.
static bool runFlag = true;
void *ThreadFunc(void *arg)
{
     // Notice: create context for this thread
    int deviceId = 0;
    aclrtContext context = nullptr;
    aclError ret = aclrtCreateContext(&context, deviceId);
    INFO_LOG("process callback thread start ");
    while (runFlag) {
        // Notice: timeout 1000ms
        (void)aclrtProcessReport(1000);
    }
    // ......
    ret = aclrtDestroyContext(context);
    return (void*)0;
}

int createThreadErr = pthread_create(&threadId_, nullptr, ThreadFunc, nullptr);

// 2. Create a callback function.
void callback(acldvppPicDesc *input, acldvppStreamDesc *outputStreamDesc, void *userdata)
{
    // Obtain the video encoding result and write it to a file.
    void *outputDev = acldvppGetStreamDescData(outputStreamDesc);
    uint32_t streamDescSize = acldvppGetStreamDescSize(outputStreamDesc);
    if (!Utils::WriteToFile(g_outFileFp, outputDev, streamDescSize)) {
        ERROR_LOG("write file:%s failed.", g_outFile.c_str());
    }
    INFO_LOG("success to callback, stream size:%u", streamDescSize);
}

// 3. Create the description of a video encoding channel and set the attributes of the channel description. The thread and callback function need to be created in advance.
// vencChannelDesc_ is of type aclvdecChannelDesc.
vencChannelDesc_ = aclvencCreateChannelDesc();
aclError ret = aclvencSetChannelDescThreadId(vencChannelDesc_, threadId_);
ret = aclvencSetChannelDescCallback(vencChannelDesc_, callback);
ret = aclvencSetChannelDescEnType(vencChannelDesc_, enType_);
ret = aclvencSetChannelDescPicFormat(vencChannelDesc_, format_);
ret = aclvencSetChannelDescPicWidth(vencChannelDesc_, 128);
ret = aclvencSetChannelDescPicHeight(vencChannelDesc_, 128);
ret = aclvencSetChannelDescKeyFrameInterval(vencChannelDesc_, 16);

// 4. Create a channel for processing video streams and single-frame encoding data.
ret = aclvencCreateChannel(vencChannelDesc_);
// vencFrameConfig_ is of type aclvencFrameConfig.
vencFrameConfig_ = aclvencCreateFrameConfig();

// 5. Allocate the device buffer dataDev to store the input data for video encoding.
// Read image data.
const char *fileName = "../dvpp_venc_128x128_nv12.yuv";
FILE *fp = fopen(fileName, "rb+");
fseek(fp, 0, SEEK_END);
long fileLenLong = ftell(fp);
fseek(fp, 0, SEEK_SET);
auto fileLen = static_cast<uint32_t>(fileLenLong);
uint32_t dataSize = fileLen;

// Call aclrtGetRunMode to obtain the run mode of the software stack. If ACL_DEVICE is returned, allocate and use the device buffer. If ACL_HOST is returned, transfer the input image data to the device by using the aclrtMemcpy call. After the data transfer is complete, the buffer needs to be freed in a timely manner.
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
if(runMode == ACL_HOST){ 
    void *dataHost = malloc(fileLen);
    size_t readSize = fread(dataHost, 1, fileLen, fp);
    void *dataDev = nullptr;
    ret = acldvppMalloc(&dataDev, dataSize);
    ret = aclrtMemcpy(dataDev, dataSize, dataHost, fileLen, ACL_MEMCPY_HOST_TO_DEVICE);
    // Free the buffer in a timely manner after data transfer is complete.
    free(dataHost);
} 
else { 
    ret = acldvppMalloc(&dataDev, dataSize);
}

// 6. Perform video encoding.
size_t g_vencCnt = 16;
// 6.1 Create the input image description and set the buffer address and buffer size of the input image data.
inputPicputDesc_ = acldvppCreatePicDesc();
ret = acldvppSetPicDescData(inputPicputDesc_, dataDev);
ret = acldvppSetPicDescSize(inputPicputDesc_, dataSize);
// 6.2 Set the single-frame encoding configuration data, not the end frame.
ret = aclvencSetFrameConfigEos(vencFrameConfig_, 0);
ret = aclvencSetFrameConfigForceIFrame(vencFrameConfig_, 0);
// 6.3 Create the output stream description.
acldvppStreamDesc *outputStreamDesc = nullptr;
// 6.4 Perform video encoding.
while (g_vencCnt > 0) {
        ret = aclvencSendFrame(vencChannelDesc_, inputPicputDesc_,
            static_cast<void *>(outputStreamDesc), vencFrameConfig_, nullptr);
        g_vencCnt--;
    }
// 6.5 Set the single-frame encoding configuration data, which is the end frame.
ret = aclvencSetFrameConfigEos(vencFrameConfig_, 1);
ret = aclvencSetFrameConfigForceIFrame(vencFrameConfig_, 0);
// 6.6 Encode the last frame.
ret = aclvencSendFrame(vencChannelDesc_, nullptr, nullptr, vencFrameConfig_, nullptr);

// 7. Destroy allocations.
(void)aclvencDestroyChannel(vencChannelDesc_);
(void)aclvencDestroyChannelDesc(vencChannelDesc_);
(void)acldvppDestroyPicDesc(inputPicputDesc_);
(void)aclvencDestroyFrameConfig(vencFrameConfig_);
// Free the buffer and destroy the thread.
(void)acldvppFree(inBufferDev_);
void *res = nullptr;
int joinThreadErr = pthread_join(threadId_, &res);

......

If aclvencSetChannelDescParam is called to set the attributes of the channel description, call aclvencGetChannelDescParam to obtain the attribute values in the channel description. The sample code is as follows:

// Set the callback function.
void *func = (void *)callback;
aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_CALLBACK_PTR, 8, &func);
// Obtain the callback function.
void *func1 = nullptr;
aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_CALLBACK_PTR, 8, &len, &func1);

// Set the input image format.
acldvppPixelFormat format = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIXEL_FORMAT_UINT32, 4, &format);
// Obtain the input image format.
acldvppPixelFormat format1 = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIXEL_FORMAT_UINT32, 4, &len, &format1);

// Set the image width.
uint32_t width = 128;
aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_WIDTH_UINT32, 4, &width);
// Obtain the image width.
uint32_t width1 = 0;
aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_WIDTH_UINT32, 4, &len, &width1);

// Set the image height.
uint32_t height = 128;
aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_HEIGHT_UINT32, 4, &height);
// Obtain the image height.
uint32_t height1 = 0;
aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_HEIGHT_UINT32, 4, &len, &height1);

// Set the address of the encoding output buffer.
ret = acldvppMalloc(&buf, bufSize);
aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_BUF_ADDR_PTR, 8, &buf);
// Obtain the address of the encoding output buffer.
void *buf1 = nullptr;
aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_BUF_ADDR_PTR, 8, &len, &buf1);