summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp')
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
new file mode 100644
index 000000000..e9dd8705a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
@@ -0,0 +1,120 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpeghwaccel_mediacodec_p.h"
+
+#include "androidsurfacetexture_p.h"
+#include <rhi/qrhi.h>
+
+extern "C" {
+#include <libavcodec/mediacodec.h>
+#include <libavutil/hwcontext_mediacodec.h>
+}
+
+#if !defined(Q_OS_ANDROID)
+# error "Configuration error"
+#endif
+
+namespace QFFmpeg {
+
+class MediaCodecTextureSet : public TextureSet
+{
+public:
+ MediaCodecTextureSet(qint64 textureHandle) : handle(textureHandle) { }
+
+ qint64 textureHandle(QRhi *, int plane) override { return (plane == 0) ? handle : 0; }
+
+private:
+ qint64 handle;
+};
+
+namespace {
+
+void deleteSurface(AVHWDeviceContext *ctx)
+{
+ AndroidSurfaceTexture* s = reinterpret_cast<AndroidSurfaceTexture *>(ctx->user_opaque);
+ delete s;
+}
+
+AndroidSurfaceTexture* getTextureSurface(AVFrame *frame)
+{
+ if (!frame || !frame->hw_frames_ctx)
+ return nullptr;
+
+ auto *frameContext = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
+
+ if (!frameContext || !frameContext->device_ctx)
+ return nullptr;
+
+ AVHWDeviceContext *deviceContext = frameContext->device_ctx;
+
+ return reinterpret_cast<AndroidSurfaceTexture *>(deviceContext->user_opaque);
+}
+} // namespace
+
+void MediaCodecTextureConverter::setupDecoderSurface(AVCodecContext *avCodecContext)
+{
+ std::unique_ptr<AndroidSurfaceTexture> androidSurfaceTexture(new AndroidSurfaceTexture(0));
+ AVMediaCodecContext *mediacodecContext = av_mediacodec_alloc_context();
+ av_mediacodec_default_init(avCodecContext, mediacodecContext, androidSurfaceTexture->surface());
+
+ if (!avCodecContext->hw_device_ctx || !avCodecContext->hw_device_ctx->data)
+ return;
+
+ AVHWDeviceContext *deviceContext =
+ reinterpret_cast<AVHWDeviceContext *>(avCodecContext->hw_device_ctx->data);
+
+ if (!deviceContext->hwctx)
+ return;
+
+ AVMediaCodecDeviceContext *mediaDeviceContext =
+ reinterpret_cast<AVMediaCodecDeviceContext *>(deviceContext->hwctx);
+
+ if (!mediaDeviceContext)
+ return;
+
+ mediaDeviceContext->surface = androidSurfaceTexture->surface();
+
+ Q_ASSERT(deviceContext->user_opaque == nullptr);
+ deviceContext->user_opaque = androidSurfaceTexture.release();
+ deviceContext->free = deleteSurface;
+}
+
+TextureSet *MediaCodecTextureConverter::getTextures(AVFrame *frame)
+{
+ AndroidSurfaceTexture * androidSurfaceTexture = getTextureSurface(frame);
+
+ if (!androidSurfaceTexture || !androidSurfaceTexture->isValid())
+ return {};
+
+ if (!externalTexture || m_currentSurfaceIndex != androidSurfaceTexture->index()) {
+ m_currentSurfaceIndex = androidSurfaceTexture->index();
+ androidSurfaceTexture->detachFromGLContext();
+ externalTexture = std::unique_ptr<QRhiTexture>(
+ rhi->newTexture(QRhiTexture::Format::RGBA8, { frame->width, frame->height }, 1,
+ QRhiTexture::ExternalOES));
+
+ if (!externalTexture->create()) {
+ qWarning() << "Failed to create the external texture!";
+ return {};
+ }
+
+ quint64 textureHandle = externalTexture->nativeTexture().object;
+ androidSurfaceTexture->attachToGLContext(textureHandle);
+ }
+
+ // release a MediaCodec buffer and render it to the surface
+ AVMediaCodecBuffer *buffer = (AVMediaCodecBuffer *)frame->data[3];
+
+ if (!buffer) {
+ qWarning() << "Received a frame without AVMediaCodecBuffer.";
+ } else if (av_mediacodec_release_buffer(buffer, 1) < 0) {
+ qWarning() << "Failed to render buffer to surface.";
+ return {};
+ }
+
+ androidSurfaceTexture->updateTexImage();
+
+ return new MediaCodecTextureSet(externalTexture->nativeTexture().object);
+}
+}