Skip to content

Commit

Permalink
Avoid touching AGffmpeg on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
huoyaoyuan committed Mar 26, 2022
1 parent 2607f39 commit 3114439
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
21 changes: 21 additions & 0 deletions osu.Framework/Graphics/Video/FFmpegConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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;
}
}
22 changes: 11 additions & 11 deletions osu.Framework/Graphics/Video/VideoDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
Expand Down Expand Up @@ -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:
Expand All @@ -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;
Expand All @@ -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()
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 3114439

Please sign in to comment.