Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
/*
 * OMX Video decoder
 *
 * Copyright (C) 2022 Baikal Electronics, JSC
 */

#include "config.h"

#include <stdlib.h>
#include <pthread.h>
#include <dlfcn.h>
#include <time.h>

#include <stdatomic.h>

#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/log.h"
#if CONFIG_OMX_IMG
#include "libswscale/swscale.h"
#endif /* CONFIG_OMX_IMG */

#include "avcodec.h"
#include "h264.h"
#include "internal.h"

#include <OMX_Core.h>
#include <OMX_Component.h>
#include <OMX_RoleNames.h>
#include <OMX_IndexExt.h>
#include <OMX_VideoExt.h>

#define OMX_DECODER_VERSION_MAJOR       (1)
#define OMX_DECODER_VERSION_MINOR       (2)
#define OMX_DECODER_VERSION_REVISION    (0)
#define OMX_DECODER_VERSION_STEP        (0)

#define OMX_STATE_TIMEOUT   5
#define OMX_OUTPUT_TIMEOUT  1
#define OMX_INPUT_QUEUE_THRESHOLD 4

static const char * const omx_core_libraries[] = {
#if CONFIG_OMX_IMG
        "libomx_vxd.so",
#else
        "libOMX_Core.so",
        "libOmxCore.so",
#endif /* CONFIG_OMX_IMG */
        NULL
};

#ifdef OMX_SKIP64BIT
static OMX_TICKS to_omx_ticks(int64_t value)
{
    OMX_TICKS res;
    s.nLowPart  = value & 0xffffffff;
    s.nHighPart = value >> 32;
    return res;
}
static int64_t from_omx_ticks(OMX_TICKS value)
{
    return (((int64_t)value.nHighPart) << 32) | value.nLowPart;
}
#else
#define to_omx_ticks(value) (value)
#define from_omx_ticks(value) (value)
#endif

#define OMX_INIT_STRUCT(p) do {  \
    memset ((p), 0, sizeof (*(p))); \
    (p)->nSize = sizeof (*(p)); \
    (p)->nVersion.s.nVersionMajor = OMX_DECODER_VERSION_MAJOR; \
    (p)->nVersion.s.nVersionMinor = OMX_DECODER_VERSION_MINOR; \
    (p)->nVersion.s.nRevision     = OMX_DECODER_VERSION_REVISION; \
    (p)->nVersion.s.nStep         = OMX_DECODER_VERSION_STEP; \
} while (0)

typedef struct _OMXCore OMXCore;
typedef struct _OMXCodecContext OMXCodecContext;
typedef struct _OMXCodecPort OMXCodecPort;
typedef struct _OMXInputBuffer OMXInputBuffer;
typedef struct _OMXBufferHeader OMXBufferHeader;

struct _OMXCore {
    void *handle;
    OMX_ERRORTYPE (*pInit)(void);
    OMX_ERRORTYPE (*pDeinit)(void);
    OMX_ERRORTYPE (*pComponentNameEnum)(OMX_STRING, OMX_U32, OMX_U32);
    OMX_ERRORTYPE (*pGetHandle)(OMX_HANDLETYPE*, OMX_STRING, OMX_PTR,
                                OMX_CALLBACKTYPE*);
    OMX_ERRORTYPE (*pFreeHandle)(OMX_HANDLETYPE);
#if CONFIG_OMX_IMG
    OMX_ERRORTYPE (*pComponentOfRoleEnum)(OMX_STRING, OMX_STRING, OMX_U32);
#else
    OMX_ERRORTYPE (*pGetComponentsOfRole)(OMX_STRING, OMX_U32*, OMX_U8**);
#endif /* CONFIG_OMX_IMG */
};

struct _OMXInputBuffer {
    AVBufferRef *ref;
    void *data;
    size_t size;
    int64_t pts;
    OMX_U32 flags;
    OMXInputBuffer *next;
};

struct _OMXBufferHeader {
    OMX_BUFFERHEADERTYPE *header;
    OMXCodecPort *port;
    int decoding;
    void *priv;
    OMXBufferHeader *next;
};

struct _OMXCodecPort {
    int index;
    OMX_PARAM_PORTDEFINITIONTYPE port_def;
    // number of port buffers
    int num_buffers;
    // buffer headers pool
    OMXBufferHeader *headers;
    // number of headers in pool
    atomic_int num_headers;
    // headers pool lock
    pthread_mutex_t mutex;
    OMXCodecContext *ctx;
};

struct _OMXCodecContext {
    const AVClass *class;

    AVCodecContext *avctx;

    char *libname;
    OMXCore *core;

    char component_name[OMX_MAX_STRINGNAME_SIZE];
    OMX_HANDLETYPE handle;

    int stride, plane_size;

    OMXCodecPort in;
    // lock free (accessed by omx_decode only)
    OMXInputBuffer *in_queue_head, *in_queue_tail; 
    int in_queue_size;

    OMXCodecPort out;
    OMXBufferHeader *out_queue_head, *out_queue_tail;
    int out_queue_size;
    pthread_mutex_t out_queue_mutex;
    pthread_cond_t out_queue_cond;
#if !CONFIG_OMX_IMG
    uint8_t *output_buf;
    int output_buf_size;
#endif /* !CONFIG_OMX_IMG */
    int settings_changed;
#if CONFIG_OMX_IMG
    struct SwsContext *sws_ctx;
    enum AVPixelFormat decoder_pix_fmt;
#endif /* CONFIG_OMX_IMG */

    int flushing;

    pthread_mutex_t state_mutex;
    pthread_cond_t state_cond;
    OMX_STATETYPE state;
    OMX_ERRORTYPE error;

    int eos_sent, eos_received;
    int extradata_sent;
};

static av_cold int omx_core_load(OMXCore *core, void *logctx,
                                 const char *libname)
{
    core->handle = dlopen(libname, RTLD_LOCAL | RTLD_NOW);
    if (!core->handle) {
        av_log(logctx, AV_LOG_WARNING, "%s not found\n", libname);
        return -1;
    }
    core->pInit                = dlsym(core->handle, "OMX_Init");
    core->pDeinit              = dlsym(core->handle, "OMX_Deinit");
    core->pGetHandle           = dlsym(core->handle, "OMX_GetHandle");
    core->pFreeHandle          = dlsym(core->handle, "OMX_FreeHandle");
#if CONFIG_OMX_IMG
    core->pComponentOfRoleEnum = dlsym(core->handle, "OMX_ComponentOfRoleEnum");
#else
    core->pGetComponentsOfRole = dlsym(core->handle, "OMX_GetComponentsOfRole");
#endif /* CONFIG_OMX_IMG */
    if (!core->pInit || !core->pDeinit || !core->pGetHandle ||
        !core->pFreeHandle ||
#if CONFIG_OMX_IMG
        !core->pComponentOfRoleEnum
#else
        !core->pGetComponentsOfRole
#endif /* CONFIG_OMX_IMG */
    ) {
        av_log(logctx, AV_LOG_WARNING, "Not all functions found in %s\n",
               libname);
        dlclose(core->handle);
        core->handle = NULL;
        return -1;
    }
    return 0;
}

static const char *omx_error_string(OMX_ERRORTYPE err)
{
    switch (err) {
    case OMX_ErrorNone:
        return "None";
    case OMX_ErrorInsufficientResources:
        return "Insufficient resources";
    case OMX_ErrorUndefined:
        return "Undefined";
    case OMX_ErrorInvalidComponentName:
        return "Invalid component name";
    case OMX_ErrorComponentNotFound:
        return "Component not found";
    case OMX_ErrorInvalidComponent:
        return "Invalid component";
    case OMX_ErrorBadParameter:
        return "Bad parameter";
    case OMX_ErrorNotImplemented:
        return "Not implemented";
    case OMX_ErrorUnderflow:
        return "Underflow";
    case OMX_ErrorOverflow:
        return "Overflow";
    case OMX_ErrorHardware:
        return "Hardware";
    case OMX_ErrorInvalidState:
        return "Invalid state";
    case OMX_ErrorStreamCorrupt:
        return "Stream corrupt";
    case OMX_ErrorPortsNotCompatible:
        return "Ports not compatible";
    case OMX_ErrorResourcesLost:
        return "Resources lost";
    case OMX_ErrorNoMore:
        return "No more";
    case OMX_ErrorVersionMismatch:
        return "Version mismatch";
    case OMX_ErrorNotReady:
        return "Not ready";
    case OMX_ErrorTimeout:
        return "Timeout";
    case OMX_ErrorSameState:
        return "Same state";
    case OMX_ErrorResourcesPreempted:
        return "Resources preempted";
    case OMX_ErrorPortUnresponsiveDuringAllocation:
        return "Port unresponsive during allocation";
    case OMX_ErrorPortUnresponsiveDuringDeallocation:
        return "Port unresponsive during deallocation";
    case OMX_ErrorPortUnresponsiveDuringStop:
        return "Port unresponsive during stop";
    case OMX_ErrorIncorrectStateTransition:
        return "Incorrect state transition";
    case OMX_ErrorIncorrectStateOperation:
        return "Incorrect state operation";
    case OMX_ErrorUnsupportedSetting:
        return "Unsupported setting";
    case OMX_ErrorUnsupportedIndex:
        return "Unsupported index";
    case OMX_ErrorBadPortIndex:
        return "Bad port index";
    case OMX_ErrorPortUnpopulated:
        return "Port unpopulated";
    case OMX_ErrorComponentSuspended:
        return "Component suspended";
    case OMX_ErrorDynamicResourcesUnavailable:
        return "Dynamic resources unavailable";
    case OMX_ErrorMbErrorsInFrame:
        return "Macroblock errors in frame";
    case OMX_ErrorFormatNotDetected:
        return "Format not detected";
    case OMX_ErrorContentPipeOpenFailed:
        return "Content pipe open failed";
    case OMX_ErrorContentPipeCreationFailed:
        return "Content pipe creation failed";
    case OMX_ErrorSeperateTablesUsed:
        return "Separate tables used";
    case OMX_ErrorTunnelingUnsupported:
        return "Tunneling unsupported";
    default:
        if (err >= OMX_ErrorKhronosExtensions &&
            err < OMX_ErrorVendorStartUnused)
            return "Khronos extension error";
        else if (err >= OMX_ErrorVendorStartUnused &&
                 err < OMX_ErrorMax)
            return "Vendor specific error";
        else
            return "Unknown error";
    }
}

static av_cold OMXCore *omx_core_init(void *logctx, const char *libname)
{
    OMXCore *core;
    int loaded = 0;
    OMX_ERRORTYPE err;

    core = av_mallocz(sizeof(*core));
    if (!core) {
        av_log(logctx, AV_LOG_ERROR,
               "Core initialization error: Not enough memory\n");
        return NULL;
    }

    if (libname) 
        loaded = (omx_core_load(core, logctx, libname) == 0);
    else {
        int i;

        for (i = 0; omx_core_libraries[i] && !loaded; i++)
            loaded = (omx_core_load(core, logctx, omx_core_libraries[i]) == 0);
    }
    if (!loaded) {
        av_log(logctx, AV_LOG_ERROR,
               "Core initialization error: Core library not found\n");
        av_free(core);
        return NULL;
    }

    err = core->pInit();
    if (err != OMX_ErrorNone) {
        av_log(logctx, AV_LOG_ERROR,
               "Core initialization error: %s\n", omx_error_string(err));
        dlclose(core->handle);
        av_free(core);
        return NULL;
    }

    return core;
}

static av_cold void omx_core_destroy(OMXCore *core)
{
    if (core) {
        core->pDeinit();
        dlclose(core->handle);
        av_free(core);
    }
}

#if !CONFIG_OMX_IMG
static av_cold int omx_component_find(OMXCodecContext *ctx,
                                      const char *role, char *str, int str_size)
{
    AVCodecContext *avctx = ctx->avctx;
    int ret = 0;
    OMX_U32 i, num = 0;
    char **components;

    ctx->core->pGetComponentsOfRole((OMX_STRING) role, &num, NULL);
    if (!num) {
        av_log(avctx, AV_LOG_ERROR, "No component for role %s found\n", role);
        return -1;
    }
    components = av_mallocz_array(num, sizeof(*components));
    if (!components) {
        av_log(avctx, AV_LOG_ERROR,
               "Component search error: Not enough memory\n");
        return -1;
    }
    for (i = 0; i < num; i++) {
        components[i] = av_mallocz(OMX_MAX_STRINGNAME_SIZE);
        if (!components[i]) {
            av_log(avctx, AV_LOG_ERROR,
                   "Component search error: Not enough memory\n");
            ret = -1;
            goto end;
        }
    }
    ctx->core->pGetComponentsOfRole((OMX_STRING) role, &num,
                                    (OMX_U8**) components);
    av_strlcpy(str, components[0], str_size);
end:
    for (i = 0; i < num; i++)
        av_free(components[i]);
    av_free(components);
    return ret;
}
#endif /* !CONFIG_OMX_IMG */

static OMX_ERRORTYPE omx_event_handler(OMX_HANDLETYPE component,
                                       OMX_PTR app_data, OMX_EVENTTYPE event,
                                       OMX_U32 data1, OMX_U32 data2,
                                       OMX_PTR event_data)
{
    OMXCodecContext *ctx = app_data;

    /* This uses casts in the printfs, since OMX_U32 actually is a typedef for
       unsigned long in official header versions (but there are also modified
       versions where it is something else). */
    switch (event) {
    case OMX_EventError:
        pthread_mutex_lock(&ctx->state_mutex);
        av_log(ctx->avctx, AV_LOG_ERROR, "OMX error %"PRIx32"\n",
               (uint32_t) data1);
        ctx->error = data1;
        pthread_cond_broadcast(&ctx->state_cond);
        pthread_mutex_unlock(&ctx->state_mutex);
        break;
    case OMX_EventCmdComplete:
        if (data1 == OMX_CommandStateSet) {
            pthread_mutex_lock(&ctx->state_mutex);
            ctx->state = data2;
            av_log(ctx->avctx, AV_LOG_VERBOSE,
                   "OMX state changed to %"PRIu32"\n", (uint32_t) data2);
            pthread_cond_broadcast(&ctx->state_cond);
            pthread_mutex_unlock(&ctx->state_mutex);
        } else if (data1 == OMX_CommandPortDisable) {
            av_log(ctx->avctx, AV_LOG_VERBOSE,
                   "OMX port %"PRIu32" disabled\n", (uint32_t) data2);
            pthread_mutex_lock(&ctx->state_mutex);
            pthread_cond_broadcast(&ctx->state_cond);
            pthread_mutex_unlock(&ctx->state_mutex);
        } else if (data1 == OMX_CommandPortEnable) {
            av_log(ctx->avctx, AV_LOG_VERBOSE,
                   "OMX port %"PRIu32" enabled\n", (uint32_t) data2);
            pthread_mutex_lock(&ctx->state_mutex);
            pthread_cond_broadcast(&ctx->state_cond);
            pthread_mutex_unlock(&ctx->state_mutex);
        } else if (data1 == OMX_CommandFlush) {
            av_log(ctx->avctx, AV_LOG_VERBOSE,
                   "OMX port %"PRIu32" flushed\n", (uint32_t) data2);
            pthread_mutex_lock(&ctx->state_mutex);
            ctx->flushing = 0;
            pthread_cond_broadcast(&ctx->state_cond);
            pthread_mutex_unlock(&ctx->state_mutex);
        } else {
            av_log(ctx->avctx, AV_LOG_VERBOSE,
                   "OMX command complete, command %"PRIu32", value %"PRIu32"\n",
                   (uint32_t) data1, (uint32_t) data2);
        }
        break;
    case OMX_EventPortSettingsChanged:
        av_log(ctx->avctx, AV_LOG_VERBOSE,
               "OMX port %"PRIu32" settings changed\n", (uint32_t) data1);
        /* Probably we are waiting for output data now. Wake up waiting task
           to apply new output port settings. */
        pthread_mutex_lock(&ctx->out_queue_mutex);
        ctx->settings_changed = 1;
        pthread_cond_broadcast(&ctx->out_queue_cond);
        pthread_mutex_unlock(&ctx->out_queue_mutex);
        break;
    case OMX_EventBufferFlag:
        av_log(ctx->avctx, AV_LOG_VERBOSE,
               "OMX port %"PRIu32" EOS received (%"PRIx32")\n",
               (uint32_t) data1, (uint32_t) data2);
        break;
    default:
        av_log(ctx->avctx, AV_LOG_VERBOSE, "OMX event %d %"PRIx32" %"PRIx32"\n",
               event, (uint32_t) data1, (uint32_t) data2);
        break;
    }
    return OMX_ErrorNone;
}

/* Must be called with pool locked */
static void omx_buffer_release(OMXBufferHeader *h)
{
    OMXCodecPort *port = h->port;
    OMXCodecContext *ctx = h->port->ctx;

    if (h->decoding) {
        h->decoding = 0;
        if (port->port_def.eDir == OMX_DirInput) {
            OMXInputBuffer *buffer = h->priv;

            h->header->pBuffer = NULL;
            av_buffer_unref(&buffer->ref);
            av_free(buffer);
        }
        h->next = port->headers;
        port->headers = h;
        atomic_fetch_add(&port->num_headers, 1);
    }
    else
        av_log(ctx->avctx, AV_LOG_VERBOSE,
               "Port %d release unused buffer %p - ignore\n", port->index, h);
}

static OMX_ERRORTYPE omx_empty_buffer_done(OMX_HANDLETYPE component,
                                           OMX_PTR app_data,
                                           OMX_BUFFERHEADERTYPE *header)
{
    OMXBufferHeader *h = header->pAppPrivate;
    OMXCodecPort *port = h->port;

    pthread_mutex_lock(&port->mutex);
    omx_buffer_release(h);
    pthread_mutex_unlock(&port->mutex);
    /* Probably we are waiting for output data now, but decoder has not enough
       input data yet. Wake up waiting task to send new data to the decoder. */
    pthread_mutex_lock(&port->ctx->out_queue_mutex);
    pthread_cond_broadcast(&port->ctx->out_queue_cond);
    pthread_mutex_unlock(&port->ctx->out_queue_mutex);

    return OMX_ErrorNone;
}

static OMX_ERRORTYPE omx_fill_buffer_done(OMX_HANDLETYPE component,
                                          OMX_PTR app_data,
                                          OMX_BUFFERHEADERTYPE *header)
{
    OMXCodecContext *ctx = app_data;
    OMXBufferHeader *h = header->pAppPrivate;
    OMXCodecPort *port = h->port;

    pthread_mutex_lock(&port->mutex);
    if (h->decoding) {
        h->decoding = 0;
        // add buffer to output queue
        pthread_mutex_lock(&ctx->out_queue_mutex);
        h->next = NULL;
        if (!ctx->out_queue_head)
            ctx->out_queue_head = h;
        if (ctx->out_queue_tail)
            ctx->out_queue_tail->next = h;
        ctx->out_queue_tail = h;
        ctx->out_queue_size++;
        pthread_cond_broadcast(&ctx->out_queue_cond);
        pthread_mutex_unlock(&ctx->out_queue_mutex);
     }
    else
        av_log(ctx->avctx, AV_LOG_VERBOSE, 
               "Fill buffer done called for unused buffer\n");
    pthread_mutex_unlock(&port->mutex);

    return OMX_ErrorNone;
}

static const OMX_CALLBACKTYPE omx_callbacks = {
    omx_event_handler,
    omx_empty_buffer_done,
    omx_fill_buffer_done
};

static enum AVPixelFormat omx_to_av_pix_fmt(OMX_COLOR_FORMATTYPE color_format)
{
    switch (color_format) {
    case OMX_COLOR_FormatL8:
        return AV_PIX_FMT_GRAY8;
    case OMX_COLOR_FormatYUV420Planar:
    case OMX_COLOR_FormatYUV420PackedPlanar:
        return AV_PIX_FMT_YUV420P;
    case OMX_COLOR_FormatYUV420SemiPlanar:
    case OMX_COLOR_FormatYUV420PackedSemiPlanar:
        return AV_PIX_FMT_NV12;
    case OMX_COLOR_FormatYUV422SemiPlanar:
        return AV_PIX_FMT_NV16;
    case OMX_COLOR_FormatYCbYCr:
        return AV_PIX_FMT_YUYV422;
    case OMX_COLOR_FormatYCrYCb:
        return AV_PIX_FMT_YVYU422;
    case OMX_COLOR_FormatCbYCrY:
        return AV_PIX_FMT_UYVY422;
    case OMX_COLOR_Format32bitARGB8888:
        /* There is a mismatch in omxil specification 4.2.1 between
           OMX_COLOR_Format32bitARGB8888 and its description
           Follow the description */
        return AV_PIX_FMT_ABGR;
    case OMX_COLOR_Format32bitBGRA8888:
        /* Same issue as OMX_COLOR_Format32bitARGB8888 */
        return AV_PIX_FMT_ARGB;
    case OMX_COLOR_Format16bitRGB565:
        return AV_PIX_FMT_RGB565;
    case OMX_COLOR_Format16bitBGR565:
        return AV_PIX_FMT_BGR565;
    }

    return AV_PIX_FMT_NONE;
}

static OMX_COLOR_FORMATTYPE omx_codec_select_color_format(OMXCodecContext *ctx)
{
    AVCodecContext *avctx = ctx->avctx;
    OMX_COLOR_FORMATTYPE color_format = OMX_COLOR_FormatUnused;

    if (avctx->pix_fmt != AV_PIX_FMT_NONE) {
        OMX_VIDEO_PARAM_PORTFORMATTYPE video_port_format = { 0 };
        OMX_ERRORTYPE err;
        int i;

        for (i = 0; ; i++) {
            OMX_INIT_STRUCT(&video_port_format);
            video_port_format.nIndex = i;
            video_port_format.nPortIndex = ctx->out.index;
            if (OMX_GetParameter(ctx->handle, OMX_IndexParamVideoPortFormat,
                                 &video_port_format) != OMX_ErrorNone)
                break;
            if (omx_to_av_pix_fmt(video_port_format.eColorFormat) ==
                avctx->pix_fmt) {
                color_format = video_port_format.eColorFormat;
                break;
            }
        }
        if (color_format != OMX_COLOR_FormatUnused) {
            OMX_INIT_STRUCT(&video_port_format);
            video_port_format.nPortIndex = ctx->out.index;
            video_port_format.eColorFormat = color_format;
            err = OMX_SetParameter(ctx->handle, OMX_IndexParamVideoPortFormat,
                                   &video_port_format);
            if (err != OMX_ErrorNone)
                av_log(avctx, AV_LOG_WARNING,
                       "Can not set port %d color format: %s\n",
                       ctx->out.index, omx_error_string(err));
        }
    }
    return color_format;
}

static av_cold void omx_port_init(OMXCodecContext *ctx, OMXCodecPort *port)
{
    port->index = -1;
    pthread_mutex_init(&port->mutex, NULL);
    port->ctx = ctx;
}

static OMX_ERRORTYPE omx_update_port_definition(OMXCodecPort *port,
                                      OMX_PARAM_PORTDEFINITIONTYPE *port_def)
{
    OMXCodecContext *ctx = port->ctx;
    OMX_ERRORTYPE err;

    if (port_def) {
        err = OMX_SetParameter(ctx->handle, OMX_IndexParamPortDefinition,
                               port_def);
        if (err != OMX_ErrorNone)
            av_log(ctx->avctx, AV_LOG_WARNING,
                   "Can not set port %d definition: %s\n", port->index,
                   omx_error_string(err));
    }
    OMX_GetParameter(ctx->handle, OMX_IndexParamPortDefinition,
                     &port->port_def);

    return err;
}

static av_cold OMX_VIDEO_CODINGTYPE omx_id_to_coding_type(enum AVCodecID id)
{
    switch (id) {
    case AV_CODEC_ID_H264:
        return OMX_VIDEO_CodingAVC;
    case AV_CODEC_ID_HEVC:
        return OMX_VIDEO_CodingHEVC;
    case AV_CODEC_ID_MPEG4:
        return OMX_VIDEO_CodingMPEG4;
    case AV_CODEC_ID_H263:
        return OMX_VIDEO_CodingH263;
    case AV_CODEC_ID_MPEG2VIDEO:
        return OMX_VIDEO_CodingMPEG2;
    case AV_CODEC_ID_MJPEG:
        return OMX_VIDEO_CodingMJPEG;
    case AV_CODEC_ID_VP8:
        return OMX_VIDEO_CodingVP8;
    case AV_CODEC_ID_WMV3:
        return OMX_VIDEO_CodingWMV;
    case AV_CODEC_ID_RV30:
    case AV_CODEC_ID_RV40:
        return OMX_VIDEO_CodingRV;
#if CONFIG_OMX_IMG
    case AV_CODEC_ID_VC1:
        return OMX_VIDEO_CodingVC1;
    case AV_CODEC_ID_FLV1:
        return OMX_VIDEO_CodingSorensonSpark;
    case AV_CODEC_ID_VP6:
    case AV_CODEC_ID_VP6F:
    case AV_CODEC_ID_VP6A:
        return OMX_VIDEO_CodingVP6;
    case AV_CODEC_ID_AVS:
        return OMX_VIDEO_CodingAVS;
#endif /* CONFIG_OMX_IMG */
    }
    return OMX_VIDEO_CodingUnused;
}

static void omx_codec_set_output_format(OMXCodecContext *ctx)
{
    AVCodecContext *avctx = ctx->avctx;

#if CONFIG_OMX_IMG
    if (avctx->pix_fmt == AV_PIX_FMT_NONE)
        avctx->pix_fmt = AV_PIX_FMT_YUV420P;

    ctx->decoder_pix_fmt =
        omx_to_av_pix_fmt(ctx->out.port_def.format.video.eColorFormat);
    if (ctx->decoder_pix_fmt != AV_PIX_FMT_NONE) {
        if (ctx->decoder_pix_fmt != avctx->pix_fmt) {
            /* prepare pixel formats converter, if decoder and requested
               output formats are not the same */
            ctx->sws_ctx = sws_getCachedContext(ctx->sws_ctx,
                avctx->width, avctx->height, ctx->decoder_pix_fmt,
                avctx->width, avctx->height, avctx->pix_fmt,
                0, NULL, NULL, NULL);
            if (!ctx->sws_ctx)
                av_log(avctx, AV_LOG_ERROR,
                       "Can not create pixel formats converter\n");
        }
    }
#else
    avctx->pix_fmt =
        omx_to_av_pix_fmt(ctx->out.port_def.format.video.eColorFormat);
#endif /* CONFIG_OMX_IMG */
}

static av_cold int omx_port_config(OMXCodecPort *port)
{
    OMXCodecContext *ctx = port->ctx;
    AVCodecContext *avctx = ctx->avctx;

    port->port_def.bEnabled   = OMX_TRUE;
    port->port_def.bPopulated = OMX_FALSE;
    port->port_def.eDomain    = OMX_PortDomainVideo;
    port->port_def.format.video.pNativeRender         = NULL;
    port->port_def.format.video.bFlagErrorConcealment = OMX_FALSE;
    port->port_def.format.video.nFrameWidth  = avctx->width;
    port->port_def.format.video.nFrameHeight = avctx->height;
    if (avctx->framerate.den > 0 && avctx->framerate.num > 0)
        port->port_def.format.video.xFramerate =
            (1LL << 16) * avctx->framerate.num / avctx->framerate.den;
    else if (avctx->pkt_timebase.den > 0 && avctx->pkt_timebase.num > 0)
        port->port_def.format.video.xFramerate =
            (1LL << 16) * avctx->pkt_timebase.den / avctx->pkt_timebase.num;
    else
        port->port_def.format.video.xFramerate = 0;

    if (port->port_def.eDir == OMX_DirInput) {
        port->port_def.format.video.nStride      = 0;
        port->port_def.format.video.nSliceHeight = 0;
        port->port_def.format.video.eCompressionFormat =
            omx_id_to_coding_type(avctx->codec_id);
    }
    else {
        port->port_def.format.video.nStride       = ctx->stride;
        port->port_def.format.video.nSliceHeight  = ctx->plane_size;
    }

    if (omx_update_port_definition(port, &port->port_def) != OMX_ErrorNone)
        return -1;
    port->num_buffers = port->port_def.nBufferCountActual;
    if (port->port_def.eDir == OMX_DirOutput) {
        ctx->stride     = port->port_def.format.video.nStride;
        ctx->plane_size = port->port_def.format.video.nSliceHeight;
        omx_codec_set_output_format(ctx);
    }

    return 0;
}

static av_cold int omx_component_init(OMXCodecContext *ctx, const char *role)
{
    AVCodecContext *avctx = ctx->avctx;
    OMX_PARAM_COMPONENTROLETYPE role_params = { 0 };
    OMX_PORT_PARAM_TYPE video_port_params = { 0 };
    OMX_ERRORTYPE err;
    int i;

    err = ctx->core->pGetHandle(&ctx->handle, ctx->component_name, ctx,
                                (OMX_CALLBACKTYPE*) &omx_callbacks);
    if (err != OMX_ErrorNone) {
        av_log(avctx, AV_LOG_ERROR, "Can not get component handle: %s\n",
               omx_error_string(err));
        return -1;
    }

    OMX_INIT_STRUCT(&role_params);
    av_strlcpy(role_params.cRole, role, sizeof(role_params.cRole));
    OMX_SetParameter(ctx->handle, OMX_IndexParamStandardComponentRole,
                     &role_params);

    OMX_INIT_STRUCT(&video_port_params);
    err = OMX_GetParameter(ctx->handle, OMX_IndexParamVideoInit,
                           &video_port_params);
    if (err != OMX_ErrorNone) {
        av_log(avctx, AV_LOG_ERROR, "Can not get component ports: %s\n",
               omx_error_string(err));
        return -1;
    }

    for (i = 0; i < video_port_params.nPorts; i++) {
        int port = video_port_params.nStartPortNumber + i;
        OMX_PARAM_PORTDEFINITIONTYPE port_def = { 0 };

        OMX_INIT_STRUCT(&port_def);
        port_def.nPortIndex = port;
        err = OMX_GetParameter(ctx->handle, OMX_IndexParamPortDefinition,
                               &port_def);
        if (err != OMX_ErrorNone) {
            av_log(avctx, AV_LOG_WARNING,
                   "Can not get port %d definition: %s\n", port,
                   omx_error_string(err));
            continue;
        }
        if (port_def.eDir == OMX_DirInput && ctx->in.index < 0) {
            ctx->in.index = port;
            ctx->in.port_def = port_def;
        } else if (port_def.eDir == OMX_DirOutput && ctx->out.index < 0) {
            ctx->out.index = port;
            ctx->out.port_def = port_def;
        }
    }
    if (ctx->in.index < 0 || ctx->out.index < 0) {
        av_log(avctx, AV_LOG_ERROR, "No in or out port found (in %d out %d)\n",
               ctx->in.index, ctx->out.index);
        return -1;
    }

    // try to set output port default color format to avctx->pix_fmt
    omx_codec_select_color_format(ctx);

#if CONFIG_OMX_IMG
    /* The following piece of code is to pass the information to OMX AVC or
       HEVC component that the CodecConfig data and Picture Data is NAL Size
       Delimited and will not contain NAL start codes(SCP- 0x00000001) */
    if (avctx->codec_id == AV_CODEC_ID_H264 ||
        avctx->codec_id == AV_CODEC_ID_HEVC) {
        OMX_SetConfig(ctx->handle, OMX_IndexImgDataIsNALSizeDelimited,
                      NULL);
    }
#endif /* CONFIG_OMX_IMG */

    if (omx_port_config(&ctx->in) < 0)
        return -1;
    if (omx_port_config(&ctx->out) < 0)
        return -1;

    return 0;
}

static int omx_port_buffers_alloc(OMXCodecPort *port)
{
    OMXCodecContext *ctx = port->ctx;
    AVCodecContext *avctx = ctx->avctx;
    int i, err = OMX_ErrorNone;

    for (i = 0; i < port->num_buffers && err == OMX_ErrorNone; i++) {
        OMXBufferHeader *h;

        h = av_mallocz(sizeof(*h));
        if (!h) {
            av_log(avctx, AV_LOG_ERROR,
                   "Port %d buffer allocation error: Not enough memory\n",
                   port->index);
            return -1;
        }
        if (port->port_def.eDir == OMX_DirInput) {
#if CONFIG_OMX_IMG
            /* hack: IMG implementation of OMX_UseBuffer returns error
                     if pBuffer is NULL */
            err = OMX_UseBuffer(ctx->handle, &h->header, port->index,
                                h, port->port_def.nBufferSize, (OMX_U8 *) 1);
            if (h->header)
                h->header->pBuffer = NULL;
#else
            err = OMX_UseBuffer(ctx->handle, &h->header, port->index,
                                h, port->port_def.nBufferSize, NULL);
#endif /* CONFIG_OMX_IMG */
        }
        else
            err = OMX_AllocateBuffer(ctx->handle, &h->header, port->index,
                                     h, port->port_def.nBufferSize);
        if (err != OMX_ErrorNone) {
            av_log(avctx, AV_LOG_ERROR, "Port %d buffer allocation error: %s\n",
                   port->index, omx_error_string(err));
            av_free(h);
            return -1;
        }
        h->port = port;
        h->next = port->headers;
        port->headers = h;
        atomic_fetch_add(&port->num_headers, 1);
    }

    return 0;
}

static av_cold int omx_wait_for_state(OMXCodecContext *ctx, OMX_STATETYPE state)
{
    struct timespec ts;
    int ret = 0;

    pthread_mutex_lock(&ctx->state_mutex);
    ctx->error = OMX_ErrorNone;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += OMX_STATE_TIMEOUT;
    while (ctx->state != state && ctx->error == OMX_ErrorNone && ret == 0)
        ret = pthread_cond_timedwait(&ctx->state_cond, &ctx->state_mutex, &ts);
    if (ctx->error != OMX_ErrorNone || ret != 0)
        ret = -1;
    pthread_mutex_unlock(&ctx->state_mutex);
    return ret;
}

static av_cold const char *omx_id_to_role(enum AVCodecID id)
{
    switch (id) {
    case AV_CODEC_ID_H264:
        return OMX_ROLE_VIDEO_DECODER_AVC;
    case AV_CODEC_ID_HEVC:
        return OMX_ROLE_VIDEO_DECODER_HEVC;
    case AV_CODEC_ID_MPEG4:
        return OMX_ROLE_VIDEO_DECODER_MPEG4;
    case AV_CODEC_ID_H263:
        return OMX_ROLE_VIDEO_DECODER_H263;
    case AV_CODEC_ID_MPEG2VIDEO:
        return OMX_ROLE_VIDEO_DECODER_MPEG2;
    case AV_CODEC_ID_MJPEG:
        return "video_decoder.mjpeg";
    case AV_CODEC_ID_VP8:
        return OMX_ROLE_VIDEO_DECODER_VP8;
    case AV_CODEC_ID_WMV3:
        return OMX_ROLE_VIDEO_DECODER_WMV;
    case AV_CODEC_ID_RV30:
    case AV_CODEC_ID_RV40:
        return OMX_ROLE_VIDEO_DECODER_RV;
#if CONFIG_OMX_IMG
    case AV_CODEC_ID_VC1:
        return OMX_ROLE_VIDEO_DECODER_VC1;
    case AV_CODEC_ID_FLV1:
        return OMX_ROLE_VIDEO_DECODER_SORENSON;
    case AV_CODEC_ID_VP6:
    case AV_CODEC_ID_VP6F:
    case AV_CODEC_ID_VP6A:
        return OMX_ROLE_VIDEO_DECODER_VP6;
    case AV_CODEC_ID_AVS:
        return "video_decoder.avs";
#endif /* CONFIG_OMX_IMG */
   }
    return NULL;
}

static av_cold int omx_decode_init(AVCodecContext *avctx)
{
    OMXCodecContext *ctx = avctx->priv_data;
    const char *role;
    OMX_ERRORTYPE err;

    role = omx_id_to_role(avctx->codec_id);
    if (!role) {
        av_log(avctx, AV_LOG_ERROR, "Unsupported codec id %d\n",
               avctx->codec_id);
        return AVERROR_DECODER_NOT_FOUND;
    }

    ctx->core = omx_core_init(avctx, ctx->libname);
    if (!ctx->core)
        return AVERROR_DECODER_NOT_FOUND;

    ctx->avctx = avctx;

    omx_port_init(ctx, &ctx->in);
    omx_port_init(ctx, &ctx->out);
    pthread_mutex_init(&ctx->out_queue_mutex, NULL);
    pthread_cond_init(&ctx->out_queue_cond, NULL);

    pthread_mutex_init(&ctx->state_mutex, NULL);
    pthread_cond_init(&ctx->state_cond, NULL);

    ctx->state = OMX_StateLoaded;
    ctx->error = OMX_ErrorNone;

#if CONFIG_OMX_IMG
    err = ctx->core->pComponentOfRoleEnum((OMX_STRING) ctx->component_name,
                                          (OMX_STRING) role, 0);
    if (err !=  OMX_ErrorNone) {
        av_log(avctx, AV_LOG_ERROR, "No component for role %s found: %s\n",
               role, omx_error_string(err));
        return AVERROR_DECODER_NOT_FOUND;
    }
#else
    if (omx_component_find(ctx, role,
                           ctx->component_name,
                           sizeof(ctx->component_name)) < 0)
        return AVERROR_DECODER_NOT_FOUND;
#endif /* CONFIG_OMX_IMG */

    av_log(avctx, AV_LOG_INFO, "Using %s\n", ctx->component_name);

    if (omx_component_init(ctx, role) < 0)
        return AVERROR_DECODER_NOT_FOUND;

    err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet, OMX_StateIdle,
                          NULL);
    if (err != OMX_ErrorNone) {
        av_log(ctx->avctx, AV_LOG_ERROR, "Set StateIdle error: %s\n",
               omx_error_string(err));
        return AVERROR_UNKNOWN;
    }
    if (omx_port_buffers_alloc(&ctx->in) < 0)
        return AVERROR_UNKNOWN;
    if (omx_port_buffers_alloc(&ctx->out) < 0)
        return AVERROR_UNKNOWN;
    if (omx_wait_for_state(ctx, OMX_StateIdle) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Did not get StateIdle\n");
        return AVERROR_UNKNOWN;
    }
    err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet, OMX_StateExecuting,
                          NULL);
    if (err != OMX_ErrorNone) {
        av_log(ctx->avctx, AV_LOG_ERROR, "Set StateExecuting error: %s\n",
               omx_error_string(err));
        return AVERROR_UNKNOWN;
    }
    if (omx_wait_for_state(ctx, OMX_StateExecuting) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Did not get StateExecuting\n");
        return AVERROR_UNKNOWN;
    }

    return 0;
}

static inline int64_t from_pts(AVRational timebase, int64_t value)
{
    if (value != AV_NOPTS_VALUE) {
        if (timebase.num > 0 && timebase.den > 0)
            return av_rescale_q(value, timebase, AV_TIME_BASE_Q);
        else
            return value;
    }
    return 0;
}

static inline int64_t to_pts(AVRational timebase, int64_t value)
{
    if (timebase.num > 0 && timebase.den > 0)
        return av_rescale_q(value, AV_TIME_BASE_Q, timebase);
    return value;
}

static int omx_add_packet(OMXCodecContext *ctx, AVPacket *avpkt,
                          int is_extradata)
{
    AVCodecContext *avctx = ctx->avctx;
    AVBufferRef *buf = NULL;
    int size = 0;
    uint8_t *data = NULL;
    int ret = 0;

    if (avpkt->size) {
        if (avpkt->buf) {
            buf = av_buffer_ref(avpkt->buf);
            size = avpkt->size;
            data = avpkt->data;
        } else {
            buf = av_buffer_alloc(avpkt->size);
            if (buf) {
                memcpy(buf->data, avpkt->data, buf->size);
                size = buf->size;
                data = buf->data;
            }
        }
        if (!buf) {
            av_log(avctx, AV_LOG_ERROR,
                   "Input queue add package error: Not enough memory\n");
            ret = AVERROR(ENOMEM);
            goto done;
        }
    }
    else if (ctx->eos_sent)
        goto done;

    do {
        OMXInputBuffer *buffer;

        buffer = av_mallocz(sizeof(*buffer));
        if (!buffer) {
            av_log(avctx, AV_LOG_ERROR,
                   "Input queue add package error: Not enough memory\n");
            ret = AVERROR(ENOMEM);
            goto done;
        }

        buffer->data = data;
        buffer->size = FFMIN(size, ctx->in.port_def.nBufferSize);
        buffer->pts = avpkt->pts;

        if (is_extradata)
            buffer->flags |= OMX_BUFFERFLAG_CODECCONFIG;

        data += buffer->size;
        size -= buffer->size;

        if (!size)
            buffer->flags |= OMX_BUFFERFLAG_ENDOFFRAME;

        if (!buffer->size) {
            buffer->flags |= OMX_BUFFERFLAG_EOS;
            ctx->eos_sent = 1;
        }

        if (buf) {
            buffer->ref = av_buffer_ref(buf);
            if (!buffer->ref) {
                av_log(avctx, AV_LOG_ERROR,
                       "Input queue add package error: Not enough memory\n");
                av_free(buffer);
                ret = AVERROR(ENOMEM);
                goto done;
            }
        }

        // add buffer to input queue
        if (!ctx->in_queue_head)
            ctx->in_queue_head = buffer;
        if (ctx->in_queue_tail)
            ctx->in_queue_tail->next = buffer;
        ctx->in_queue_tail = buffer;
        ctx->in_queue_size++;
    } while (size);

done:
    av_buffer_unref(&buf);
    return ret;
}

static int omx_fill_output_port(OMXCodecContext *ctx)
{
    OMXCodecPort *port = &ctx->out;
    OMXBufferHeader *h;
    int ret = 0;

    if (ctx->settings_changed)
        return 0;

    pthread_mutex_lock(&port->mutex);
    while ((h = port->headers)) {
        OMX_ERRORTYPE err;

        // prepare buffer header
        h->decoding = 1;
        // remove buffer header from pool
        port->headers = h->next;
        atomic_fetch_add(&port->num_headers, -1);
        /* deadlock could happens if FillThisBuffer called locked
           when FillBufferDone is waiting for lock */
        pthread_mutex_unlock(&port->mutex);
        err = OMX_FillThisBuffer(ctx->handle, h->header);
        pthread_mutex_lock(&port->mutex);
        if (err != OMX_ErrorNone) {
            omx_buffer_release(h);
            ret = AVERROR_UNKNOWN;
            av_log(ctx->avctx, AV_LOG_ERROR, "FillThisBuffer error: %s\n",
                   omx_error_string(err));
            break;
        }
    }
    pthread_mutex_unlock(&port->mutex);
    return ret;
}

static int omx_fill_input_port(OMXCodecContext *ctx)
{
    AVCodecContext *avctx = ctx->avctx;
    OMXCodecPort *port = &ctx->in;
    OMXInputBuffer *buffer;

    if (ctx->settings_changed)
        return 0;

    while ((buffer = ctx->in_queue_head)) {
        OMXBufferHeader *h;
        OMX_ERRORTYPE err;

        pthread_mutex_lock(&port->mutex);
        h = port->headers;
        if (!h) {
            pthread_mutex_unlock(&port->mutex);
            return 0;
        }
        // prepare buffer header
        h->priv = buffer;
        h->header->pBuffer = buffer->data;
        h->header->nOffset = 0;
        h->header->nFilledLen = buffer->size;
        h->header->nTimeStamp = to_omx_ticks(from_pts(avctx->pkt_timebase,
                                                      buffer->pts));
        h->header->nFlags = buffer->flags;
        h->decoding = 1;
        // remove buffer header from pool
        port->headers = h->next;
        atomic_fetch_add(&port->num_headers, -1);
        // remove buffer from input queue
        ctx->in_queue_head = buffer->next;
        if (ctx->in_queue_tail == buffer)
            ctx->in_queue_tail = NULL;
        ctx->in_queue_size--;
        /* deadlock could happens if EmptyThisBuffer called locked
           when EmptyBufferDone is waiting for lock */
        pthread_mutex_unlock(&port->mutex);
        err = OMX_EmptyThisBuffer(ctx->handle, h->header);
        if (err != OMX_ErrorNone) {
            pthread_mutex_lock(&port->mutex);
            omx_buffer_release(h);
            pthread_mutex_unlock(&port->mutex);
            av_log(avctx, AV_LOG_ERROR, "EmptyThisBuffer error: %s\n",
                   omx_error_string(err));
            return AVERROR_UNKNOWN;
        }
    }
    return 0;
}

static OMXBufferHeader *omx_get_output(OMXCodecContext *ctx, int wait)
{
    OMXBufferHeader *h = NULL;
    int ret = 0;

    pthread_mutex_lock(&ctx->out_queue_mutex);
    if (wait) {
        struct timespec ts;

        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += OMX_OUTPUT_TIMEOUT;
        /* Waiting until output buffer available or output port settings changed
           or input port buffer available or timeout occured */
        while (!ctx->out_queue_head && !ctx->settings_changed &&
               atomic_load(&ctx->in.num_headers) == 0 && ret == 0)
            ret = pthread_cond_timedwait(&ctx->out_queue_cond,
                                         &ctx->out_queue_mutex, &ts);
    }
    if (ret == 0 && !ctx->settings_changed && (h = ctx->out_queue_head)) {
        ctx->out_queue_head = h->next;
        if (ctx->out_queue_tail == h)
            ctx->out_queue_tail = NULL;
        ctx->out_queue_size--;
    }
    pthread_mutex_unlock(&ctx->out_queue_mutex);

    return h;
}

static int omx_read_frame(OMXCodecContext *ctx, AVFrame *frame, int *got_frame)
{
    AVCodecContext *avctx = ctx->avctx;
    OMXCodecPort *port = &ctx->out;
    int ret = 0;

    while (!*got_frame && ret == 0 && !ctx->eos_received) {
        OMXBufferHeader *h;
        int wait = ctx->eos_sent ||
                   ctx->in_queue_size >= OMX_INPUT_QUEUE_THRESHOLD;
        int eof;

        // if flushing or input queue exceeded, try blocking wait
        h = omx_get_output(ctx, wait);
        if (!h) {
            if (wait && !ctx->settings_changed &&
                atomic_load(&ctx->in.num_headers) == 0) {
                av_log(avctx, AV_LOG_VERBOSE, "Output data timeout\n");
                ret = AVERROR_UNKNOWN;
            }
            break;
        }

#if CONFIG_OMX_IMG
        eof = 1;
#else
        eof = h->header->nFlags & OMX_BUFFERFLAG_ENDOFFRAME;
#endif /* CONFIG_OMX_IMG */

        if (h->header->nFlags & OMX_BUFFERFLAG_EOS) {
            ctx->eos_received = 1;
        }

#if !CONFIG_OMX_IMG
        if (h->header->nFilledLen > 0 && (ctx->output_buf || !eof)) {
            if ((ret = av_reallocp(&ctx->output_buf,
                                   ctx->output_buf_size +
                                   h->header->nFilledLen)) < 0) {
                ctx->output_buf_size = 0;
                av_log(avctx, AV_LOG_ERROR,
                       "Output buffer allocation error: %d\n", ret);
                goto end;
            }
            memcpy(ctx->output_buf + ctx->output_buf_size,
                   h->header->pBuffer + h->header->nOffset,
                   h->header->nFilledLen);
            ctx->output_buf_size += h->header->nFilledLen;
        }
#endif /* !CONFIG_OMX_IMG */

        if (eof) {
            uint8_t *buf = NULL;
            int len = 0;
            uint8_t *src[4];
            int linesize[4];

#if !CONFIG_OMX_IMG
            if (ctx->output_buf) {
                buf = ctx->output_buf;
                len = ctx->output_buf_size;
            }
            else
#endif /* !CONFIG_OMX_IMG */
            if (h->header->nFilledLen > 0) {
                buf = h->header->pBuffer + h->header->nOffset;
                len = h->header->nFilledLen;
            }

            if (!buf || len == 0) {
                goto end;
            }

            if ((ret = ff_get_buffer(avctx, frame, 0)) == 0) {
                av_image_fill_arrays(src, linesize, buf,
#if CONFIG_OMX_IMG
                                     ctx->decoder_pix_fmt,
#else
                                     avctx->pix_fmt,
#endif /* CONFIG_OMX_IMG */
                                     ctx->stride, ctx->plane_size, 1);
#if CONFIG_OMX_IMG
                if (ctx->sws_ctx)
                    sws_scale(ctx->sws_ctx, (const uint8_t **) src, linesize,
                              0, avctx->height, frame->data, frame->linesize);
                else
#endif /* CONFIG_OMX_IMG */
                av_image_copy(frame->data, frame->linesize,
                              (const uint8_t **) src, linesize,
                              avctx->pix_fmt, avctx->width, avctx->height);

                frame->pts = to_pts(avctx->pkt_timebase,
                                    from_omx_ticks(h->header->nTimeStamp));
#if FF_API_PKT_PTS
FF_DISABLE_DEPRECATION_WARNINGS
                frame->pkt_pts = frame->pts;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
                frame->pkt_dts = AV_NOPTS_VALUE;

                *got_frame = 1;
            }
            else
                av_log(avctx, AV_LOG_ERROR,
                       "Frame buffer allocation error: %d\n", ret);
#if !CONFIG_OMX_IMG
            av_freep(&ctx->output_buf);
            ctx->output_buf_size = 0;
#endif /* !CONFIG_OMX_IMG */
        }
end:
        pthread_mutex_lock(&port->mutex);
        h->next = port->headers;
        port->headers = h;
        atomic_fetch_add(&port->num_headers, 1);
        pthread_mutex_unlock(&port->mutex);
    }

    return ret;
}

static void omx_port_free(OMXCodecPort *port)
{
    OMXBufferHeader *h;

    while ((h = port->headers)) {
        port->headers = h->next;
        atomic_fetch_add(&port->num_headers, -1);
        port->num_buffers--;
        OMX_FreeBuffer(port->ctx->handle, port->index, h->header);
        av_free(h);
    }
}

static av_cold int omx_wait_port_state(OMXCodecPort *port, int enabled)
{
    OMXCodecContext *ctx = port->ctx;
    struct timespec ts;
    int ret = 0;

    pthread_mutex_lock(&ctx->state_mutex);
    ctx->error = OMX_ErrorNone;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += OMX_STATE_TIMEOUT;
    omx_update_port_definition(port, NULL);
    while (!!port->port_def.bEnabled != !!enabled &&
           ctx->error == OMX_ErrorNone && ret == 0) {
        ret = pthread_cond_timedwait(&ctx->state_cond, &ctx->state_mutex, &ts);
        omx_update_port_definition(port, NULL);
    }
    if (ctx->error != OMX_ErrorNone || ret != 0)
        ret = -1;
    pthread_mutex_unlock(&ctx->state_mutex);
    return ret;
}

static int omx_change_settings(OMXCodecContext *ctx)
{
    AVCodecContext *avctx = ctx->avctx;
    OMXCodecPort *port = &ctx->out;
    int width, height;
#if CONFIG_OMX_IMG
    OMX_CONFIG_RECTTYPE crop_rect;
#endif /* CONFIG_OMX_IMG */
    OMX_ERRORTYPE err;

    /* For decoder we dealing with output port settings only */

    // disable port
    omx_update_port_definition(port, NULL);
    if (port->port_def.bEnabled) {
        err = OMX_SendCommand(ctx->handle, OMX_CommandPortDisable, port->index,
                              NULL);
        if (err != OMX_ErrorNone) {
            av_log(avctx, AV_LOG_ERROR, "Port %d disable error: %s\n",
                   port->index, omx_error_string(err));
            return AVERROR_UNKNOWN;
        }
    }
    // deallocate port buffers
    pthread_mutex_lock(&port->mutex);
    omx_port_free(port);
    pthread_mutex_unlock(&port->mutex);
    while (port->num_buffers > 0) {
        OMXBufferHeader *h;

        h = omx_get_output(ctx, 1);
        if (!h) {
            av_log(avctx, AV_LOG_VERBOSE, "Output data timeout\n");
            break;
        }
        port->num_buffers--;
        OMX_FreeBuffer(ctx->handle, port->index, h->header);
        av_free(h);
    }
    // wait for port disabled
    if (omx_wait_port_state(port, 0) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Port %d not disabled\n", port->index);
        return AVERROR_UNKNOWN;
    }
    // update port config
    omx_codec_select_color_format(ctx);
    if (omx_update_port_definition(port, &port->port_def) !=  OMX_ErrorNone)
        return AVERROR_UNKNOWN;
    port->num_buffers = port->port_def.nBufferCountActual;
    ctx->stride     = port->port_def.format.video.nStride;
    ctx->plane_size = port->port_def.format.video.nSliceHeight;
    width  = port->port_def.format.video.nFrameWidth;
    height = port->port_def.format.video.nFrameHeight;
#if CONFIG_OMX_IMG
    OMX_INIT_STRUCT(&crop_rect);
    err = OMX_GetConfig(ctx->handle, OMX_IndexConfigCommonOutputCrop,
                        &crop_rect);
    if (err == OMX_ErrorNone) {
        width  = crop_rect.nWidth;
        height = crop_rect.nHeight;
    }
    else
        av_log(avctx, AV_LOG_WARNING,
               "Can not get output port crop rectangle: %s\n",
               omx_error_string(err));
#endif /* CONFIG_OMX_IMG */
    if (ff_set_dimensions(avctx, width, height) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Can not set frame dimensions\n");
        return AVERROR_UNKNOWN;
    }
    omx_codec_set_output_format(ctx);
    if (avctx->pix_fmt == AV_PIX_FMT_NONE) {
        av_log(avctx, AV_LOG_ERROR, "Undefined output pixel format\n");
        return -1;
    }
    // enable port
    if (!port->port_def.bEnabled) {
        err = OMX_SendCommand(ctx->handle, OMX_CommandPortEnable, port->index,
                              NULL);
        if (err != OMX_ErrorNone) {
            av_log(avctx, AV_LOG_ERROR, "Port %d enable error: %s\n",
                   port->index, omx_error_string(err));
            return AVERROR_UNKNOWN;
        }
    }
    // allocate port buffers
    if (omx_port_buffers_alloc(port) < 0)
        return AVERROR_UNKNOWN;
    // wait for port enabled
    if (omx_wait_port_state(port, 1) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Port %d not enabled\n", port->index);
        return AVERROR_UNKNOWN;
    }
    return 0;
}

static int omx_decode(AVCodecContext *avctx, void *data, int *got_frame,
                              AVPacket *avpkt)
{
    OMXCodecContext *ctx = avctx->priv_data;
    AVFrame *frame = data;
    int ret = 0;

    if (avctx->extradata_size && !ctx->extradata_sent) {
        AVPacket pkt = {0};

        av_init_packet(&pkt);
        pkt.data = avctx->extradata;
        pkt.size = avctx->extradata_size;
        ctx->extradata_sent = 1;
        if ((ret = omx_add_packet(ctx, &pkt, 1)) < 0)
            return ret;
    }

    if ((ret = omx_add_packet(ctx, avpkt, 0)) < 0)
        return ret;

    if ((ret = omx_fill_output_port(ctx)) < 0)
        return ret;

    if ((ret = omx_fill_input_port(ctx)) < 0)
        return ret;

    if ((ret = omx_read_frame(ctx, frame, got_frame)) < 0)
        return ret;

    if (ctx->settings_changed) {
        ctx->settings_changed = 0;
        if ((ret = omx_change_settings(ctx)) < 0)
            return ret;
    }

    if ((ret = omx_fill_output_port(ctx)) < 0)
        return ret;

    if ((ret = omx_fill_input_port(ctx)) < 0)
        return ret;

    return 0;
}

static int omx_wait_flushed(OMXCodecContext *ctx)
{
    struct timespec ts;
    int ret = 0;

    pthread_mutex_lock(&ctx->state_mutex);
    ctx->error = OMX_ErrorNone;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += OMX_STATE_TIMEOUT;
    while (ctx->flushing && ctx->error == OMX_ErrorNone && ret == 0) {
        ret = pthread_cond_timedwait(&ctx->state_cond, &ctx->state_mutex, &ts);
    }
    if (ctx->error != OMX_ErrorNone || ret != 0)
        ret = -1;
    pthread_mutex_unlock(&ctx->state_mutex);
    return ret;
}

static void omx_input_queue_free(OMXCodecContext *ctx)
{
    OMXInputBuffer *buffer;

    while ((buffer = ctx->in_queue_head)) {
        ctx->in_queue_head = buffer->next;
        if (ctx->in_queue_tail == buffer)
            ctx->in_queue_tail = NULL;
        ctx->in_queue_size--;
        av_buffer_unref(&buffer->ref);
        av_free(buffer);
    }
}

static void omx_flush(AVCodecContext *avctx)
{
    OMXCodecContext *ctx = avctx->priv_data;
    OMXBufferHeader *h;
    OMX_STATETYPE state;
    OMX_ERRORTYPE err;

    pthread_mutex_lock(&ctx->state_mutex);
    state = ctx->state;
    pthread_mutex_unlock(&ctx->state_mutex);

    // pause component
    if (state == OMX_StateExecuting) {
        err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet,
                              OMX_StatePause, NULL);
        if (err != OMX_ErrorNone)
            av_log(ctx->avctx, AV_LOG_ERROR, "Set StatePause error: %s\n",
                   omx_error_string(err));
    }
    // input queue free
    omx_input_queue_free(ctx);
    ctx->in_queue_head = ctx->in_queue_tail = NULL;
    ctx->in_queue_size = 0;
    // flush input port
    ctx->flushing = 1;
    err = OMX_SendCommand(ctx->handle, OMX_CommandFlush, ctx->in.index, NULL);
    if (err != OMX_ErrorNone) {
        av_log(avctx, AV_LOG_ERROR, "Input port flush error: %s\n",
               omx_error_string(err));
        ctx->flushing = 0;
    }
    if (omx_wait_flushed(ctx) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Input port not flushed\n");
        ctx->flushing = 0;
    }
    // flush output port
    ctx->flushing = 1;
    err = OMX_SendCommand(ctx->handle, OMX_CommandFlush, ctx->out.index, NULL);
    if (err != OMX_ErrorNone) {
        av_log(avctx, AV_LOG_ERROR, "Output port flush error: %s\n",
               omx_error_string(err));
        ctx->flushing = 0;
    }
    if (omx_wait_flushed(ctx) < 0) {
        av_log(avctx, AV_LOG_ERROR, "Output port not flushed\n");
        ctx->flushing = 0;
    }
    // output queue free
    pthread_mutex_lock(&ctx->out.mutex);
    pthread_mutex_lock(&ctx->out_queue_mutex);
    while ((h = ctx->out_queue_head)) {
        ctx->out_queue_head = h->next;
        if (ctx->out_queue_tail == h)
            ctx->out_queue_tail = NULL;
        ctx->out_queue_size--;
        h->next = ctx->out.headers;
        ctx->out.headers = h;
        atomic_fetch_add(&ctx->out.num_headers, 1);
    }
    ctx->out_queue_head = ctx->out_queue_tail = NULL;
    ctx->out_queue_size = 0;
    pthread_mutex_unlock(&ctx->out_queue_mutex);
    pthread_mutex_unlock(&ctx->out.mutex);
    // resume component
    err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet, OMX_StateExecuting,
                          NULL);
    if (err != OMX_ErrorNone)
        av_log(ctx->avctx, AV_LOG_ERROR, "Set StateExecuting error: %s\n",
               omx_error_string(err));
    // reset EOS flag
    ctx->eos_sent = ctx->eos_received = 0;
    // fill output port
    omx_fill_output_port(ctx);
}

static av_cold void omx_port_destroy(OMXCodecPort *port)
{
    pthread_mutex_destroy(&port->mutex);
    port->num_buffers = 0;
    atomic_store(&port->num_headers, 0);
    port->headers = NULL;
}

static av_cold void omx_output_queue_free(OMXCodecContext *ctx)
{
    OMXBufferHeader *h;

    while ((h = ctx->out_queue_head)) {
        ctx->out_queue_head = h->next;
        if (ctx->out_queue_tail == h)
            ctx->out_queue_tail = NULL;
        ctx->out_queue_size--;
        ctx->out.num_buffers--;
        OMX_FreeBuffer(ctx->handle, ctx->out.index, h->header);
        av_free(h);
    }
}

static av_cold void omx_decode_stop(OMXCodecContext *ctx)
{
    omx_port_free(&ctx->in);
    omx_input_queue_free(ctx);
    omx_port_free(&ctx->out);
    omx_output_queue_free(ctx);
}

static av_cold int omx_decode_close(AVCodecContext *avctx)
{
    OMXCodecContext *ctx = avctx->priv_data;
    OMX_STATETYPE state;

    pthread_mutex_lock(&ctx->state_mutex);
    state = ctx->state;
    pthread_mutex_unlock(&ctx->state_mutex);

    if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
        OMX_ERRORTYPE err;

        if (state > OMX_StateIdle) {
            err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet,
                                  OMX_StateIdle, NULL);
            if (err != OMX_ErrorNone)
                av_log(ctx->avctx, AV_LOG_ERROR, "Set StateIdle error: %s\n",
                       omx_error_string(err));
            if (omx_wait_for_state(ctx, OMX_StateIdle) < 0)
                av_log(avctx, AV_LOG_ERROR, "Did not get StateIdle\n");
        }
        err = OMX_SendCommand(ctx->handle, OMX_CommandStateSet, OMX_StateLoaded,
                              NULL);
        if (err != OMX_ErrorNone)
            av_log(ctx->avctx, AV_LOG_ERROR, "Set StateLoaded error: %s\n",
                   omx_error_string(err));
        omx_decode_stop(ctx);
        if (omx_wait_for_state(ctx, OMX_StateLoaded) < 0)
            av_log(avctx, AV_LOG_ERROR, "Did not get StateLoaded\n");
    }

    if (ctx->handle) {
        ctx->core->pFreeHandle(ctx->handle);
        ctx->handle = NULL;
    }

    omx_core_destroy(ctx->core);
    ctx->core = NULL;

    pthread_cond_destroy(&ctx->state_cond);
    pthread_mutex_destroy(&ctx->state_mutex);

    ctx->in_queue_head = ctx->in_queue_tail = NULL;
    ctx->in_queue_size = 0;

    ctx->out_queue_head = ctx->out_queue_tail = NULL;
    ctx->out_queue_size = 0;
    pthread_cond_destroy(&ctx->out_queue_cond);
    pthread_mutex_destroy(&ctx->out_queue_mutex);
#if !CONFIG_OMX_IMG
    av_freep(&ctx->output_buf);
    ctx->output_buf_size = 0;
#endif /* !CONFIG_OMX_IMG */
    ctx->settings_changed = 0;
#if CONFIG_OMX_IMG
    sws_freeContext(ctx->sws_ctx);
    ctx->sws_ctx = NULL;
    ctx->decoder_pix_fmt = AV_PIX_FMT_NONE;
#endif /* CONFIG_OMX_IMG */

    omx_port_destroy(&ctx->in);
    omx_port_destroy(&ctx->out);

    ctx->eos_sent = ctx->eos_received = ctx->extradata_sent = 0;

    return 0;
}

#define OFFSET(x) offsetof(OMXCodecContext, x)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
    { "omx_libname", "OpenMAX library name", OFFSET(libname),
      AV_OPT_TYPE_STRING, { 0 }, 0, 0, FLAGS },
    { NULL }
};

static const enum AVPixelFormat omx_decoder_pix_fmts[] = {
    AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE
};

#define OMXDEC_CLASS(NAME) \
    static const AVClass omx_ ## NAME ## _dec_class = { \
        .class_name = #NAME "_omx_decoder", \
        .item_name  = av_default_item_name, \
        .option     = options, \
        .version    = LIBAVUTIL_VERSION_INT, \
    };

#define OMXDEC(NAME, LONGNAME, ID) \
    OMXDEC_CLASS(NAME) \
    AVCodec ff_ ## NAME ## _omx_decoder = { \
        .name           = #NAME "_omx" , \
        .long_name      = NULL_IF_CONFIG_SMALL("OpenMAX " LONGNAME " decoder"),\
        .type           = AVMEDIA_TYPE_VIDEO, \
        .id             = ID , \
        .priv_data_size = sizeof(OMXCodecContext), \
        .init           = omx_decode_init, \
        .decode         = omx_decode, \
        .flush          = omx_flush, \
        .close          = omx_decode_close, \
        .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, \
        .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | \
                          FF_CODEC_CAP_INIT_CLEANUP, \
        .pix_fmts       = omx_decoder_pix_fmts, \
        .wrapper_name   = "omx", \
    }

OMXDEC(h264,     "H.264",          AV_CODEC_ID_H264);
OMXDEC(hevc,     "H.265",          AV_CODEC_ID_HEVC);
OMXDEC(mpeg4,    "MPEG4",          AV_CODEC_ID_MPEG4);
OMXDEC(h263,     "H.263",          AV_CODEC_ID_H263);
OMXDEC(mpeg2,    "MPEG2",          AV_CODEC_ID_MPEG2VIDEO);
OMXDEC(mjpeg,    "MJPEG",          AV_CODEC_ID_MJPEG);
OMXDEC(vp8,      "VP8",            AV_CODEC_ID_VP8);
OMXDEC(wmv3,     "WMV3",           AV_CODEC_ID_WMV3);
OMXDEC(rv30,     "RealVideo 3.0",  AV_CODEC_ID_RV30);
OMXDEC(rv40,     "RealVideo 4.0",  AV_CODEC_ID_RV40);
#if CONFIG_OMX_IMG
OMXDEC(vc1,      "VC1",            AV_CODEC_ID_VC1);
OMXDEC(sorenson, "Sorenson",       AV_CODEC_ID_FLV1);
OMXDEC(vp6,      "VP6",            AV_CODEC_ID_VP6);
OMXDEC(vp6f,     "VP6F",           AV_CODEC_ID_VP6F);
OMXDEC(vp6a,     "VP6A",           AV_CODEC_ID_VP6A);
OMXDEC(avs,      "AVS",            AV_CODEC_ID_AVS);
#endif /* CONFIG_OMX_IMG */