Quellcode durchsuchen

Merge tag 'JH7110_MM_v5.5.0' into vf2-devel

Andy Hu vor 9 Monaten
Ursprung
Commit
301bd5b854

+ 1 - 0
package/busybox/blacklist.conf

@@ -2,3 +2,4 @@ blacklist xrp
 blacklist e24
 blacklist starfive_mailbox_test
 blacklist starfive_mailbox
+blacklist wave5

+ 155 - 0
package/ffmpeg/0022-omxdec-optimize-with-omx-zero-copy-feature.patch

@@ -0,0 +1,155 @@
+From 640ea14c3ca4536dce22a24153ac37d752009412 Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 11:38:04 +0800
+Subject: [PATCH 1/8] omxdec optimize with omx zero copy feature
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 63 ++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 48 insertions(+), 15 deletions(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index 1bb08b1..c518542 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -342,6 +342,8 @@ typedef struct OMXCodecContext {
+     pthread_cond_t input_cond;
+     pthread_mutex_t output_mutex;
+     pthread_cond_t output_cond;
++    pthread_mutex_t disableEVnt_mutex;
++    pthread_cond_t disableEVnt_cond;
+ 
+     pthread_mutex_t state_mutex;
+     pthread_cond_t state_cond;
+@@ -419,7 +421,7 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+     OMX_PARAM_PORTDEFINITIONTYPE out_port_params = { 0 };
+     OMX_PORT_PARAM_TYPE video_port_params = { 0 };
+     OMX_ERRORTYPE err;
+-    int i;
++    int i, num;
+ 
+     switch (event) {
+     case OMX_EventError:
+@@ -438,6 +440,11 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+             pthread_mutex_unlock(&s->state_mutex);
+         } else if (data1 == OMX_CommandPortDisable) {
+             av_log(s->avctx, AV_LOG_VERBOSE, "OMX port %"PRIu32" disabled\n", (uint32_t) data2);
++            if (data2 == 1) {
++                pthread_mutex_lock(&s->disableEVnt_mutex);
++                pthread_cond_broadcast(&s->disableEVnt_cond);
++                pthread_mutex_unlock(&s->disableEVnt_mutex);
++            }
+         } else if (data1 == OMX_CommandPortEnable) {
+             av_log(s->avctx, AV_LOG_VERBOSE, "OMX port %"PRIu32" enabled\n", (uint32_t) data2);
+         } else {
+@@ -470,6 +477,32 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+                 dec_pix_fmt = out_port_params.format.video.eColorFormat;
+ 
+                 av_log(s->avctx, AV_LOG_VERBOSE, "w:%d, h:%d, fmt:%d\n", dec_out_width, dec_out_height, dec_pix_fmt); 
++                if (!s->num_out_buffers) {
++                    s->num_out_buffers = out_port_params.nBufferCountActual;
++                    s->out_buffer_headers = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
++                    s->done_out_buffers   = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
++
++                    if (!s->out_buffer_headers || !s->done_out_buffers)
++                        return AVERROR(ENOMEM);
++
++                    OMX_SendCommand(s->handle, OMX_CommandPortEnable, 1, NULL);
++
++                    for (num = 0; num < s->num_out_buffers && err == OMX_ErrorNone; num++)
++                        err = OMX_AllocateBuffer(s->handle, &s->out_buffer_headers[num], s->out_port, s, out_port_params.nBufferSize);
++
++                    if (err != OMX_ErrorNone) {
++                        av_log(s->avctx, AV_LOG_ERROR,  "err %x (%d) on line %d\n", err, err, __LINE__);
++                        return AVERROR_UNKNOWN;
++                    }
++                    s->num_out_buffers = num;
++
++                    for (num = 0; num < s->num_out_buffers && err == OMX_ErrorNone; num++)
++                        err = OMX_FillThisBuffer(s->handle, s->out_buffer_headers[num]);
++                    if (err != OMX_ErrorNone) {
++                        for (; num < s->num_out_buffers; num++)
++                            s->done_out_buffers[s->num_done_out_buffers++] = s->out_buffer_headers[num];
++                    }
++                }
+             } 
+         }
+         break;
+@@ -866,15 +899,18 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role)
+     CHECK(err);
+     err = OMX_GetParameter(s->handle, OMX_IndexParamPortDefinition, &out_port_params);
+     CHECK(err);
+-    s->num_out_buffers = out_port_params.nBufferCountActual;
++
++    OMX_SendCommand(s->handle, OMX_CommandPortDisable, 1, NULL);
++    pthread_mutex_lock(&s->disableEVnt_mutex);
++    pthread_cond_wait(&s->disableEVnt_cond, &s->disableEVnt_mutex);
++    pthread_mutex_unlock(&s->disableEVnt_mutex);
+ 
+     err = OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
+     CHECK(err);
+     s->in_buffer_headers  = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_in_buffers);
+     s->free_in_buffers    = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_in_buffers);
+-    s->out_buffer_headers = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
+-    s->done_out_buffers   = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
+-    if (!s->in_buffer_headers || !s->free_in_buffers || !s->out_buffer_headers || !s->done_out_buffers)
++
++    if (!s->in_buffer_headers || !s->free_in_buffers)
+         return AVERROR(ENOMEM);
+ 
+     for (i = 0; i < s->num_in_buffers && err == OMX_ErrorNone; i++) {
+@@ -887,10 +923,7 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role)
+     }
+     CHECK(err);
+     s->num_in_buffers = i;
+-    for (i = 0; i < s->num_out_buffers && err == OMX_ErrorNone; i++)
+-        err = OMX_AllocateBuffer(s->handle, &s->out_buffer_headers[i], s->out_port, s, out_port_params.nBufferSize);
+-    CHECK(err);
+-    s->num_out_buffers = i;
++
+     if (wait_for_state(s, OMX_StateIdle) < 0) {
+         av_log(avctx, AV_LOG_ERROR, "Didn't get OMX_StateIdle\n");
+         return AVERROR_UNKNOWN;
+@@ -901,12 +934,7 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role)
+         av_log(avctx, AV_LOG_ERROR, "Didn't get OMX_StateExecuting\n");
+         return AVERROR_UNKNOWN;
+     }
+-    for (i = 0; i < s->num_out_buffers && err == OMX_ErrorNone; i++)
+-        err = OMX_FillThisBuffer(s->handle, s->out_buffer_headers[i]);
+-    if (err != OMX_ErrorNone) {
+-        for (; i < s->num_out_buffers; i++)
+-            s->done_out_buffers[s->num_done_out_buffers++] = s->out_buffer_headers[i];
+-    }
++
+     for (i = 0; i < s->num_in_buffers; i++)
+         s->free_in_buffers[s->num_free_in_buffers++] = s->in_buffer_headers[i];
+     return err != OMX_ErrorNone ? AVERROR_UNKNOWN : 0;
+@@ -952,6 +980,8 @@ static av_cold void cleanup(OMXCodecContext *s)
+         pthread_mutex_destroy(&s->input_mutex);
+         pthread_cond_destroy(&s->output_cond);
+         pthread_mutex_destroy(&s->output_mutex);
++        pthread_cond_destroy(&s->disableEVnt_cond);
++        pthread_mutex_destroy(&s->disableEVnt_mutex);
+         s->mutex_cond_inited = 0;
+     }
+     OMXDecodeQueueDestory(&s->decode_pts_queue);
+@@ -984,6 +1014,8 @@ static av_cold int omx_decode_init(AVCodecContext *avctx)
+     pthread_cond_init(&s->input_cond, NULL);
+     pthread_mutex_init(&s->output_mutex, NULL);
+     pthread_cond_init(&s->output_cond, NULL);
++    pthread_mutex_init(&s->disableEVnt_mutex, NULL);
++    pthread_cond_init(&s->disableEVnt_cond, NULL);
+     s->mutex_cond_inited = 1;
+     s->avctx = avctx;
+     s->state = OMX_StateLoaded;
+@@ -1009,6 +1041,7 @@ static av_cold int omx_decode_init(AVCodecContext *avctx)
+ 
+     if ((ret = find_component(s->omx_context, avctx, role, s->component_name, sizeof(s->component_name))) < 0)
+         goto fail;
++    strcat(s->component_name, ".internal");
+ 
+     av_log(avctx, AV_LOG_INFO, "Using %s\n", s->component_name);
+ 
+-- 
+2.25.1
+

+ 252 - 0
package/ffmpeg/0023-omxdec-add-output-buffers-release-callback-to-reduce.patch

@@ -0,0 +1,252 @@
+From 0f054a4a66e82d8f644acb8bf2a0b637cfee9aa8 Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 11:52:49 +0800
+Subject: [PATCH 2/8] omxdec : add output buffers release callback to reduce
+ memcpy
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/allcodecs.c |   8 +--
+ libavcodec/omxdec.c    | 131 +++++++++++++++++++++++++++++++++++------
+ 2 files changed, 116 insertions(+), 23 deletions(-)
+
+diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
+index 69627df..cb5b3d7 100644
+--- a/libavcodec/allcodecs.c
++++ b/libavcodec/allcodecs.c
+@@ -29,6 +29,10 @@
+ #include "avcodec.h"
+ #include "version.h"
+ 
++extern AVCodec ff_h264_omx_decoder;
++extern AVCodec ff_hevc_omx_encoder;
++extern AVCodec ff_hevc_omx_decoder;
++extern AVCodec ff_mjpeg_omx_decoder;
+ extern AVCodec ff_a64multi_encoder;
+ extern AVCodec ff_a64multi5_encoder;
+ extern AVCodec ff_aasc_decoder;
+@@ -792,9 +796,6 @@ extern AVCodec ff_h264_cuvid_decoder;
+ extern AVCodec ff_h264_mf_encoder;
+ extern AVCodec ff_h264_nvenc_encoder;
+ extern AVCodec ff_h264_omx_encoder;
+-extern AVCodec ff_h264_omx_decoder;
+-extern AVCodec ff_hevc_omx_encoder;
+-extern AVCodec ff_hevc_omx_decoder;
+ extern AVCodec ff_h264_qsv_encoder;
+ extern AVCodec ff_h264_v4l2m2m_encoder;
+ extern AVCodec ff_h264_vaapi_encoder;
+@@ -817,7 +818,6 @@ extern AVCodec ff_libkvazaar_encoder;
+ extern AVCodec ff_mjpeg_cuvid_decoder;
+ extern AVCodec ff_mjpeg_qsv_encoder;
+ extern AVCodec ff_mjpeg_qsv_decoder;
+-extern AVCodec ff_mjpeg_omx_decoder;
+ extern AVCodec ff_mjpeg_vaapi_encoder;
+ extern AVCodec ff_mp3_mf_encoder;
+ extern AVCodec ff_mpeg1_cuvid_decoder;
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index c518542..afcba9b 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -34,6 +34,7 @@
+ #include <stdlib.h>
+ #include <sys/time.h>
+ 
++#include "libavcodec/decode.h"
+ #include "libavutil/avstring.h"
+ #include "libavutil/avutil.h"
+ #include "libavutil/common.h"
+@@ -355,6 +356,7 @@ typedef struct OMXCodecContext {
+     int eos_sent, got_eos, evnet_bufferflag, first_get_outbuffer;
+ 
+     int extradata_sent;
++    int has_cleanup;
+ 
+     uint8_t *output_buf;
+     int output_buf_size;
+@@ -943,6 +945,7 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role)
+ static av_cold void cleanup(OMXCodecContext *s)
+ {
+     int i, executing;
++    OMX_BUFFERHEADERTYPE *buffer;
+ 
+     pthread_mutex_lock(&s->state_mutex);
+     executing = s->state == OMX_StateExecuting;
+@@ -953,17 +956,24 @@ static av_cold void cleanup(OMXCodecContext *s)
+         wait_for_state(s, OMX_StateIdle);
+         OMX_SendCommand(s->handle, OMX_CommandStateSet, OMX_StateLoaded, NULL);
+         for (i = 0; i < s->num_in_buffers; i++) {
+-            OMX_BUFFERHEADERTYPE *buffer = get_buffer(&s->input_mutex, &s->input_cond,
++            buffer = get_buffer(&s->input_mutex, &s->input_cond,
+                                                       &s->num_free_in_buffers, s->free_in_buffers, 1);
+             if (s->input_zerocopy)
+                 buffer->pBuffer = NULL;
+             OMX_FreeBuffer(s->handle, s->in_port, buffer);
+         }
+-        for (i = 0; i < s->num_out_buffers; i++) {
+-            OMX_BUFFERHEADERTYPE *buffer = get_buffer(&s->output_mutex, &s->output_cond,
+-                                                      &s->num_done_out_buffers, s->done_out_buffers, 1);
++
++        while (buffer = get_buffer(&s->output_mutex, &s->output_cond,
++                    &s->num_done_out_buffers, s->done_out_buffers, 0)) {
+             OMX_FreeBuffer(s->handle, s->out_port, buffer);
++            s->num_out_buffers--;
++        }
++
++        if (s->num_out_buffers) {
++            s->has_cleanup = 1;
++            return;
+         }
++
+         wait_for_state(s, OMX_StateLoaded);
+     }
+     if (s->handle) {
+@@ -1099,6 +1109,94 @@ fail:
+     return ret;
+ }
+ 
++static void omx_free_out_buffer(void *opaque, uint8_t *unused)
++{
++    OMX_ERRORTYPE err;
++    OMX_BUFFERHEADERTYPE *buffer = opaque;
++    OMXCodecContext *s = buffer->pAppPrivate;
++
++    if (!s->has_cleanup) {
++        err = OMX_FillThisBuffer(s->handle, buffer);
++        if (err != OMX_ErrorNone) {
++            append_buffer(&s->output_mutex, &s->output_cond, &s->num_done_out_buffers, s->done_out_buffers, buffer);
++            av_log(s->avctx, AV_LOG_ERROR, "OMX_FillThisBuffer failed, err: %x\n", err);
++        }
++    } else {
++        OMX_FreeBuffer(s->handle, s->out_port, buffer);
++        s->num_out_buffers--;
++        if (!s->num_out_buffers) {
++            wait_for_state(s, OMX_StateLoaded);
++            if (s->handle) {
++                s->omx_context->ptr_FreeHandle(s->handle);
++                s->handle = NULL;
++            }
++
++            omx_deinit(s->omx_context);
++            s->omx_context = NULL;
++            if (s->mutex_cond_inited) {
++                pthread_cond_destroy(&s->state_cond);
++                pthread_mutex_destroy(&s->state_mutex);
++                pthread_cond_destroy(&s->input_cond);
++                pthread_mutex_destroy(&s->input_mutex);
++                pthread_cond_destroy(&s->output_cond);
++                pthread_mutex_destroy(&s->output_mutex);
++                pthread_cond_destroy(&s->disableEVnt_cond);
++                pthread_mutex_destroy(&s->disableEVnt_mutex);
++                s->mutex_cond_inited = 0;
++            }
++            OMXDecodeQueueDestory(&s->decode_pts_queue);
++            av_freep(&s->decode_pts_queue);
++            av_freep(&s->in_buffer_headers);
++            av_freep(&s->out_buffer_headers);
++            av_freep(&s->free_in_buffers);
++            av_freep(&s->done_out_buffers);
++            av_freep(&s->output_buf);
++        }
++    }
++}
++
++static int omx_buf_to_swframe(OMXCodecContext *s, AVFrame *frame, OMX_BUFFERHEADERTYPE *buffer)
++{
++    int ret;
++    uint8_t *dst[4];
++    int linesize[4];
++
++    frame->buf[0] = av_buffer_create((char *)buffer->pBuffer, buffer->nFilledLen, omx_free_out_buffer, buffer, 0);
++
++    if (!frame->buf[0])
++        return AVERROR(ENOMEM);
++
++    ret = av_image_fill_arrays(dst, linesize, buffer->pBuffer,
++                               s->avctx->pix_fmt, s->stride, s->plane_size, 1);
++    if (ret < 0){
++        av_log(s->avctx, AV_LOG_ERROR, "av_image_fill_arrays ret:%d\n", ret);
++        return AVERROR(EINVAL);
++    }
++
++    frame->linesize[0] = linesize[0];
++    frame->data[0] = frame->buf[0]->data;
++
++    /* fixup special cases */
++    switch (s->avctx->pix_fmt) {
++    case AV_PIX_FMT_NV12:
++    case AV_PIX_FMT_NV21:
++        frame->linesize[1] = linesize[1];
++        frame->data[1] = dst[1];
++        break;
++
++    case AV_PIX_FMT_YUV420P:
++        frame->linesize[1] = linesize[1];
++        frame->linesize[2] = linesize[2];
++        frame->data[1] = dst[1];
++        frame->data[2] = dst[2];
++        break;
++
++    default:
++        break;
++    }
++
++    return 0;
++}
+ 
+ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+                            int *got_packet, AVPacket *pkt)
+@@ -1111,9 +1209,6 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+ 
+     AVFrame *avframe = data;
+ 
+-    uint8_t *dst[4];
+-    int linesize[4];
+-
+     av_log(avctx, AV_LOG_VERBOSE, "s->decode_flag: %d\n", s->decode_flag);
+     av_log(avctx, AV_LOG_VERBOSE, "avctx->time_base: %d/%d \n", avctx->time_base.num, avctx->time_base.den);
+     av_log(avctx, AV_LOG_VERBOSE, "avctx->pkt_timebase: %d/%d \n", avctx->pkt_timebase.num, avctx->pkt_timebase.den);
+@@ -1226,20 +1321,21 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+         if (buffer->nFlags & OMX_BUFFERFLAG_EOS)
+             s->got_eos = 1;
+ 
+-        if ((ret = ff_get_buffer(avctx, avframe, 0)) < 0) {
+-            av_log(avctx, AV_LOG_ERROR, "Unable to allocate buffer\n");
++        avframe->width  = avctx->width;
++        avframe->height = avctx->height;
++
++        ret = ff_decode_frame_props(avctx, avframe);
++        if(ret < 0) {
++            av_log(avctx, AV_LOG_ERROR, "Unable to fill buffer props\n");
+             goto end;
+         }
+ 
+-        ret = av_image_fill_arrays(dst, linesize, buffer->pBuffer,
+-                                   avctx->pix_fmt, s->stride, s->plane_size, 1);
+-        if (ret < 0){
+-            av_log(avctx, AV_LOG_ERROR, "av_image_fill_arrays ret:%d\n", ret);
++        ret = omx_buf_to_swframe(s, avframe, buffer);
++        if(ret < 0) {
++            av_log(avctx, AV_LOG_ERROR, "Unable to alloce frame\n");
+             goto end;
+         }
+ 
+-        av_image_copy(avframe->data, avframe->linesize, (const uint8_t**)dst, linesize, 
+-                            avctx->pix_fmt, avctx->width, avctx->height);
+         if (pkt->pts) {
+             if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
+                 av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
+@@ -1251,10 +1347,7 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+         s->decode_flag += 1;
+         *got_packet = 1;
+ 
+-        /*
+-        if ((ret = av_frame_ref(data, avframe)) < 0)
+-            goto end;
+-        */
++        return ret;
+ 
+ end:     
+         err = OMX_FillThisBuffer(s->handle, buffer);
+-- 
+2.25.1
+

+ 34 - 0
package/ffmpeg/0024-Fix-omx-decoder-fill-frames-pts-incorrect.patch

@@ -0,0 +1,34 @@
+From e0e6de3a210675444850c826bf5611b8d29228bf Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 13:47:48 +0800
+Subject: [PATCH 3/8] Fix omx decoder fill frames pts incorrect
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index afcba9b..f71e4bf 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -1217,7 +1217,15 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+                                      pkt->size, pkt->pts, pkt->dts, pkt->duration);
+     av_log(avctx, AV_LOG_VERBOSE, "avctx->pts_correction_last_pts: %ld avctx->pts_correction_last_dts: %ld\n",
+                                      avctx->pts_correction_last_pts, avctx->pts_correction_last_dts);
+-    OMXDecodeQueuePush(&s->decode_pts_queue, pkt->dts);
++    if (pkt->dts != AV_NOPTS_VALUE) {
++        OMXDecodeQueuePush(&s->decode_pts_queue, pkt->dts);
++    } else {
++        if (OMXDecodeQueueEmpty(&s->decode_pts_queue)) {
++            OMXDecodeQueuePush(&s->decode_pts_queue, 0);
++        } else {
++            OMXDecodeQueuePush(&s->decode_pts_queue, s->decode_pts_queue.tail->val + pkt->duration);
++        }
++    }
+     if (pkt->size) {
+ 
+         //VPU init and fill buffer slow, so empty buf sleep to send before get vpu fill buf.
+-- 
+2.25.1
+

+ 106 - 0
package/ffmpeg/0025-Omxdec-Fix-mjpg_omx-buffer-err.patch

@@ -0,0 +1,106 @@
+From e3866de00ce3b6508c7636c9f96b1b1dfeab530a Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 13:50:10 +0800
+Subject: [PATCH 4/8] Omxdec: Fix mjpg_omx buffer err
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 77 ++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 55 insertions(+), 22 deletions(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index f71e4bf..05e37d0 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -1329,33 +1329,66 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+         if (buffer->nFlags & OMX_BUFFERFLAG_EOS)
+             s->got_eos = 1;
+ 
+-        avframe->width  = avctx->width;
+-        avframe->height = avctx->height;
++        if (avctx->codec->id == AV_CODEC_ID_HEVC ||
++                avctx->codec->id == AV_CODEC_ID_H264) {
++            avframe->width  = avctx->width;
++            avframe->height = avctx->height;
++
++            ret = ff_decode_frame_props(avctx, avframe);
++            if(ret < 0) {
++                av_log(avctx, AV_LOG_ERROR, "Unable to fill buffer props\n");
++                goto end;
++            }
+ 
+-        ret = ff_decode_frame_props(avctx, avframe);
+-        if(ret < 0) {
+-            av_log(avctx, AV_LOG_ERROR, "Unable to fill buffer props\n");
+-            goto end;
+-        }
++            ret = omx_buf_to_swframe(s, avframe, buffer);
++            if(ret < 0) {
++                av_log(avctx, AV_LOG_ERROR, "Unable to alloce frame\n");
++                goto end;
++            }
+ 
+-        ret = omx_buf_to_swframe(s, avframe, buffer);
+-        if(ret < 0) {
+-            av_log(avctx, AV_LOG_ERROR, "Unable to alloce frame\n");
+-            goto end;
+-        }
++            if (pkt->pts) {
++                if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
++                    av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
++                    return AVERROR_INVALIDDATA;
++                }
++                avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
++                OMXDecodeQueuePop(&s->decode_pts_queue);
++            }
++            s->decode_flag += 1;
++            *got_packet = 1;
+ 
+-        if (pkt->pts) {
+-            if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
+-                av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
+-                return AVERROR_INVALIDDATA;
++            return ret;
++
++        } else if (avctx->codec->id == AV_CODEC_ID_MPEG4 ||
++        avctx->codec->id == AV_CODEC_ID_MJPEG) {
++
++            uint8_t *dst[4];
++            int linesize[4];
++			if ((ret = ff_get_buffer(avctx, avframe, 0)) < 0) {
++                av_log(avctx, AV_LOG_ERROR, "Unable to allocate buffer\n");
++                goto end;
++            }
++
++            ret = av_image_fill_arrays(dst, linesize, buffer->pBuffer,
++                                   avctx->pix_fmt, s->stride, s->plane_size, 1);
++            if (ret < 0){
++                av_log(avctx, AV_LOG_ERROR, "av_image_fill_arrays ret:%d\n", ret);
++                goto end;
+             }
+-            avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
+-            OMXDecodeQueuePop(&s->decode_pts_queue);
+-        }
+-        s->decode_flag += 1;
+-        *got_packet = 1;
+ 
+-        return ret;
++            av_image_copy(avframe->data, avframe->linesize, (const uint8_t**)dst, linesize, 
++                                avctx->pix_fmt, avctx->width, avctx->height);
++            if (pkt->pts) {
++                if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
++                    av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
++                    return AVERROR_INVALIDDATA;
++                }
++                avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
++                OMXDecodeQueuePop(&s->decode_pts_queue);
++            }
++            s->decode_flag += 1;
++            *got_packet = 1;
++        }
+ 
+ end:     
+         err = OMX_FillThisBuffer(s->handle, buffer);
+-- 
+2.25.1
+

+ 88 - 0
package/ffmpeg/0026-Omxdec-Add-flush-callback-of-sf-omx-decoder.patch

@@ -0,0 +1,88 @@
+From b8af55868ea8b2b68ef06fee2eb8a19b16fdb79d Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 13:53:57 +0800
+Subject: [PATCH 5/8] Omxdec: Add flush callback of sf omx decoder
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index 05e37d0..d268454 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -365,6 +365,7 @@ typedef struct OMXCodecContext {
+     OMX_U32 scale_height;
+     OMX_U32 rotation;
+     OMX_U32 mirror;
++    OMX_U32 skip;
+     char *crop_expr;
+     struct {
+         int x;
+@@ -1310,6 +1311,11 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+             }
+             break;
+         }
++
++        if(s->skip) {
++            s->skip--;
++            goto end;
++        }
+         //if (!buffer)
+            // break;
+         // if(!s->first_get_outbuffer)
+@@ -1401,6 +1407,18 @@ end:
+     return ret;
+ }
+ 
++static void omx_decode_flush(AVCodecContext *avctx)
++{
++    OMXCodecContext *s = avctx->priv_data;
++
++    av_log(avctx, AV_LOG_VERBOSE, "Flushing buffer\n");
++
++    while(!OMXDecodeQueueEmpty(&s->decode_pts_queue)) {
++        OMXDecodeQueuePop(&s->decode_pts_queue);
++        s->skip++;
++    }
++}
++
+ static av_cold int omx_decode_end(AVCodecContext *avctx)
+ {
+     OMXCodecContext *s = avctx->priv_data;
+@@ -1465,6 +1483,7 @@ AVCodec ff_mpeg4_omx_decoder = {
+     .init             = omx_decode_init,
+     .decode           = omx_decode_frame,
+     .close            = omx_decode_end,
++    .flush            = omx_decode_flush,
+     .capabilities     = AV_CODEC_CAP_DELAY,
+     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+     .priv_class       = &omx_mpeg4dec_class,
+@@ -1485,6 +1504,7 @@ AVCodec ff_h264_omx_decoder = {
+     .init             = omx_decode_init,
+     .decode           = omx_decode_frame,
+     .close            = omx_decode_end,
++    .flush            = omx_decode_flush,
+     .capabilities     = AV_CODEC_CAP_DELAY,
+     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+     .priv_class       = &omx_h264dec_class,
+@@ -1506,6 +1526,7 @@ AVCodec ff_hevc_omx_decoder = {
+     .init             = omx_decode_init,
+     .decode           = omx_decode_frame,
+     .close            = omx_decode_end,
++    .flush            = omx_decode_flush,
+     .profiles         = NULL_IF_CONFIG_SMALL(ff_hevc_profiles),
+     .capabilities     = AV_CODEC_CAP_DELAY,
+     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+@@ -1528,6 +1549,7 @@ AVCodec ff_mjpeg_omx_decoder = {
+     .init             = omx_decode_init,
+     .decode           = omx_decode_frame,
+     .close            = omx_decode_end,
++    .flush            = omx_decode_flush,
+     .capabilities     = AV_CODEC_CAP_DR1,
+     .max_lowres       = 3,
+     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+-- 
+2.25.1
+

+ 86 - 0
package/ffmpeg/0027-Omxdec-Remove-the-global-variables-in-omxdec-it-make.patch

@@ -0,0 +1,86 @@
+From d44bd33ca4fa0975f284279916f8709282bfe020 Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 13:57:24 +0800
+Subject: [PATCH 6/8] Omxdec: Remove the global variables in omxdec, it make
+ issue in mutithread.
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 25 ++++++++++++-------------
+ 1 file changed, 12 insertions(+), 13 deletions(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index d268454..aa3ddea 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -48,10 +48,6 @@
+ #include "internal.h"
+ #include "profiles.h"
+ 
+-int evnet_bufferflag;
+-int dec_out_height;
+-int dec_out_width;
+-int dec_pix_fmt;
+ 
+ #ifdef OMX_SKIP64BIT
+ static OMX_TICKS to_omx_ticks(int64_t value)
+@@ -354,6 +350,9 @@ typedef struct OMXCodecContext {
+     int mutex_cond_inited;
+ 
+     int eos_sent, got_eos, evnet_bufferflag, first_get_outbuffer;
++    int dec_out_height;
++    int dec_out_width;
++    int dec_pix_fmt;
+ 
+     int extradata_sent;
+     int has_cleanup;
+@@ -475,11 +474,11 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+             }
+             if (port_params.eDir == OMX_DirOutput) {
+                 out_port_params = port_params;
+-                dec_out_width = out_port_params.format.video.nFrameWidth;
+-                dec_out_height = out_port_params.format.video.nFrameHeight;
+-                dec_pix_fmt = out_port_params.format.video.eColorFormat;
++                s->dec_out_width = out_port_params.format.video.nFrameWidth;
++                s->dec_out_height = out_port_params.format.video.nFrameHeight;
++                s->dec_pix_fmt = out_port_params.format.video.eColorFormat;
+ 
+-                av_log(s->avctx, AV_LOG_VERBOSE, "w:%d, h:%d, fmt:%d\n", dec_out_width, dec_out_height, dec_pix_fmt); 
++                av_log(s->avctx, AV_LOG_VERBOSE, "w:%d, h:%d, fmt:%d\n", s->dec_out_width, s->dec_out_height, s->dec_pix_fmt); 
+                 if (!s->num_out_buffers) {
+                     s->num_out_buffers = out_port_params.nBufferCountActual;
+                     s->out_buffer_headers = av_mallocz(sizeof(OMX_BUFFERHEADERTYPE*) * s->num_out_buffers);
+@@ -511,7 +510,7 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+         break;
+     case OMX_EventBufferFlag:
+         av_log(s->avctx, AV_LOG_VERBOSE, "OMX decoder competd set event_bufferflag\n");
+-        evnet_bufferflag = 1;
++        s->evnet_bufferflag = 1;
+     default:
+         av_log(s->avctx, AV_LOG_VERBOSE, "OMX event %d %"PRIx32" %"PRIx32"\n",
+                                          event, (uint32_t) data1, (uint32_t) data2);
+@@ -1306,7 +1305,7 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+             /*eos is sent wait for vpu evnet_bufferflag to get all frames
+               mjpeg: sent a frame, then wait for a decoder frame 
+             */
+-            if((s->eos_sent && !evnet_bufferflag) || (avctx->codec_id == AV_CODEC_ID_MJPEG )) {
++            if((s->eos_sent && !s->evnet_bufferflag) || (avctx->codec_id == AV_CODEC_ID_MJPEG )) {
+                 continue; 
+             }
+             break;
+@@ -1326,9 +1325,9 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+             goto end;
+         }
+ 
+-        avctx->width = dec_out_width;
+-        avctx->height = dec_out_height;
+-        avctx->pix_fmt = omx_map_color_format(avctx, dec_pix_fmt);
++        avctx->width = s->dec_out_width;
++        avctx->height = s->dec_out_height;
++        avctx->pix_fmt = omx_map_color_format(avctx, s->dec_pix_fmt);
+         s->stride     = avctx->width;
+         s->plane_size = avctx->height;
+         
+-- 
+2.25.1
+

+ 59 - 0
package/ffmpeg/0028-Omxdec-no-longer-send-eos-when-processing-zero-size-.patch

@@ -0,0 +1,59 @@
+From d9ac2081cfb04a0d519e9bf57b6b68d12eefadb0 Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 13:59:25 +0800
+Subject: [PATCH 7/8] Omxdec: no longer send eos when processing zero size pkt
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 28 +---------------------------
+ 1 file changed, 1 insertion(+), 27 deletions(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index aa3ddea..47fb482 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -1268,29 +1268,6 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+             av_log(avctx, AV_LOG_ERROR, "OMX_EmptyThisBuffer failed: %x\n", err);
+             return AVERROR_UNKNOWN;
+         }
+-    } else if (!s->eos_sent) {
+-
+-        // if(!s->first_get_outbuffer)
+-        // av_usleep(1000000);
+-        buffer = get_buffer(&s->input_mutex, &s->input_cond,
+-                            &s->num_free_in_buffers, s->free_in_buffers, 1);
+-
+-        if(!buffer) {
+-            av_log(avctx, AV_LOG_ERROR, "get_buffer NULL\n");
+-            return AVERROR(ENOMEM);
+-        }
+-
+-        buffer->nFilledLen = 0;
+-        buffer->nFlags = OMX_BUFFERFLAG_EOS;
+-        buffer->pAppPrivate = buffer->pOutputPortPrivate = NULL;
+-        
+-        err = OMX_EmptyThisBuffer(s->handle, buffer);
+-        if (err != OMX_ErrorNone) {
+-            append_buffer(&s->input_mutex, &s->input_cond, &s->num_free_in_buffers, s->free_in_buffers, buffer);
+-            av_log(avctx, AV_LOG_ERROR, "OMX_EmptyThisBuffer failed: %x\n", err);
+-            return AVERROR_UNKNOWN;
+-        }
+-        s->eos_sent = 1;
+     }
+ 
+     while (!*got_packet && ret == 0 && !s->got_eos) {
+@@ -1302,10 +1279,7 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+                             !pkt || had_partial);
+ 
+         if (!buffer) {
+-            /*eos is sent wait for vpu evnet_bufferflag to get all frames
+-              mjpeg: sent a frame, then wait for a decoder frame 
+-            */
+-            if((s->eos_sent && !s->evnet_bufferflag) || (avctx->codec_id == AV_CODEC_ID_MJPEG )) {
++            if( avctx->codec_id == AV_CODEC_ID_MJPEG ) {
+                 continue; 
+             }
+             break;
+-- 
+2.25.1
+

+ 150 - 0
package/ffmpeg/0029-Omxdec-optimize-buffer-method-for-mjpg-decoder.patch

@@ -0,0 +1,150 @@
+From cc356de583854b257b52b8bf79eb519b7da34c9d Mon Sep 17 00:00:00 2001
+From: Som Qin <som.qin@starfivetech.com>
+Date: Fri, 4 Aug 2023 14:02:26 +0800
+Subject: [PATCH 8/8] Omxdec: optimize buffer method for mjpg decoder
+
+Signed-off-by: Som Qin <som.qin@starfivetech.com>
+---
+ libavcodec/omxdec.c | 88 +++++++++++++++------------------------------
+ 1 file changed, 28 insertions(+), 60 deletions(-)
+
+diff --git a/libavcodec/omxdec.c b/libavcodec/omxdec.c
+index 47fb482..b38e3b9 100755
+--- a/libavcodec/omxdec.c
++++ b/libavcodec/omxdec.c
+@@ -437,7 +437,8 @@ static OMX_ERRORTYPE event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, O
+         if (data1 == OMX_CommandStateSet) {
+             pthread_mutex_lock(&s->state_mutex);
+             s->state = data2;
+-            av_log(s->avctx, AV_LOG_VERBOSE, "OMX state changed to %"PRIu32"\n", (uint32_t) data2);
++            if (!s->has_cleanup)
++                av_log(s->avctx, AV_LOG_VERBOSE, "OMX state changed to %"PRIu32"\n", (uint32_t) data2);
+             pthread_cond_broadcast(&s->state_cond);
+             pthread_mutex_unlock(&s->state_mutex);
+         } else if (data1 == OMX_CommandPortDisable) {
+@@ -1180,11 +1181,14 @@ static int omx_buf_to_swframe(OMXCodecContext *s, AVFrame *frame, OMX_BUFFERHEAD
+     switch (s->avctx->pix_fmt) {
+     case AV_PIX_FMT_NV12:
+     case AV_PIX_FMT_NV21:
++    case AV_PIX_FMT_NV16:
+         frame->linesize[1] = linesize[1];
+         frame->data[1] = dst[1];
+         break;
+ 
+     case AV_PIX_FMT_YUV420P:
++    case AV_PIX_FMT_YUV422P:
++    case AV_PIX_FMT_YUV444P:
+         frame->linesize[1] = linesize[1];
+         frame->linesize[2] = linesize[2];
+         frame->data[1] = dst[1];
+@@ -1279,9 +1283,6 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+                             !pkt || had_partial);
+ 
+         if (!buffer) {
+-            if( avctx->codec_id == AV_CODEC_ID_MJPEG ) {
+-                continue; 
+-            }
+             break;
+         }
+ 
+@@ -1308,66 +1309,33 @@ static int omx_decode_frame(AVCodecContext *avctx, void *data,
+         if (buffer->nFlags & OMX_BUFFERFLAG_EOS)
+             s->got_eos = 1;
+ 
+-        if (avctx->codec->id == AV_CODEC_ID_HEVC ||
+-                avctx->codec->id == AV_CODEC_ID_H264) {
+-            avframe->width  = avctx->width;
+-            avframe->height = avctx->height;
+-
+-            ret = ff_decode_frame_props(avctx, avframe);
+-            if(ret < 0) {
+-                av_log(avctx, AV_LOG_ERROR, "Unable to fill buffer props\n");
+-                goto end;
+-            }
+-
+-            ret = omx_buf_to_swframe(s, avframe, buffer);
+-            if(ret < 0) {
+-                av_log(avctx, AV_LOG_ERROR, "Unable to alloce frame\n");
+-                goto end;
+-            }
+-
+-            if (pkt->pts) {
+-                if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
+-                    av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
+-                    return AVERROR_INVALIDDATA;
+-                }
+-                avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
+-                OMXDecodeQueuePop(&s->decode_pts_queue);
+-            }
+-            s->decode_flag += 1;
+-            *got_packet = 1;
+-
+-            return ret;
+-
+-        } else if (avctx->codec->id == AV_CODEC_ID_MPEG4 ||
+-        avctx->codec->id == AV_CODEC_ID_MJPEG) {
++        avframe->width  = avctx->width;
++        avframe->height = avctx->height;
+ 
+-            uint8_t *dst[4];
+-            int linesize[4];
+-			if ((ret = ff_get_buffer(avctx, avframe, 0)) < 0) {
+-                av_log(avctx, AV_LOG_ERROR, "Unable to allocate buffer\n");
+-                goto end;
+-            }
++        ret = ff_decode_frame_props(avctx, avframe);
++        if(ret < 0) {
++            av_log(avctx, AV_LOG_ERROR, "Unable to fill buffer props\n");
++            goto end;
++        }
+ 
+-            ret = av_image_fill_arrays(dst, linesize, buffer->pBuffer,
+-                                   avctx->pix_fmt, s->stride, s->plane_size, 1);
+-            if (ret < 0){
+-                av_log(avctx, AV_LOG_ERROR, "av_image_fill_arrays ret:%d\n", ret);
+-                goto end;
+-            }
++        ret = omx_buf_to_swframe(s, avframe, buffer);
++        if(ret < 0) {
++            av_log(avctx, AV_LOG_ERROR, "Unable to alloce frame\n");
++            goto end;
++        }
+ 
+-            av_image_copy(avframe->data, avframe->linesize, (const uint8_t**)dst, linesize, 
+-                                avctx->pix_fmt, avctx->width, avctx->height);
+-            if (pkt->pts) {
+-                if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
+-                    av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
+-                    return AVERROR_INVALIDDATA;
+-                }
+-                avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
+-                OMXDecodeQueuePop(&s->decode_pts_queue);
++        if (pkt->pts) {
++            if (OMXDecodeQueueEmpty(&s->decode_pts_queue) != 0){
++                av_log(avctx, AV_LOG_ERROR, "The queue of decode pts is empty.\n");
++                return AVERROR_INVALIDDATA;
+             }
+-            s->decode_flag += 1;
+-            *got_packet = 1;
++            avframe->pts = OMXDecodeQueueFront(&s->decode_pts_queue);
++            OMXDecodeQueuePop(&s->decode_pts_queue);
+         }
++        s->decode_flag += 1;
++        *got_packet = 1;
++
++        return ret;
+ 
+ end:     
+         err = OMX_FillThisBuffer(s->handle, buffer);
+@@ -1523,7 +1491,7 @@ AVCodec ff_mjpeg_omx_decoder = {
+     .decode           = omx_decode_frame,
+     .close            = omx_decode_end,
+     .flush            = omx_decode_flush,
+-    .capabilities     = AV_CODEC_CAP_DR1,
++    .capabilities     = AV_CODEC_CAP_DELAY,
+     .max_lowres       = 3,
+     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+     .priv_class       = &omx_mjpegdec_class,
+-- 
+2.25.1
+

+ 2 - 0
package/starfive/Config.in

@@ -15,3 +15,5 @@ source "package/starfive/img-gpu-powervr/Config.in"
 source "package/starfive/drm_test/Config.in"
 source "package/starfive/pm/Config.in"
 source "package/starfive/usb_wifi/Config.in"
+source "package/starfive/v4l2_dec_test/Config.in"
+

+ 14 - 0
package/starfive/v4l2_dec_test/Config.in

@@ -0,0 +1,14 @@
+config BR2_PACKAGE_V4L2_DEC_TEST
+	bool "v4l2 decode test"
+	depends on BR2_PACKAGE_SF_OMX_IL && BR2_PACKAGE_FFMPEG
+	depends on BR2_TOOLCHAIN_HAS_THREADS # libv4l
+	depends on BR2_USE_MMU # libv4l
+	depends on !BR2_STATIC_LIBS # libv4l
+	depends on BR2_INSTALL_LIBSTDCPP # libv4l
+	depends on BR2_TOOLCHAIN_HEADERS_AT_LEAST_3_0 # libv4l
+	select BR2_PACKAGE_LIBV4L
+	help
+	  v4l2 decoder package
+
+comment "v4l2 decoder package requires ffmpeg implementation"
+	depends on !BR2_PACKAGE_FFMPEG

+ 855 - 0
package/starfive/v4l2_dec_test/v4l2_dec_test.c

@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include "libavformat/avformat.h"
+#include "libavcodec/avcodec.h"
+#include <libv4l2.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/ioctl.h>
+#include <sys/msg.h>
+#include <sys/mman.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <asm/types.h>
+#include <linux/videodev2.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+#define MAX_BUF_CNT  10
+#define BUFCOUNT     5
+#define MAX_PLANES   3
+#define MAX_VIDEO_CNT 24
+#define VPU_DEC_DRV_NAME "wave5-dec"
+
+#define CLEAR(x)  memset (&(x), 0, sizeof (x))
+#define PCLEAR(x) memset ((x), 0, sizeof (*x))
+
+//typedef enum __bool { false = 0, true = 1, } bool;
+
+typedef struct v4l2_buffer v4l2_buffer;
+
+typedef enum IOMethod {
+    IO_METHOD_MMAP,
+    IO_METHOD_USERPTR,
+    IO_METHOD_DMABUF,
+    IO_METHOD_READ
+} IOMethod;
+
+typedef struct buffer {
+    void*   start[MAX_PLANES];
+    size_t  length[MAX_PLANES];
+    int     dmabuf_fd[MAX_PLANES];
+    int     index;
+}buffer;
+
+typedef struct DecodeTestContext
+{
+    int fd;
+    IOMethod io_mthd;  // IO_METHOD_MMAP
+    enum v4l2_memory mem_type;
+    char sOutputFilePath[256];
+    char sInputFilePath[256];
+    char sOutputFormat[64];
+    uint32_t  ScaleWidth;
+    uint32_t  ScaleHeight;
+    uint32_t  StreamFormat;
+    uint32_t  format;
+    uint32_t  n_inputbuffers;
+    uint32_t  n_outputbuffers;
+    uint32_t  inputBufSize;
+    uint32_t  outputBufSize;
+    uint32_t  width;     // = 1920;
+    uint32_t  height;    // = 1080;
+    v4l2_buffer InputV4L2BufArray[MAX_BUF_CNT];
+    v4l2_buffer OutputV4L2BufArray[MAX_BUF_CNT];
+    buffer InputBufArray[MAX_BUF_CNT];
+    buffer OutputBufArray[MAX_BUF_CNT];
+    AVFormatContext *avContext;
+    int32_t  video_stream_idx;
+} DecodeTestContext;
+DecodeTestContext *decodeTestContext;
+struct v4l2_plane *gInput_v4l2_plane;
+struct v4l2_plane *gOutput_v4l2_plane;
+
+static char devPath[16];
+
+static int32_t FillInputBuffer(DecodeTestContext *decodeTestContext, struct v4l2_buffer *buf, int32_t pIndex);
+
+static bool justQuit = false;
+static bool bitsteamEnd = false;
+static bool testFPS =false;
+static FILE *fb = NULL;
+
+static int xioctl(int fd, int request, void* argp)
+{
+    int r;
+
+    // TODO: the orign is v4l2_ioctl()
+    do r = ioctl(fd, request, argp);
+    while (-1 == r && EINTR == errno);
+
+    return r;
+}
+
+static void convert_v4l2_mem_type(int iomthd, enum v4l2_memory *mem_type)
+{
+    if (iomthd < IO_METHOD_MMAP || iomthd > IO_METHOD_READ) {
+        printf("iomthd %d out of range\n", iomthd);
+        return;
+    }
+
+    switch (iomthd) {
+    case IO_METHOD_MMAP:
+        *mem_type = V4L2_MEMORY_MMAP;
+        break;
+    case IO_METHOD_USERPTR:
+        *mem_type = V4L2_MEMORY_USERPTR;
+        break;
+    case IO_METHOD_DMABUF:
+        *mem_type = V4L2_MEMORY_DMABUF;
+        break;
+    case IO_METHOD_READ:
+        *mem_type = 0;  // not use memory machanism
+        break;
+    default:
+        *mem_type = 0;  // not use memory machanism
+        break;
+    }
+}
+
+static void help()
+{
+    printf("v4l2_dec_test - v4l2 hardware decode unit test case\r\n\r\n");
+    printf("Usage:\r\n\r\n");
+    printf("./v4l2_dec_test -i <input file>      input file\r\n");
+    printf("                 -o <output file>     output file\r\n");
+    printf("                 -f <format>          i420/nv12/nv21\r\n");
+    printf("                 --scaleW=<width>     (optional) scale width down. ceil32(width/8) <= scaledW <= width\r\n");
+    printf("                 --scaleH=<heitht>    (optional) scale height down, ceil8(height/8) <= scaledH <= height\r\n\r\n");
+    printf("./v4l2_dec_test --help: show this message\r\n");
+}
+
+
+static void signal_handle(int sig)
+{
+    printf("[%s,%d]: receive sig=%d \n", __FUNCTION__, __LINE__, sig);
+    justQuit = true;
+}
+
+static int32_t FillInputBuffer(DecodeTestContext *decodeTestContext, struct v4l2_buffer *buf, int32_t pIndex)
+{
+    AVFormatContext *avFormatContext = decodeTestContext->avContext;
+    AVPacket *avpacket;
+    int32_t error = 0;
+    avpacket = av_packet_alloc();
+    while (error >= 0)
+    {
+        error = av_read_frame(avFormatContext, avpacket);
+        if (avpacket->stream_index == decodeTestContext->video_stream_idx)
+            break;
+        printf("get audio frame\n");
+    }
+
+    if (error < 0)
+    {
+        if (error == AVERROR_EOF || avFormatContext->pb->eof_reached)
+        {
+            printf("get stream eos\n");
+            buf->m.planes[pIndex].bytesused = 0;
+            return 0;
+        }
+        else
+        {
+            printf("%s:%d failed to av_read_frame, error: %s\n",
+                    __FUNCTION__, __LINE__, av_err2str(error));
+            buf->m.planes[pIndex].bytesused = 0;
+            return 0;
+        }
+    }
+    if (decodeTestContext->InputBufArray[buf->index].length[pIndex] >= avpacket->size){
+        memcpy(decodeTestContext->InputBufArray[buf->index].start[pIndex], avpacket->data, avpacket->size);
+        buf->m.planes[pIndex].bytesused = avpacket->size;
+        return avpacket->size;
+    }else{
+        printf("buff size too small %ld<%d\n",decodeTestContext->InputBufArray[buf->index].length[pIndex], avpacket->size);
+        buf->m.planes[pIndex].bytesused = 0;
+        return 0;
+    }
+}
+
+static void mainloop()
+{
+    int r, i, j, fps, dec_cnt = 0;
+    uint64_t diff_time;
+    struct pollfd* fds = NULL;
+    struct v4l2_event event;
+    struct v4l2_format fmt;
+    struct v4l2_requestbuffers req;
+    struct v4l2_buffer buf;
+    struct v4l2_plane planes[MAX_PLANES];
+    struct timeval tv_old, tv;
+    enum v4l2_buf_type type;
+
+    fds = (struct pollfd*)malloc(sizeof(struct pollfd));
+    PCLEAR(fds);
+    fds->fd = decodeTestContext->fd;
+    fds->events = POLLIN | POLLOUT | POLLPRI;
+
+    while (true){
+        if (justQuit)
+            break;
+        r = poll(fds, 1, 3000);
+        if (-1 == r) {
+            if (EINTR == errno) {
+                continue;
+            }
+           printf("error in poll %d", errno);
+            break;
+        }
+        if (0 == r) {
+            printf("poll timeout, %d\n", errno);
+            break;
+        }
+
+        if (fds->revents & POLLPRI) {
+            if (-1 == xioctl(decodeTestContext->fd, VIDIOC_DQEVENT, &event)) {
+                printf("get event fail\n");
+                break;
+            }
+            printf("get event %d\n",event.type);
+            if (event.type == V4L2_EVENT_SOURCE_CHANGE &&
+                    event.u.src_change.changes == V4L2_EVENT_SRC_CH_RESOLUTION){
+
+                printf("request dist buffer\n");
+                CLEAR(req);
+                req.count = BUFCOUNT;
+                req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+                req.memory = V4L2_MEMORY_MMAP;
+                if (-1 == xioctl(decodeTestContext->fd, VIDIOC_REQBUFS, &req)) {
+                    printf("%s request buffer fail %d\n", devPath, errno);
+                    break;
+                }
+                decodeTestContext->n_outputbuffers = req.count;
+
+                printf("get dist q format\n");
+                CLEAR(fmt);
+                fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+                fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+                if (-1 == ioctl(decodeTestContext->fd, VIDIOC_G_FMT, &fmt)) {
+                    printf("VIDIOC_G_FMT fail\n");
+                    break;
+                }
+                printf("VIDIOC_G_FMT: type=%d, Fourcc format=%c%c%c%c\n",
+                        fmt.type, fmt.fmt.pix_mp.pixelformat & 0xff,
+                        (fmt.fmt.pix_mp.pixelformat >> 8) &0xff,
+                        (fmt.fmt.pix_mp.pixelformat >> 16) &0xff,
+                        (fmt.fmt.pix_mp.pixelformat >> 24) &0xff);
+                printf(" \t width=%d, height=%d, field=%d, bytesperline=%d, sizeimage=%d\n",
+                        fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, fmt.fmt.pix_mp.field,
+                        fmt.fmt.pix_mp.plane_fmt[0].bytesperline, fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+
+                if (fmt.fmt.pix_mp.pixelformat != decodeTestContext->format) {
+                    printf("v4l2 didn't accept format %d. Can't proceed.\n", decodeTestContext->format);
+                    break;
+                }
+
+                /* Note VIDIOC_S_FMT may change width and height. */
+                if (decodeTestContext->width != fmt.fmt.pix_mp.width) {
+                    decodeTestContext->width = fmt.fmt.pix_mp.width;
+                    printf("Correct image width set to %i by device %s.\n", decodeTestContext->width, devPath);
+                }
+
+                if (decodeTestContext->height != fmt.fmt.pix_mp.height) {
+                    decodeTestContext->height = fmt.fmt.pix_mp.height;
+                    printf("Correct image height set to %i by device %s.\n", decodeTestContext->height, devPath);
+                }
+
+                for (i = 0; i < decodeTestContext->n_outputbuffers; i++) {
+                    decodeTestContext->OutputV4L2BufArray[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+                    decodeTestContext->OutputV4L2BufArray[i].memory = V4L2_MEMORY_MMAP;
+                    decodeTestContext->OutputV4L2BufArray[i].index = i;
+                    // VIDIOC_PREPARE_BUF
+                    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_PREPARE_BUF, &decodeTestContext->OutputV4L2BufArray[i]))
+                    {
+                        printf("VIDIOC_PREPARE_BUF fail\n");
+                        break;
+                    }
+                    // VIDIOC_PREPARE_BUF end
+                    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QUERYBUF, &decodeTestContext->OutputV4L2BufArray[i]))
+                    {
+                        printf("VIDIOC_QUERYBUF fail\n");
+                        break;
+                    }
+                    printf("src buffer %d type %d method %d n_plane %d\n",i ,decodeTestContext->OutputV4L2BufArray[i].type,
+                                decodeTestContext->OutputV4L2BufArray[i].memory, decodeTestContext->OutputV4L2BufArray[i].length);
+
+                    for (j = 0; j < decodeTestContext->OutputV4L2BufArray[i].length; j++){
+                        printf("    plane %d length 0x%x offset 0x%x\n", j,
+                                decodeTestContext->OutputV4L2BufArray[i].m.planes[j].length, decodeTestContext->OutputV4L2BufArray[i].m.planes[j].m.mem_offset);
+
+                        decodeTestContext->OutputBufArray[i].length[j] = decodeTestContext->OutputV4L2BufArray[i].m.planes[j].length;
+                        decodeTestContext->OutputBufArray[i].start[j] = v4l2_mmap(NULL, /* start anywhere */
+                                decodeTestContext->OutputV4L2BufArray[i].m.planes[j].length, PROT_READ | PROT_WRITE, /* required */
+                                MAP_SHARED, /* recommended */
+                                decodeTestContext->fd, decodeTestContext->OutputV4L2BufArray[i].m.planes[j].m.mem_offset);
+                        if (MAP_FAILED == decodeTestContext->OutputBufArray[i].start[j]) {
+                            printf("map fail %d err %d\n",i, errno);
+                            break;
+                        }
+                    }
+                }
+
+                for (i = 0; i < decodeTestContext->n_outputbuffers; i++){
+                    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QBUF, &decodeTestContext->OutputV4L2BufArray[i])) {
+                        printf("VIDIOC_QBUF fail\n");
+                        break;
+                    }
+                }
+
+                printf("start dst stream\n");
+                type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+                if (-1 == xioctl(decodeTestContext->fd, VIDIOC_STREAMON, &type)) {
+                    printf("start dst stream fail\n");
+                    break;
+                }
+            }else if (event.type == V4L2_EVENT_EOS){
+                printf("finished, end\n");
+                break;
+            }
+        }
+
+        if (fds->revents & POLLIN) {
+            CLEAR(buf);
+            memset(planes, 0, sizeof(struct v4l2_plane) * MAX_PLANES);
+            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+            buf.memory = decodeTestContext->mem_type;
+            buf.length = MAX_PLANES;
+            buf.m.planes = planes;
+            if (-1 == xioctl(decodeTestContext->fd, VIDIOC_DQBUF, &buf)) {
+                switch (errno) {
+                case EAGAIN:
+                    continue;
+                case EIO:
+                    /* Could ignore EIO, see spec. */
+                    /* fall through */
+                default:
+                    printf("pollin VIDIOC_DQBUF fail\n");
+                }
+            }
+
+            if (buf.index > decodeTestContext->n_outputbuffers)
+            {
+                printf("error capture index %d\n",buf.index);
+                break;
+            }
+
+            if (testFPS)
+            {
+                if (dec_cnt == 0) {
+                    gettimeofday(&tv_old, NULL);
+                }
+                if (dec_cnt++ >= 50) {
+                    gettimeofday(&tv, NULL);
+                    diff_time = (tv.tv_sec - tv_old.tv_sec) * 1000 + (tv.tv_usec - tv_old.tv_usec) / 1000;
+                    fps = 1000  * (dec_cnt - 1) / diff_time;
+                    dec_cnt = 0;
+                    printf("Decoding fps: %d \r\n", fps);
+                }
+            }
+            else
+            {
+                for (i = 0; i < buf.length; i++){
+                    fwrite(decodeTestContext->OutputBufArray[buf.index].start[i], 1, buf.m.planes[i].bytesused, fb);
+                }
+            }
+ 
+            if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QBUF, &buf)) {
+                printf("VIDIOC_QBUF fail\n");
+                break;
+            }
+
+            if (buf.flags & V4L2_BUF_FLAG_LAST){
+                printf("get last buffer\n");
+                justQuit = true;
+            }
+        }
+
+        if (fds->revents & POLLOUT) {
+            CLEAR(buf);
+            memset(planes, 0, sizeof(struct v4l2_plane) * MAX_PLANES);
+            buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+            buf.memory = decodeTestContext->mem_type;
+            buf.length = MAX_PLANES;
+            buf.m.planes = planes;
+            if (-1 == xioctl(decodeTestContext->fd, VIDIOC_DQBUF, &buf)) {
+                switch (errno) {
+                case EAGAIN:
+                    continue;
+                case EIO:
+                    /* Could ignore EIO, see spec. */
+                    /* fall through */
+                default:
+                    printf("pollout VIDIOC_DQBUF fail %d\n", errno);
+                }
+            }
+
+            if (buf.index > decodeTestContext->n_inputbuffers)
+            {
+                printf("error out index %d\n",buf.index);
+                break;
+            }
+
+            //if (bitsteamEnd)
+            //    continue;
+
+            if(!FillInputBuffer(decodeTestContext, &buf, 0)){
+                bitsteamEnd = true;
+            }
+
+            if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QBUF, &buf)) {
+                printf("VIDIOC_QBUF fail\n");
+                break;
+            }
+        }
+    }
+
+    if (fds) {
+        free(fds);
+        fds = NULL;
+    }
+}
+
+int main(int argc, char **argv)
+{
+    printf("=============================\r\n");
+    int32_t error,i,j;
+    struct stat st;
+    struct v4l2_capability cap;
+    struct v4l2_format fmt;
+    struct v4l2_requestbuffers req;
+    struct v4l2_event_subscription argp;
+    enum v4l2_buf_type type;
+    decodeTestContext = malloc(sizeof(DecodeTestContext));
+    gInput_v4l2_plane = malloc(sizeof(struct v4l2_plane) * MAX_BUF_CNT * MAX_PLANES);
+    gOutput_v4l2_plane = malloc(sizeof(struct v4l2_plane) * MAX_BUF_CNT * MAX_PLANES);
+    if (!decodeTestContext || !gInput_v4l2_plane || !gInput_v4l2_plane){
+        return -1;
+    }
+    PCLEAR(decodeTestContext);
+    PCLEAR(gInput_v4l2_plane);
+    PCLEAR(gOutput_v4l2_plane);
+    for (i = 0; i < MAX_BUF_CNT; i++){
+        decodeTestContext->InputV4L2BufArray[i].m.planes = gInput_v4l2_plane + i * MAX_PLANES;
+        decodeTestContext->OutputV4L2BufArray[i].m.planes = gOutput_v4l2_plane + i * MAX_PLANES;
+        decodeTestContext->InputV4L2BufArray[i].length = MAX_PLANES;
+        decodeTestContext->OutputV4L2BufArray[i].length = MAX_PLANES;
+    }
+
+    struct option longOpt[] = {
+        {"output", required_argument, NULL, 'o'},
+        {"input", required_argument, NULL, 'i'},
+        {"format", required_argument, NULL, 'f'},
+        {"scaleW", required_argument, NULL, 'w'},
+        {"scaleH", required_argument, NULL, 'h'},
+        {"test", required_argument, NULL, 't'},
+        {"help", no_argument, NULL, '0'},
+        {NULL, no_argument, NULL, 0},
+    };
+    char *shortOpt = "i:o:f:w:h:t";
+    uint32_t c;
+    int32_t l;
+
+    if (argc == 0)
+    {
+        help();
+        return -1;
+    }
+
+    while ((c = getopt_long(argc, argv, shortOpt, longOpt, (int *)&l)) != -1)
+    {
+        switch (c)
+        {
+        case 'i':
+            printf("input: %s\r\n", optarg);
+            if (access(optarg, R_OK) != -1)
+            {
+                memcpy(decodeTestContext->sInputFilePath, optarg, strlen(optarg));
+            }
+            else
+            {
+                printf("input file not exist!\r\n");
+                return -1;
+            }
+            break;
+        case 'o':
+            printf("output: %s\r\n", optarg);
+            memcpy(decodeTestContext->sOutputFilePath, optarg, strlen(optarg));
+            break;
+        case 'f':
+            printf("format: %s\r\n", optarg);
+            memcpy(decodeTestContext->sOutputFormat, optarg, strlen(optarg));
+            break;
+        case 'w':
+            printf("ScaleWidth: %s\r\n", optarg);
+            decodeTestContext->ScaleWidth = atoi(optarg);
+            break;
+        case 'h':
+            printf("ScaleHeight: %s\r\n", optarg);
+            decodeTestContext->ScaleHeight = atoi(optarg);
+            break;
+        case 't':
+            testFPS = true;
+            break;
+        case '0':
+        default:
+            help();
+            return -1;
+        }
+    }
+
+    decodeTestContext->io_mthd = IO_METHOD_MMAP; //for now
+    if (strstr(decodeTestContext->sOutputFormat, "nv12") != NULL)
+    {
+        decodeTestContext->format = V4L2_PIX_FMT_NV12M;
+    }
+    else if (strstr(decodeTestContext->sOutputFormat, "nv21") != NULL)
+    {
+        decodeTestContext->format = V4L2_PIX_FMT_NV21M;
+    }
+    else if (strstr(decodeTestContext->sOutputFormat, "i420") != NULL)
+    {
+        decodeTestContext->format = V4L2_PIX_FMT_YUV420M;
+    }
+    else
+    {
+        printf("Unsupported color format!\r\n");
+        return -1;
+    }
+
+    /*ffmpeg init*/
+    printf("init ffmpeg\r\n");
+    AVFormatContext *avContext = NULL;
+    AVCodecParameters *codecParameters = NULL;
+    AVInputFormat *avfmt = NULL;
+    int32_t videoIndex;
+    if ((avContext = avformat_alloc_context()) == NULL)
+    {
+        printf("avformat_alloc_context fail\r\n");
+        return -1;
+    }
+    avContext->flags |= AV_CODEC_FLAG_TRUNCATED;
+
+    printf("avformat_open_input\r\n");
+    if ((error = avformat_open_input(&avContext, decodeTestContext->sInputFilePath, avfmt, NULL)))
+    {
+        printf("%s:%d failed to av_open_input_file error(%s), %s\n",
+               __FILE__, __LINE__, av_err2str(error), decodeTestContext->sInputFilePath);
+        avformat_free_context(avContext);
+        return -1;
+    }
+
+    printf("avformat_find_stream_info\r\n");
+    if ((error = avformat_find_stream_info(avContext, NULL)) < 0)
+    {
+        printf("%s:%d failed to avformat_find_stream_info. error(%s)\n",
+               __FUNCTION__, __LINE__, av_err2str(error));
+        avformat_close_input(&avContext);
+        avformat_free_context(avContext);
+        return -1;
+    }
+
+    printf("av_find_best_stream\r\n");
+    videoIndex = av_find_best_stream(avContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
+    if (videoIndex < 0)
+    {
+        printf("%s:%d failed to av_find_best_stream.\n", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    printf("video index = %d\r\n", videoIndex);
+    decodeTestContext->video_stream_idx = videoIndex;
+    decodeTestContext->avContext = avContext;
+    /*get video info*/
+    codecParameters = avContext->streams[videoIndex]->codecpar;
+    printf("codec_id = %d, width = %d, height = %d\r\n", (int)codecParameters->codec_id,
+           codecParameters->width, codecParameters->height);
+    decodeTestContext->width = codecParameters->width;
+    decodeTestContext->height = codecParameters->height;
+
+    if (codecParameters->codec_id == AV_CODEC_ID_H264)
+    {
+        decodeTestContext->StreamFormat = V4L2_PIX_FMT_H264;
+    }
+    else if (codecParameters->codec_id == AV_CODEC_ID_HEVC)
+    {
+        decodeTestContext->StreamFormat = V4L2_PIX_FMT_HEVC;
+    }
+    else
+    {
+        printf("not support stream %d \n",(int)codecParameters->codec_id);
+        goto end;
+    }
+
+    if (decodeTestContext->ScaleWidth)
+    {
+        int scalew = codecParameters->width / decodeTestContext->ScaleWidth;
+        if (scalew > 8 || scalew < 1)
+        {
+            printf("orign width %d scale width %d.\n",codecParameters->width, decodeTestContext->ScaleWidth);
+            printf("Scaling should be 1 to 1/8 (down-scaling only)! Use input parameter, end.\n");
+            goto end;
+        }
+    }
+
+    if (decodeTestContext->ScaleHeight)
+    {
+        int scaleh= codecParameters->height / decodeTestContext->ScaleHeight;
+        if (scaleh > 8 || scaleh < 1)
+        {
+            printf("orign height %d scale height %d.\n",codecParameters->height, decodeTestContext->ScaleHeight);
+            printf("Scaling should be 1 to 1/8 (down-scaling only)! Use input parameter, end.\n");
+            goto end;
+        }
+    }
+
+    signal(SIGINT, signal_handle);
+
+    /* find video device */
+    for (i = 0; i < MAX_VIDEO_CNT; i++){
+        sprintf(devPath, "/dev/video%d", i);
+        if (-1 == stat(devPath, &st)) {
+            continue;
+        }
+        decodeTestContext->fd = v4l2_open(devPath, O_RDWR /* required */ | O_NONBLOCK, 0);
+        if (-1 == decodeTestContext->fd) {
+            printf("Cannot open '%s': %d, %s\n", devPath, errno, strerror(errno));
+            continue;
+        }
+
+        if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QUERYCAP, &cap)) {
+            if (EINVAL == errno) {
+                printf("%s is no V4L2 device\n", devPath);
+                v4l2_close(decodeTestContext->fd);
+                continue;
+            } else {
+                printf("%s can not VIDIOC_QUERYCAP fail\n", devPath);
+                v4l2_close(decodeTestContext->fd);
+                continue;
+            }
+        }
+
+        if (strncmp((const char *)cap.driver, VPU_DEC_DRV_NAME, 9)) {
+            v4l2_close(decodeTestContext->fd);
+            continue;
+        }
+        printf("find %s %s\n", VPU_DEC_DRV_NAME, devPath);
+        break;
+    }
+
+    if (i == MAX_VIDEO_CNT){
+        printf("can not find decoder, end\n");
+        goto end;
+    }
+
+    fb = fopen(decodeTestContext->sOutputFilePath, "wb+");
+    if (!fb)
+    {
+        fprintf(stderr, "output file open err or no output file patch  %d\n", errno);
+        goto end;
+    }
+
+
+    printf("init V4L2 device\n");
+
+    switch (decodeTestContext->io_mthd) {
+    case IO_METHOD_MMAP:
+    case IO_METHOD_USERPTR:
+    case IO_METHOD_DMABUF:
+        if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
+            printf("%s does not support streaming i/o\n", devPath);
+            goto end;
+        }
+        break;
+    default:
+        printf("%s does not specify streaming i/o\n", devPath);
+        goto end;
+        break;
+    }
+
+    argp.type = V4L2_EVENT_SOURCE_CHANGE;
+    argp.id = 0;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_SUBSCRIBE_EVENT, &argp)) {
+        printf("VIDIOC_SUBSCRIBE_EVENT fail\n");
+        goto end;
+    }
+
+    argp.type = V4L2_EVENT_EOS;
+    argp.id = 0;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_SUBSCRIBE_EVENT, &argp)) {
+        printf("VIDIOC_SUBSCRIBE_EVENT fail\n");
+        goto end;
+    }
+
+    printf("set src q format\n");
+
+    fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+    fmt.fmt.pix_mp.width = codecParameters->width;
+    fmt.fmt.pix_mp.height = codecParameters->height;
+    fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+    fmt.fmt.pix_mp.pixelformat = decodeTestContext->StreamFormat;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_S_FMT, &fmt)) {
+        printf("VIDIOC_S_FMT fail\n");
+        goto end;
+    }
+
+    CLEAR(fmt);
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+    fmt.fmt.pix_mp.width = decodeTestContext->ScaleWidth? decodeTestContext->ScaleWidth: codecParameters->width;
+    fmt.fmt.pix_mp.height = decodeTestContext->ScaleHeight? decodeTestContext->ScaleHeight: codecParameters->height;
+    fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+    fmt.fmt.pix_mp.pixelformat = decodeTestContext->format;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_S_FMT, &fmt)) {
+        printf("VIDIOC_S_FMT fail\n");
+        goto end;
+    }
+
+    convert_v4l2_mem_type(decodeTestContext->io_mthd, &decodeTestContext->mem_type);
+
+    printf("get dist q format\n");
+    CLEAR(fmt);
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+    fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
+    if (-1 == ioctl(decodeTestContext->fd, VIDIOC_G_FMT, &fmt)) {
+        printf("VIDIOC_G_FMT fail\n");
+        goto end;
+    }
+    printf("VIDIOC_G_FMT: type=%d, Fourcc format=%c%c%c%c\n",
+            fmt.type, fmt.fmt.pix_mp.pixelformat & 0xff,
+            (fmt.fmt.pix_mp.pixelformat >> 8) &0xff,
+            (fmt.fmt.pix_mp.pixelformat >> 16) &0xff,
+            (fmt.fmt.pix_mp.pixelformat >> 24) &0xff);
+    printf(" \t width=%d, height=%d, field=%d, bytesperline=%d, sizeimage=%d\n",
+            fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, fmt.fmt.pix_mp.field,
+            fmt.fmt.pix_mp.plane_fmt[0].bytesperline, fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+
+    if (fmt.fmt.pix_mp.pixelformat != decodeTestContext->format) {
+        printf("v4l2 didn't accept format %d. Can't proceed.\n", decodeTestContext->format);
+        goto end;
+    }
+
+    /* Note VIDIOC_S_FMT may change width and height. */
+    if (decodeTestContext->width != fmt.fmt.pix_mp.width) {
+        decodeTestContext->width = fmt.fmt.pix_mp.width;
+        printf("Correct image width set to %i by device %s.\n", decodeTestContext->width, devPath);
+    }
+
+    if (decodeTestContext->height != fmt.fmt.pix_mp.height) {
+        decodeTestContext->height = fmt.fmt.pix_mp.height;
+        printf("Correct image height set to %i by device %s.\n", decodeTestContext->height,devPath);
+    }
+
+    /* prepare and start v4l2 stream */
+    printf("request src buffer\n");
+    CLEAR(req);
+    req.count = BUFCOUNT;
+    req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+    req.memory = V4L2_MEMORY_MMAP;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_REQBUFS, &req)) {
+         printf("%s request buffer fail %d\n", devPath, errno);
+         goto end;
+    }
+    decodeTestContext->n_inputbuffers = req.count;
+
+    for (i = 0; i < decodeTestContext->n_inputbuffers; i++) {
+        if (bitsteamEnd)
+            break;
+
+        decodeTestContext->InputV4L2BufArray[i].type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+        decodeTestContext->InputV4L2BufArray[i].memory = V4L2_MEMORY_MMAP;
+        decodeTestContext->InputV4L2BufArray[i].index = i;
+
+        // test prepare
+        //if (-1 == xioctl(decodeTestContext->fd, VIDIOC_PREPARE_BUF, &decodeTestContext->InputV4L2BufArray[i]))
+        //{
+        //    printf("VIDIOC_PREPARE_BUF fail %d\n",errno);
+        //    goto end;
+        //}
+        // test prepare end
+
+        if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QUERYBUF, &decodeTestContext->InputV4L2BufArray[i]))
+        {
+            printf("VIDIOC_QUERYBUF fail %d\n",errno);
+            goto end;
+        }
+        printf("src buffer %d type %d method %d n_plane %d\n",i ,decodeTestContext->InputV4L2BufArray[i].type,
+                    decodeTestContext->InputV4L2BufArray[i].memory, decodeTestContext->InputV4L2BufArray[i].length);
+
+        for (j = 0; j < decodeTestContext->InputV4L2BufArray[i].length; j++){
+            printf("    plane %d length 0x%x offset 0x%x\n", j,
+                    decodeTestContext->InputV4L2BufArray[i].m.planes[j].length, decodeTestContext->InputV4L2BufArray[i].m.planes[j].m.mem_offset);
+
+            decodeTestContext->InputBufArray[i].length[j] = decodeTestContext->InputV4L2BufArray[i].m.planes[j].length;
+            decodeTestContext->InputBufArray[i].start[j] = v4l2_mmap(NULL, /* start anywhere */
+                    decodeTestContext->InputV4L2BufArray[i].m.planes[j].length, PROT_READ | PROT_WRITE, /* required */
+                    MAP_SHARED, /* recommended */
+                    decodeTestContext->fd, decodeTestContext->InputV4L2BufArray[i].m.planes[j].m.mem_offset);
+            if (MAP_FAILED == decodeTestContext->InputBufArray[i].start[j]) {
+                printf("map fail %d\n",i);
+                goto end;
+            }
+        }
+
+        if(!FillInputBuffer(decodeTestContext, &decodeTestContext->InputV4L2BufArray[i], 0)){
+            bitsteamEnd = true;
+        }
+        if (-1 == xioctl(decodeTestContext->fd, VIDIOC_QBUF, &decodeTestContext->InputV4L2BufArray[i])) {
+            printf("VIDIOC_QBUF fail\n");
+            goto end;
+        }
+    }
+
+    printf("start src stream\n");
+    type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_STREAMON, &type)) {
+        printf("start src stream fail\n");
+        goto end;
+    }
+
+    mainloop();
+
+    printf("stop src stream\n");
+    type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_STREAMOFF, &type)) {
+        printf("stop src stream fail\n");
+        goto end;
+    }
+    printf("stop dist stream\n");
+    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+    if (-1 == xioctl(decodeTestContext->fd, VIDIOC_STREAMOFF, &type)) {
+        printf("stop dist stream fail\n");
+        goto end;
+    }
+    v4l2_close(decodeTestContext->fd);
+
+
+end:
+    /*free resource*/
+    if (fb)
+        fclose(fb);
+    avformat_close_input(&avContext);
+    avformat_free_context(avContext);
+}

+ 23 - 0
package/starfive/v4l2_dec_test/v4l2_dec_test.mk

@@ -0,0 +1,23 @@
+################################################################################
+#
+# v4l2_dec_test
+#
+################################################################################
+
+V4L2_DEC_TEST_LICENSE = GPL-2.0+
+
+define V4L2_DEC_TEST_BUILD_CMDS
+	cp package/starfive/v4l2_dec_test/v4l2_dec_test.c $(@D)/
+	(cd $(@D); $(TARGET_CC) -Wall v4l2_dec_test.c -lv4l2 \
+			-ldl -lpthread -Wl,--fatal-warning \
+			-lavformat -lavcodec -lavutil -lswresample \
+			-o v4l2_dec_test)
+endef
+
+define V4L2_DEC_TEST_INSTALL_TARGET_CMDS
+	$(INSTALL) -m 0777 $(@D)/v4l2_dec_test $(TARGET_DIR)/root/v4l2_dec_test
+endef
+
+V4L2_DEC_TEST_DEPENDENCIES = ffmpeg libv4l
+$(eval $(generic-package))
+