From 3114439f3c8df4f0e174ae4aee1da57249bd781a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 26 Mar 2022 19:30:11 +0800 Subject: [PATCH] Avoid touching AGffmpeg on Android --- .../Graphics/Video/FFmpegConstants.cs | 21 ++++++++++++++++++ osu.Framework/Graphics/Video/VideoDecoder.cs | 22 +++++++++---------- 2 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 osu.Framework/Graphics/Video/FFmpegConstants.cs diff --git a/osu.Framework/Graphics/Video/FFmpegConstants.cs b/osu.Framework/Graphics/Video/FFmpegConstants.cs new file mode 100644 index 0000000000..569c5151bb --- /dev/null +++ b/osu.Framework/Graphics/Video/FFmpegConstants.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Framework.Graphics.Video +{ + // Touching AGffmpeg or its LibraryLoader in any way on non-Desktop platforms + // will cause it to throw in static constructor, which can't be bypassed. + // Define our own constants to avoid touching the class. + + internal static class FFmpegConstants + { + public const int AVSEEK_FLAG_BACKWARD = 1; + public const int AVSEEK_SIZE = 0x10000; + public const int AVFMT_FLAG_GENPTS = 0x0001; + public const int AV_TIME_BASE = 1000000; + public static readonly int EAGAIN = RuntimeInfo.IsApple ? 35 : 11; + public const int AVERROR_EOF = -('E' + ('O' << 8) + ('F' << 16) + (' ' << 24)); + public const long AV_NOPTS_VALUE = unchecked((long)0x8000000000000000); + public const int ENOMEM = 12; + } +} diff --git a/osu.Framework/Graphics/Video/VideoDecoder.cs b/osu.Framework/Graphics/Video/VideoDecoder.cs index f1dc4acbb1..c9cad26d8f 100644 --- a/osu.Framework/Graphics/Video/VideoDecoder.cs +++ b/osu.Framework/Graphics/Video/VideoDecoder.cs @@ -153,7 +153,7 @@ public void Seek(double targetTimestamp) decoderCommands.Enqueue(() => { ffmpeg.avcodec_flush_buffers(codecContext); - ffmpeg.av_seek_frame(formatContext, stream->index, (long)(targetTimestamp / timeBaseInSeconds / 1000.0), AGffmpeg.AVSEEK_FLAG_BACKWARD); + ffmpeg.av_seek_frame(formatContext, stream->index, (long)(targetTimestamp / timeBaseInSeconds / 1000.0), FFmpegConstants.AVSEEK_FLAG_BACKWARD); skipOutputUntilTime = targetTimestamp; State = DecoderState.Ready; }); @@ -294,7 +294,7 @@ private static long streamSeekCallbacks(void* opaque, long offset, int whence) decoder.videoStream.Seek(offset, SeekOrigin.Begin); break; - case AGffmpeg.AVSEEK_SIZE: + case FFmpegConstants.AVSEEK_SIZE: return decoder.videoStream.Length; default: @@ -319,7 +319,7 @@ private void prepareDecoding() var fcPtr = ffmpeg.avformat_alloc_context(); formatContext = fcPtr; formatContext->pb = ioContext; - formatContext->flags |= AGffmpeg.AVFMT_FLAG_GENPTS; // required for most HW decoders as they only read `pts` + formatContext->flags |= FFmpegConstants.AVFMT_FLAG_GENPTS; // required for most HW decoders as they only read `pts` int openInputResult = ffmpeg.avformat_open_input(&fcPtr, "dummy", null, null); inputOpened = openInputResult >= 0; @@ -340,7 +340,7 @@ private void prepareDecoding() if (stream->duration > 0) Duration = stream->duration * timeBaseInSeconds * 1000.0; else - Duration = formatContext->duration / (double)AGffmpeg.AV_TIME_BASE * 1000.0; + Duration = formatContext->duration / (double)FFmpegConstants.AV_TIME_BASE * 1000.0; } private void recreateCodecContext() @@ -493,7 +493,7 @@ private void decodeNextFrame(AVPacket* packet, AVFrame* receiveFrame) int sendPacketResult = sendPacket(receiveFrame, packet); // keep the packet data for next frame if we didn't send it successfully. - if (sendPacketResult == -AGffmpeg.EAGAIN) + if (sendPacketResult == -FFmpegConstants.EAGAIN) { unrefPacket = false; } @@ -502,7 +502,7 @@ private void decodeNextFrame(AVPacket* packet, AVFrame* receiveFrame) if (unrefPacket) ffmpeg.av_packet_unref(packet); } - else if (readFrameResult == AGffmpeg.AVERROR_EOF) + else if (readFrameResult == FFmpegConstants.AVERROR_EOF) { // Flush decoder. sendPacket(receiveFrame, null); @@ -517,7 +517,7 @@ private void decodeNextFrame(AVPacket* packet, AVFrame* receiveFrame) State = DecoderState.EndOfStream; } } - else if (readFrameResult == -AGffmpeg.EAGAIN) + else if (readFrameResult == -FFmpegConstants.EAGAIN) { State = DecoderState.Ready; Thread.Sleep(1); @@ -536,7 +536,7 @@ private int sendPacket(AVFrame* receiveFrame, AVPacket* packet) // Note: EAGAIN can be returned if there's too many pending frames, which we have to read, // otherwise we would get stuck in an infinite loop. - if (sendPacketResult == 0 || sendPacketResult == -AGffmpeg.EAGAIN) + if (sendPacketResult == 0 || sendPacketResult == -FFmpegConstants.EAGAIN) { readDecodedFrames(receiveFrame); } @@ -560,7 +560,7 @@ private void readDecodedFrames(AVFrame* receiveFrame) if (receiveFrameResult < 0) { - if (receiveFrameResult != -AGffmpeg.EAGAIN && receiveFrameResult != AGffmpeg.AVERROR_EOF) + if (receiveFrameResult != -FFmpegConstants.EAGAIN && receiveFrameResult != FFmpegConstants.AVERROR_EOF) { Logger.Log($"Failed to receive frame from avcodec: {getErrorMessage(receiveFrameResult)}"); tryDisableHwDecoding(receiveFrameResult); @@ -571,7 +571,7 @@ private void readDecodedFrames(AVFrame* receiveFrame) // use `best_effort_timestamp` as it can be more accurate if timestamps from the source file (pts) are broken. // but some HW codecs don't set it in which case fallback to `pts` - long frameTimestamp = receiveFrame->best_effort_timestamp != AGffmpeg.AV_NOPTS_VALUE ? receiveFrame->best_effort_timestamp : receiveFrame->pts; + long frameTimestamp = receiveFrame->best_effort_timestamp != FFmpegConstants.AV_NOPTS_VALUE ? receiveFrame->best_effort_timestamp : receiveFrame->pts; double frameTime = (frameTimestamp - stream->start_time) * timeBaseInSeconds * 1000; @@ -695,7 +695,7 @@ private void tryDisableHwDecoding(int errorCode) hwDecodingAllowed = false; - if (errorCode == -AGffmpeg.ENOMEM) + if (errorCode == -FFmpegConstants.ENOMEM) { Logger.Log("Disabling hardware decoding of all videos due to a lack of memory"); TargetHardwareVideoDecoders.Value = HardwareVideoDecoder.None;