Blob Blame History Raw
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -146,10 +146,15 @@
   RefPtr<ImageContainer> mImageContainer;
   VideoInfo mInfo;
   int mDecodedFrames;
 #if LIBAVCODEC_VERSION_MAJOR >= 58
   int mDecodedFramesLate;
+  // Tracks when decode time of recent frame and averange decode time of
+  // previous frames is bigger than frame interval,
+  // i.e. we fail to decode in time.
+  // We switch to SW decode when we hit HW_DECODE_LATE_FRAMES treshold.
+  int mMissedDecodeInAverangeTime;
 #endif
   float mAverangeDecodeTime;
 
   class PtsCorrectionContext {
    public:
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -14,10 +14,13 @@
 #include "VPXDecoder.h"
 #include "mozilla/layers/KnowsCompositor.h"
 #if LIBAVCODEC_VERSION_MAJOR >= 57
 #  include "mozilla/layers/TextureClient.h"
 #endif
+#if LIBAVCODEC_VERSION_MAJOR >= 58
+#  include "mozilla/ProfilerMarkers.h"
+#endif
 #ifdef MOZ_WAYLAND_USE_VAAPI
 #  include "H264.h"
 #  include "mozilla/layers/DMABUFSurfaceImage.h"
 #  include "mozilla/widget/DMABufLibWrapper.h"
 #  include "FFmpegVideoFramePool.h"
@@ -56,13 +59,14 @@
 typedef int VAStatus;
 #  define VA_EXPORT_SURFACE_READ_ONLY 0x0001
 #  define VA_EXPORT_SURFACE_SEPARATE_LAYERS 0x0004
 #  define VA_STATUS_SUCCESS 0x00000000
 #endif
-
 // Use some extra HW frames for potential rendering lags.
 #define EXTRA_HW_FRAMES 6
+// Defines number of delayed frames until we switch back to SW decode.
+#define HW_DECODE_LATE_FRAMES 15
 
 #if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
 #  define CUSTOMIZED_BUFFER_ALLOCATION 1
 #endif
 
@@ -386,10 +390,11 @@
       mImageContainer(aImageContainer),
       mInfo(aConfig),
       mDecodedFrames(0),
 #if LIBAVCODEC_VERSION_MAJOR >= 58
       mDecodedFramesLate(0),
+      mMissedDecodeInAverangeTime(0),
 #endif
       mAverangeDecodeTime(0),
       mLowLatency(aLowLatency) {
   FFMPEG_LOG("FFmpegVideoDecoder::FFmpegVideoDecoder MIME %s Codec ID %d",
              aConfig.mMimeType.get(), mCodecID);
@@ -781,22 +786,32 @@
   float decodeTime = (TimeStamp::Now() - aDecodeStart).ToMilliseconds();
   mAverangeDecodeTime =
       (mAverangeDecodeTime * (mDecodedFrames - 1) + decodeTime) /
       mDecodedFrames;
   FFMPEG_LOG(
-      "  decode time %.2f ms averange decode time %.2f ms decoded frames %d\n",
+      "Frame decode finished, time %.2f ms averange decode time %.2f ms "
+      "decoded %d frames\n",
       decodeTime, mAverangeDecodeTime, mDecodedFrames);
 #if LIBAVCODEC_VERSION_MAJOR >= 58
-  int frameDuration = mFrame->pkt_duration;
-  if (frameDuration > 0 && frameDuration / 1000.0 < decodeTime) {
-    mDecodedFramesLate++;
-    FFMPEG_LOG(
-        "  slow decode: failed to decode in time, frame duration %.2f ms, "
-        "decode time %.2f\n",
-        frameDuration / 1000.0, decodeTime);
-    FFMPEG_LOG("  all decoded frames / late decoded frames %d/%d\n",
-               mDecodedFrames, mDecodedFramesLate);
+  if (mFrame->pkt_duration > 0) {
+    // Switch frame duration to ms
+    float frameDuration = mFrame->pkt_duration / 1000.0f;
+    if (frameDuration < decodeTime) {
+      PROFILER_MARKER_TEXT("FFmpegVideoDecoder::DoDecode", MEDIA_PLAYBACK, {},
+                           "frame decode takes too long");
+      mDecodedFramesLate++;
+      if (frameDuration < mAverangeDecodeTime) {
+        mMissedDecodeInAverangeTime++;
+      }
+      FFMPEG_LOG(
+          "  slow decode: failed to decode in time, frame duration %.2f ms, "
+          "decode time %.2f\n",
+          frameDuration, decodeTime);
+      FFMPEG_LOG("  frames: all decoded %d late decoded %d over averange %d\n",
+                 mDecodedFrames, mDecodedFramesLate,
+                 mMissedDecodeInAverangeTime);
+    }
   }
 #endif
 }
 
 MediaResult FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
@@ -866,10 +881,18 @@
     decodeStart = TimeStamp::Now();
 
     MediaResult rv;
 #  ifdef MOZ_WAYLAND_USE_VAAPI
     if (IsHardwareAccelerated()) {
+      if (mMissedDecodeInAverangeTime > HW_DECODE_LATE_FRAMES) {
+        PROFILER_MARKER_TEXT("FFmpegVideoDecoder::DoDecode", MEDIA_PLAYBACK, {},
+                             "Fallback to SW decode");
+        FFMPEG_LOG("  HW decoding is slow, switch back to SW decode");
+        return MediaResult(
+            NS_ERROR_DOM_MEDIA_DECODE_ERR,
+            RESULT_DETAIL("HW decoding is slow, switch back to SW decode"));
+      }
       rv = CreateImageVAAPI(mFrame->pkt_pos, GetFramePts(mFrame),
                             mFrame->pkt_duration, aResults);
       // If VA-API playback failed, just quit. Decoder is going to be restarted
       // without VA-API.
       if (NS_FAILED(rv)) {
@@ -1129,11 +1152,11 @@
 }
 
 MediaResult FFmpegVideoDecoder<LIBAV_VER>::CreateImageVAAPI(
     int64_t aOffset, int64_t aPts, int64_t aDuration,
     MediaDataDecoder::DecodedData& aResults) {
-  FFMPEG_LOG("VA-API Got one frame output with pts=%" PRId64 "dts=%" PRId64
+  FFMPEG_LOG("VA-API Got one frame output with pts=%" PRId64 " dts=%" PRId64
              " duration=%" PRId64 " opaque=%" PRId64,
              aPts, mFrame->pkt_dts, aDuration, mCodecContext->reordered_opaque);
 
   VADRMPRIMESurfaceDescriptor vaDesc;
   if (!GetVAAPISurfaceDescriptor(&vaDesc)) {