LogoLogo
HomeMPEG-5 LCEVCSMPTE VC-6V-Nova PresenZV-Nova Platform
MPEG-5 LCEVC
MPEG-5 LCEVC
  • V-NOVA LCEVC
    • Overview
    • LCEVC Best Practices
  • Encoder
    • Getting Started
    • SDK
      • Encoder Integration Layer (EIL)
        • EIL Integration Process
          • Error Handling
          • Base Encoder Plugin
        • Features
        • Encoding Configuration Properties
        • CBR vs CRF
        • Lower Resolutions (720p and below)
        • V-Nova LCEVC-specific Parameters
        • Metadata
        • API (version 3.9)
    • NDK
      • LCEVC Encoder IP Core
  • Decoder
    • Getting Started
    • SDK
      • LCEVC Decoder for Web (LCEVCdecJS)
      • Decoder Integration Layer (DIL)
        • Functionality
        • API Overview
        • Example Integration Code
        • DIL API
        • DIL Types and Enumerations
        • DIL Properties API
    • NDK
      • LCEVC Decoder IP Core
        • LCEVC Decoder IP Core Deliverables
      • LCEVC Hybrid Driver-level Decoder
  • Integrations
    • LCEVC in Chromium
    • LCEVC in WebRTC
    • FFmpeg with LCEVC
      • Example Script
      • FFmpeg Encoder
      • FFmpeg Decoder
      • Putting the Software Together
    • Player Integrations
      • AVPlayer with LCEVC
      • VLCKit with LCEVC
      • Shaka Player with LCEVC
      • Embedding LCEVC-enabled demo hls.js player
      • AndroidX (ExoPlayer) with LCEVC
    • LCEVC in Android Open-Source Project
Powered by GitBook
LogoLogo

© Copyright V-Nova 2025

On this page
  • Overview
  • 1. Creation of the EIL
  • 2. Initialisation of the EIL
  • 3. Passing video frames to the EIL
  • 4. Fetching the encoded bitstream from the EIL
  • 5. Flushing the EIL
  • 6. Destruction of the EIL and Shutdown

Was this helpful?

  1. Encoder
  2. SDK
  3. Encoder Integration Layer (EIL)

EIL Integration Process

Overview

The integration of the EIL can be summarised in several steps:

  1. Creation of the EIL

  2. Initialisation of the EIL

  3. Passing video frames to the EIL

  4. Fetching the encoded bitstream from the EIL

  5. Flushing the EIL

  6. Destruction of the EIL

The above steps, as well as the various options available to integrations, are described below.

1. Creation of the EIL

An instance of the EIL is created using EIL_Open. An EILOpenSettings object is required, which at minimum, must specify the name of a base encoder. This instance of the EIL should be passed to all other functions. The EILOpenSettings can be initialised to a sensible default state using EIL_OpenSettingsDefault.

The EIL issues internally generated logging messages via a callback. The callback is registered on the EILOpenSettings struct. You must provide a callback function pointer before calling Open to receive messages.

void Log(void* userdata, int32_t level, const char* msg)
{
    fprintf(level == EIL_LL_Error ? stderr : stdout, msg);
}

EILContext OpenEIL()
{
    EILOpenSettings settings;
    EILReturnCode   rc = EIL_OpenSettingsDefault(&settings);

    if (rc != EIL_RC_Success)
    {
        fprintf(stderr, "Unable to initialise default open settings: %s\n",
                EIL_GetErrorString(rc));
        return NULL;
    }

    settings.base_encoder = "x264";
    settings.log_callback = Log;
    EILContext context   = NULL;

    if ((rc = EIL_Open(&settings, &context)) != EIL_RC_Success)
    {
        fprintf(stderr, "Unable to open the EIL: %s\n", EIL_GetErrorString(rc));
        return NULL;
    }

    return context;
}

2. Initialisation of the EIL

The EIL is initialised using EIL_Initialise. An EILInitSettings object is required, in which the encoding properties, such as resolution, framerate and bitrate, are specified. properties_json should be set to a json object string with extra optional encoder options.

Upon successful initialisation, the user can then proceed to encoding YUV input.

// context should have been previously created with EIL_Open
bool InitialiseEIL(EILContext context)
{
    EILInitSettings settings;
    EILReturnCode   rc = EIL_InitSettingsDefault(&settings);

    settings.width     = 1920;
    settings.height    = 1080;
    settings.fps_num   = 25;
    settings.fps_denom = 1;
    settings.bitrate   = 3000; // NB: This is in kbps

    // The properties are usually generated from user input
    settings.properties_json = "{ \"encoding_transform_type\": \"dds\" }";

    if ((rc = EIL_Initialise(context, &settings)) != EIL_RC_Success)
    {
        fprintf(stderr, "Unable to intiailise the EIL: %s\n", 
                EIL_GetErrorString(rc));
        return false;
    }

    return true;
}

3. Passing video frames to the EIL

If the previous steps succeeded, then the EIL is now ready to receive frames.

If the EIL has been configured to not use external input, then a preallocated frame can be fetched from the EIL using EIL_GetPicture; otherwise, an EILPicture object should be created at the integration layer and passed to EIL_Encode.

To encode YUV frames, perform the following steps:

  1. Call EIL_GetPicture to obtain an EILPicture object for the type of frame to be encoded. The caller now temporarily owns the picture object.

  2. Load YUV data into the pre-allocated memory of the plane member of the picture object.

  3. Call EIL_Encode using the EILPicture object to begin encoding the picture. The encoder retakes ownership of the EILPicture object, and it is no longer valid to manipulate it. This is a blocking call for the duration of the encoding of a single frame.

bool Encode(EILContext context, int64_t pts)
{
    EILPicture* picture = NULL;
    EILReturnCode rc = EIL_GetPicture(context, EIL_FrameType_Progressive, &picture);

    if (rc != EIL_RC_Success)
    {
        fprintf(stderr, "Unable to obtain picture from the EIL: %s\n",
                EIL_GetErrorString(rc));
        return false;
    }

    // Write the frame data to picture->plane ensuring that the picture->stride
    // for each plane is obeyed.

    picture->pts = pts;

    if ((rc = EIL_Encode(context, picture)) != EIL_RC_Success)
    {
        fprintf(stderr, "Unable to encode picture: %s\n", EIL_GetErrorString(rc));
        return false;
    }

    return true;
}

It is invalid behaviour to hold onto the EILOutput object across multiple frames. The caller must copy and buffer the data if that is the required behaviour.

4. Fetching the encoded bitstream from the EIL

After receiving enough frames, the EIL is ready to output encoded data. The encoded data is fetched using the EIL_GetOutput function.

  • If there is any output data available when EIL_GetOutput is invoked, then EIL_GetOutput returns EIL_RC_Success.

  • If there is no data available and no errors occurred, then EIL_GetOutput returns EIL_RC_Finished.

To fetch the encoded bitstream from the EIL, perform the following steps:

  1. The loop call EIL_GetOutput returns the following, depending upon the presence of output data: - If there is output data, then EILGetOutput returns EIL_RC_Success and populates EILOutput* with a valid pointer. The caller now owns the output data object. - If there is no more output data, then the EIL_GetOutput returns EIL_RC_Finished, unless an error is generated. Please be aware this step is important, as the EIL may generate more than one output per single input, for example, when generating an interlaced encode.

  2. Utilise the output data, either by writing it directly out to file or copying it if it must be buffered for output in a muxing system.

  3. Call EIL_ReleaseOutput to release the output object back to the ownership of the encoder. It is then no longer valid to read from this object.

Once the integration has finished with the output data, it should return it to the EIL using EIL_ReleaseOutput. It is perfectly fine to call this method from a separate thread other than EIL_Encode.

int HandleOutput(EILContext context)
{
    EILReturnCode rc     = EIL_RC_Success;
    EILOutput*    output = NULL;

    /* Always loop over output. Flush blocks and accumulates may frames worth of
     output. */
    while ((rc = EIL_GetOutput(context, &output)) == EIL_RC_Success)
    {
        // pass the bitstream to a muxer
        mux(output->pts, output->dts, output->data, output->data_length);
		
        /* Immediately release the output. */
        if (EIL_ReleaseOutput(context, output) != EIL_RC_Success)
        {
            fprintf(stderr, "Unable to get output from the EIL: %s\n",
                    EIL_GetErrorString(rc));
            return -1;
        }
    }

    if (rc != EIL_RC_Finished)
    {
        fprintf("Error: Failed to handle output\n");
        return -1;
    }

    return 0;
}

5. Flushing the EIL

To signal to the EIL that no more input data is available, simply pass NULL to EIL_Encode. The EIL then finishes processing any queued video frames. This should only ever be performed at the end of the stream.

To complete encoding, perform the following steps:

  1. Call EIL_Encode once using a null pointer for the picture object argument, to flush the entire pipeline. This is a blocking call whilst the remaining pictures are encoded. Subsequent calls will have no effect.

  2. Repeat step 4 from the encoding process IL_RC_Finished is returned.

6. Destruction of the EIL and Shutdown

Once encoding has finished, then the EIL can be destroyed. Call EIL_Close to safely shut down and release the encoding context.

void CloseEIL(EILContext* context)
{
    EIL_Close(*context);
    *context = NULL;
}
PreviousEncoder Integration Layer (EIL)NextError Handling

Last updated 4 months ago

Was this helpful?