OSDN Git Service

Add hardware accelerated codecs
authorChih-Wei Huang <cwhuang@linux.org.tw>
Tue, 9 May 2017 05:49:24 +0000 (13:49 +0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Tue, 9 May 2017 05:49:24 +0000 (13:49 +0800)
Inspired by the nice work from Michael Goffioul and Jaap Jan Meijer.

The patch adds hardware accelerated codecs via ffmpeg vaapi support.
It is still experimental and unstable so it's disabled by default.
To enable it, set the property media.sf.hwaccel=1.

omx/Android.mk
omx/SoftFFmpegVideo.cpp
omx/ffmpeg_hwaccel.c [new file with mode: 0644]
omx/ffmpeg_hwaccel.h [new file with mode: 0644]

index c7c41c5..de0e9bc 100644 (file)
@@ -5,7 +5,8 @@ include $(SF_COMMON_MK)
 LOCAL_SRC_FILES := \
        FFmpegOMXPlugin.cpp \
        SoftFFmpegAudio.cpp \
-       SoftFFmpegVideo.cpp
+       SoftFFmpegVideo.cpp \
+       ffmpeg_hwaccel.c \
 
 LOCAL_C_INCLUDES += \
        $(TOP)/frameworks/native/include/media/hardware \
index f5cfa87..9034417 100644 (file)
 
 #define LOG_TAG "SoftFFmpegVideo"
 #include <utils/Log.h>
+#include <cutils/properties.h>
 
 #include "SoftFFmpegVideo.h"
 #include "FFmpegComponents.h"
+#include "ffmpeg_hwaccel.h"
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -122,10 +124,13 @@ status_t SoftFFmpegVideo::initDecoder(enum AVCodecID codecID) {
     mCtx->extradata = NULL;
     mCtx->width = mWidth;
     mCtx->height = mHeight;
+    ffmpeg_hwaccel_init(mCtx);
+    ALOGD("%p initDecoder: %p", this, mCtx);
     return OK;
 }
 
 void SoftFFmpegVideo::deInitDecoder() {
+    ALOGD("%p deInitDecoder: %p", this, mCtx);
     if (mCtx) {
         if (avcodec_is_open(mCtx)) {
             avcodec_flush_buffers(mCtx);
@@ -139,8 +144,8 @@ void SoftFFmpegVideo::deInitDecoder() {
             avcodec_close(mCtx);
             mCodecAlreadyOpened = false;
         }
-        av_free(mCtx);
-        mCtx = NULL;
+        ffmpeg_hwaccel_deinit(mCtx);
+        av_freep(&mCtx);
     }
     if (mFrame) {
         av_frame_free(&mFrame);
@@ -245,7 +250,7 @@ OMX_ERRORTYPE SoftFFmpegVideo::internalSetParameter(
             if (newWidth != oldWidth || newHeight != oldHeight) {
                 bool outputPort = (newParams->nPortIndex == kOutputPortIndex);
                 if (outputPort) {
-                    ALOGV("OMX_IndexParamPortDefinition (output) width=%d height=%d", newWidth, newHeight);
+                    ALOGI("OMX_IndexParamPortDefinition (output) width=%d height=%d", newWidth, newHeight);
 
                     // only update (essentially crop) if size changes
                     mWidth = newWidth;
@@ -258,7 +263,7 @@ OMX_ERRORTYPE SoftFFmpegVideo::internalSetParameter(
                     // For input port, we only set nFrameWidth and nFrameHeight. Buffer size
                     // is updated when configuring the output port using the max-frame-size,
                     // though client can still request a larger size.
-                    ALOGV("OMX_IndexParamPortDefinition (input) width=%d height=%d", newWidth, newHeight);
+                    ALOGI("OMX_IndexParamPortDefinition (input) width=%d height=%d", newWidth, newHeight);
                     def->format.video.nFrameWidth = newWidth;
                     def->format.video.nFrameHeight = newHeight;
                     mCtx->width = newWidth;
@@ -496,7 +501,14 @@ int32_t SoftFFmpegVideo::decodeVideo() {
                 ret = ERR_NO_FRM;
             }
         } else {
-            ret = ERR_OK;
+            err = ffmpeg_hwaccel_get_frame(mCtx, mFrame);
+            if (err < 0) {
+                ALOGE("ffmpeg HW video decoder failed to decode frame. (%d)", err);
+                //don't send error to OMXCodec, skip!
+                ret = ERR_NO_FRM;
+            } else {
+                ret = ERR_OK;
+            }
         }
     }
 
@@ -755,6 +767,9 @@ SoftOMXComponent* SoftFFmpegVideo::createSoftOMXComponent(
         const char *name, const OMX_CALLBACKTYPE *callbacks,
         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
 
+    if (!property_get_bool("media.sf.hwaccel", 1))
+        return NULL;
+
     OMX_VIDEO_CODINGTYPE codingType = OMX_VIDEO_CodingAutoDetect;
     char *componentRole = NULL;
     enum AVCodecID codecID = AV_CODEC_ID_NONE;
diff --git a/omx/ffmpeg_hwaccel.c b/omx/ffmpeg_hwaccel.c
new file mode 100644 (file)
index 0000000..d59963c
--- /dev/null
@@ -0,0 +1,170 @@
+#define DEBUG_HWACCEL 0
+#define LOG_TAG "HWACCEL"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "config.h"
+#include "ffmpeg_hwaccel.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#ifdef LIBAV_CONFIG_H
+#include "avtools/avconv.h"
+#else
+#include "ffmpeg.h"
+#endif
+
+/* BEGIN: Extracted from ffmpeg_opt.c */
+
+const HWAccel hwaccels[] = {
+#if HAVE_VDPAU_X11
+    { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
+#endif
+#if CONFIG_VDA
+    { "vda", videotoolbox_init, HWACCEL_VDA, AV_PIX_FMT_VDA },
+#endif
+#if CONFIG_LIBMFX
+    { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV },
+#endif
+#if CONFIG_VAAPI
+#ifdef LIBAV_CONFIG_H
+    { "vaapi", hwaccel_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI, AV_HWDEVICE_TYPE_VAAPI },
+#else
+    { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI },
+#endif
+#endif
+#if CONFIG_CUVID
+    { "cuvid", cuvid_init, HWACCEL_CUVID, AV_PIX_FMT_CUDA },
+#endif
+    { 0, 0, HWACCEL_NONE, AV_PIX_FMT_NONE },
+};
+
+/* END: Extracted from ffmpeg_opt.c */
+
+int ffmpeg_hwaccel_get_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    int err;
+    InputStream *ist = avctx->opaque;
+
+    if (ist && ist->hwaccel_retrieve_data && frame->format == ist->hwaccel_pix_fmt) {
+        ALOGV_IF(DEBUG_HWACCEL, "ffmpeg_hwaccel_get_frame ctx=%p ist=%p format=%d hwaccel_pix_fmt=%d hwaccel_id=%d",
+                avctx, ist, frame->format, ist->hwaccel_pix_fmt, ist->hwaccel_id);
+        err = ist->hwaccel_retrieve_data(avctx, frame);
+        if (err == 0)
+            ist->hwaccel_retrieved_pix_fmt = frame->format;
+    } else {
+        err = 0;
+    }
+
+    return err;
+}
+
+/* BEGIN: Extracted from ffmpeg.c */
+
+static const HWAccel *get_hwaccel(enum AVPixelFormat pix_fmt)
+{
+    int i;
+    for (i = 0; hwaccels[i].name; i++)
+        if (hwaccels[i].pix_fmt == pix_fmt)
+            return &hwaccels[i];
+    return NULL;
+}
+
+static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts)
+{
+    InputStream *ist = s->opaque;
+    const enum AVPixelFormat *p;
+    int ret = -1;
+
+    for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
+        ALOGD_IF(DEBUG_HWACCEL, "check %td pix_fmts=%d", p - pix_fmts, *p);
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p);
+        const HWAccel *hwaccel;
+
+        if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
+            break;
+
+        hwaccel = get_hwaccel(*p);
+        if (!hwaccel ||
+                (ist->active_hwaccel_id && ist->active_hwaccel_id != hwaccel->id) ||
+                (ist->hwaccel_id != HWACCEL_AUTO && ist->hwaccel_id != hwaccel->id))
+            continue;
+
+        ret = hwaccel->init(s);
+        if (ret < 0) {
+            if (ist->hwaccel_id == hwaccel->id) {
+                av_log(NULL, AV_LOG_FATAL,
+                       "%s hwaccel requested for input stream #%d, "
+                       "but cannot be initialized.\n", hwaccel->name, ist->file_index);
+            }
+            continue;
+        }
+        if (ist->hw_frames_ctx) {
+            s->hw_frames_ctx = av_buffer_ref(ist->hw_frames_ctx);
+            if (!s->hw_frames_ctx)
+                return AV_PIX_FMT_NONE;
+        }
+        ist->active_hwaccel_id = hwaccel->id;
+        ist->hwaccel_pix_fmt = *p;
+#ifdef LIBAV_CONFIG_H
+        ist->dec = s->codec;
+        ist->dec_ctx = avcodec_alloc_context3(ist->dec);
+        avcodec_copy_context(ist->dec_ctx, s);
+        ret = hw_device_setup_for_decode(ist);
+        s->hw_device_ctx = ist->dec_ctx->hw_device_ctx;
+        ALOGD_IF(DEBUG_HWACCEL, "hw_device_setup_for_decode: %d ctx=%p hw_device_ctx=%p pix_fmts=%d", ret, s, s->hw_device_ctx, *p);
+#endif
+        break;
+    }
+
+    ALOGI("hw codec %s %sabled: s=%p ist=%p pix_fmts=%d", avcodec_get_name(s->codec_id), ret ? "dis" : "en", s, ist, *p);
+    return *p;
+}
+
+static int get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
+{
+    InputStream *ist = s->opaque;
+    ALOGV_IF(DEBUG_HWACCEL, "get_buffer: s=%p ist=%p format=%d hwaccel_pix_fmt=%d",
+            s, ist, frame->format, ist->hwaccel_pix_fmt);
+
+    if (ist->hwaccel_get_buffer && frame->format == ist->hwaccel_pix_fmt)
+        return ist->hwaccel_get_buffer(s, frame, flags);
+
+    return avcodec_default_get_buffer2(s, frame, flags);
+}
+
+/* END: Extracted from ffmpeg.c */
+
+int ffmpeg_hwaccel_init(AVCodecContext *avctx)
+{
+    if (!property_get_bool("media.sf.hwaccel", 0))
+        return 0;
+
+    InputStream *ist = av_mallocz(sizeof(*ist));
+    if (!ist)
+        return AVERROR(ENOMEM);
+
+    ist->hwaccel_id = HWACCEL_AUTO;
+    ist->hwaccel_device = "android";
+    ist->hwaccel_output_format = AV_PIX_FMT_YUV420P;
+
+    avctx->opaque = ist;
+    avctx->get_format = get_format;
+    avctx->get_buffer2 = get_buffer;
+    avctx->thread_safe_callbacks = 1;
+    av_opt_set_int(avctx, "refcounted_frames", 1, 0);
+
+    ALOGD_IF(DEBUG_HWACCEL, "ffmpeg_hwaccel_init ctx=%p ist=%p", avctx, ist);
+    return 0;
+}
+
+void ffmpeg_hwaccel_deinit(AVCodecContext *avctx)
+{
+    if (avctx->opaque) {
+#ifdef LIBAV_CONFIG_H
+        InputStream *ist = avctx->opaque;
+        avcodec_free_context(&ist->dec_ctx);
+#endif
+        av_freep(&avctx->opaque);
+    }
+}
diff --git a/omx/ffmpeg_hwaccel.h b/omx/ffmpeg_hwaccel.h
new file mode 100644 (file)
index 0000000..0d520d1
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef FFMPEG_HWACCEL_H
+#define FFMPEG_HWACCEL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libavcodec/avcodec.h"
+
+extern int  ffmpeg_hwaccel_init(AVCodecContext *avctx);
+extern void ffmpeg_hwaccel_deinit(AVCodecContext *avctx);
+extern int  ffmpeg_hwaccel_get_frame(AVCodecContext *avctx, AVFrame *frame);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif