From 24fc53ead365b18982feda7b5711825f45cc11a6 Mon Sep 17 00:00:00 2001 From: ownedbywuigi Date: Mon, 11 May 2026 08:24:18 -0700 Subject: [PATCH 1/4] Adaptive buffering --- dom/media/AdaptiveBufferingPolicy.cpp | 153 ++++++++++++++++++++++++++ dom/media/AdaptiveBufferingPolicy.h | 91 +++++++++++++++ dom/media/FramePool.cpp | 91 +++++++++++++++ dom/media/FramePool.h | 103 +++++++++++++++++ dom/media/VideoPlaybackStats.cpp | 150 +++++++++++++++++++++++++ dom/media/VideoPlaybackStats.h | 127 +++++++++++++++++++++ dom/media/moz.build | 4 + 7 files changed, 719 insertions(+) create mode 100644 dom/media/AdaptiveBufferingPolicy.cpp create mode 100644 dom/media/AdaptiveBufferingPolicy.h create mode 100644 dom/media/FramePool.cpp create mode 100644 dom/media/FramePool.h create mode 100644 dom/media/VideoPlaybackStats.cpp create mode 100644 dom/media/VideoPlaybackStats.h diff --git a/dom/media/AdaptiveBufferingPolicy.cpp b/dom/media/AdaptiveBufferingPolicy.cpp new file mode 100644 index 0000000000..2bb48b005c --- /dev/null +++ b/dom/media/AdaptiveBufferingPolicy.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "AdaptiveBufferingPolicy.h" +#include "mozilla/Logging.h" + +namespace mozilla { + +extern LazyLogModule gMediaDecoderLog; + +#define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("AdaptiveBuffering::%s: " arg, __func__, ##__VA_ARGS__)) + +/** + * Compute estimated memory usage for a single frame. + */ +static uint64_t EstimateFrameMemory(const gfx::IntSize& aSize) +{ + // YUV420 format: 1.5 bytes per pixel + uint64_t width = aSize.width; + uint64_t height = aSize.height; + return (width * height * 3) / 2; +} + +/** + * Calculate resolution tier (affects quality and frame size). + */ +static uint32_t GetResolutionTier(const gfx::IntSize& aSize) +{ + uint32_t pixels = aSize.width * aSize.height; + + // Classify resolution + if (pixels < 480 * 360) return 0; // SD-like + if (pixels < 1280 * 720) return 1; // HD-ready + if (pixels < 1920 * 1080) return 2; // Full HD + if (pixels < 3840 * 2160) return 3; // 4K + return 4; // 8K+ +} + +AdaptiveBufferingPolicy::Config +AdaptiveBufferingPolicy::ComputeOptimalConfig( + const gfx::IntSize& aVideoSize, + uint32_t aFrameRate, + uint64_t aVideoBitrate, + bool aHardwareAccelerated, + uint64_t aAvailableMemory) +{ + Config config; + uint32_t resolutionTier = GetResolutionTier(aVideoSize); + uint64_t frameMemory = EstimateFrameMemory(aVideoSize); + + // Adjust video queue size based on resolution and memory availability + uint32_t recommendedQueueSize = config.mDefaultVideoQueueFrames; + + if (aHardwareAccelerated) { + recommendedQueueSize = config.mHWAccelVideoQueueFrames; + } + + // For high-resolution content, reduce queue size to save memory + if (resolutionTier >= 3) { // 4K or higher + recommendedQueueSize = std::max(config.mMinVideoQueueFrames, + recommendedQueueSize / 2); + config.mMaxVideoBufferMemory = 50 * 1024 * 1024; // 50MB for 4K + } else if (resolutionTier == 2) { // Full HD + config.mMaxVideoBufferMemory = 75 * 1024 * 1024; // 75MB + } + + // Constrain by available memory + uint64_t maxFramesInMemory = config.mMaxVideoBufferMemory / + std::max(frameMemory, (uint64_t)1); + recommendedQueueSize = std::min((uint32_t)maxFramesInMemory, recommendedQueueSize); + recommendedQueueSize = std::max(config.mMinVideoQueueFrames, recommendedQueueSize); + + config.mDefaultVideoQueueFrames = recommendedQueueSize; + + // Adjust decode speed threshold based on bitrate + if (aVideoBitrate > 0) { + // High bitrate content may need faster decode speed + if (aVideoBitrate > 10000000) { // >10 Mbps + config.mDecodeSpeedRatio = 2.0; + } else if (aVideoBitrate > 5000000) { // >5 Mbps + config.mDecodeSpeedRatio = 1.75; + } + } + + // For network streams, use more conservative buffering + if (aVideoBitrate > 0) { + config.mLowAudioThreshold = 500000; // 500ms + config.mAmpleAudioThreshold = 3000000; // 3s + } + + LOG("Computed config: queueSize=%u, maxMemory=%llu, decodeRatio=%.2f (res=%u)", + recommendedQueueSize, + config.mMaxVideoBufferMemory, + config.mDecodeSpeedRatio, + resolutionTier); + + return config; +} + +uint32_t AdaptiveBufferingPolicy::GetOptimalVideoQueueSize( + const gfx::IntSize& aVideoSize, + bool aHardwareAccelerated, + uint64_t aAvailableMemory) +{ + // Start with base size + uint32_t queueSize = aHardwareAccelerated ? 5 : 10; + + uint64_t frameMemory = EstimateFrameMemory(aVideoSize); + uint64_t maxFramesInMemory = (100 * 1024 * 1024) / frameMemory; + + // Constrain by available memory + queueSize = std::min((uint32_t)maxFramesInMemory, queueSize); + + return std::max(3u, queueSize); +} + +uint64_t AdaptiveBufferingPolicy::GetOptimalAudioThreshold(bool aIsNetworkStream) +{ + if (aIsNetworkStream) { + return 500000; // 500ms for network (more conservative) + } + return 300000; // 300ms for local files +} + +bool AdaptiveBufferingPolicy::ShouldSkipFrame( + int64_t aAudioTimeUs, + int64_t aFrameDisplayTimeUs, + int64_t aVideoDecodeTimeUs, + uint32_t aThresholdMs) +{ + // Skip if frame is already in the past relative to audio + int64_t thresholdUs = aThresholdMs * 1000; + + // Calculate how late this frame is + int64_t timeLatenessUs = aAudioTimeUs - aFrameDisplayTimeUs; + + // Skip if frame is significantly behind audio timeline + if (timeLatenessUs > thresholdUs) { + return true; + } + + // Also skip if decoding this frame would take us further behind + int64_t projectedLatenessUs = timeLatenessUs + aVideoDecodeTimeUs; + if (projectedLatenessUs > (thresholdUs * 2)) { + return true; + } + + return false; +} + +} // namespace mozilla diff --git a/dom/media/AdaptiveBufferingPolicy.h b/dom/media/AdaptiveBufferingPolicy.h new file mode 100644 index 0000000000..2f75157ff9 --- /dev/null +++ b/dom/media/AdaptiveBufferingPolicy.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_ADAPTIVEBUFFERINGPOLICY_H_ +#define MOZILLA_ADAPTIVEBUFFERINGPOLICY_H_ + +#include "mozilla/Atomics.h" +#include "TimeUnits.h" +#include "gfxTypes.h" + +namespace mozilla { + +/** + * Adaptive buffering policy manager for video playback. + * + * Dynamically adjusts buffering thresholds based on: + * - Available system memory + * - Video resolution and bitrate + * - Decode speed vs real-time ratio + * - Network conditions (if applicable) + * + * This reduces memory usage while maintaining smooth playback. + */ +class AdaptiveBufferingPolicy { +public: + struct Config { + // Audio buffering thresholds (microseconds) + uint64_t mLowAudioThreshold = 300000; // 300ms - trigger faster decoding + uint64_t mAmpleAudioThreshold = 2000000; // 2s - target comfortable level + + // Video frame queue sizing + uint32_t mMinVideoQueueFrames = 3; // Absolute minimum + uint32_t mDefaultVideoQueueFrames = 10; // Default for standard content + uint32_t mMaxVideoQueueFrames = 20; // Absolute maximum + + // Hardware acceleration thresholds + uint32_t mHWAccelVideoQueueFrames = 5; // Target for HW-accelerated playback + + // Memory limits (bytes) + uint64_t mMaxVideoBufferMemory = 100 * 1024 * 1024; // 100MB default + uint64_t mTargetVideoBufferMemory = 50 * 1024 * 1024; // 50MB target + + // Decode speed thresholds + double mDecodeSpeedRatio = 1.5; // Must decode 1.5x realtime speed + + // Frame skip configuration + bool mEnableAdaptiveFrameSkip = true; + bool mSkipNonKeyframes = true; + uint32_t mFrameSkipThresholdMs = 60; // Skip if >60ms behind audio + }; + + /** + * Adjust buffering policy based on system state and video properties. + * Returns optimized configuration for the given video specifications. + */ + static Config ComputeOptimalConfig( + const gfx::IntSize& aVideoSize, + uint32_t aFrameRate, + uint64_t aVideoBitrate, + bool aHardwareAccelerated, + uint64_t aAvailableMemory); + + /** + * Get the recommended video queue size based on context. + */ + static uint32_t GetOptimalVideoQueueSize( + const gfx::IntSize& aVideoSize, + bool aHardwareAccelerated, + uint64_t aAvailableMemory); + + /** + * Get audio buffering threshold based on network conditions. + */ + static uint64_t GetOptimalAudioThreshold(bool aIsNetworkStream); + + /** + * For HD+ content with limited memory, suggest lower priority video frames + * that can be safely skipped without visible quality loss. + */ + static bool ShouldSkipFrame( + int64_t aAudioTimeUs, + int64_t aFrameDisplayTimeUs, + int64_t aVideoDecodeTimeUs, + uint32_t aThresholdMs); +}; + +} // namespace mozilla + +#endif // MOZILLA_ADAPTIVEBUFFERINGPOLICY_H_ diff --git a/dom/media/FramePool.cpp b/dom/media/FramePool.cpp new file mode 100644 index 0000000000..621eb3d28c --- /dev/null +++ b/dom/media/FramePool.cpp @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FramePool.h" +#include "mozilla/Logging.h" +#include "ImageContainer.h" + +namespace mozilla { + +extern LazyLogModule gMediaDecoderLog; + +#define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("FramePool::%s: " arg, __func__, ##__VA_ARGS__)) + +FramePool::FramePool(size_t aMaxFrames) + : mMaxPoolSize(aMaxFrames > 0 ? aMaxFrames : 16) +{ + LOG("FramePool created with max size: %zu", mMaxPoolSize); +} + +FramePool::~FramePool() +{ + Clear(); +} + +RefPtr FramePool::AcquireFrame(const gfx::IntSize& aSize) +{ + MutexAutoLock lock(mMutex); + + // Try to reuse a frame from pool + if (!mFramePool.IsEmpty()) { + RefPtr frame = mFramePool.PopBack(); + mFramesReused++; + LOGV("Reused frame from pool, pool size now: %zu", mFramePool.length()); + return frame; + } + + // Allocate new frame + RefPtr frame = new layers::RecyclingPlanarYCbCrImage(); + mTotalAllocated++; + + LOG("Allocated new frame, total: %zu", mTotalAllocated); + return frame; +} + +void FramePool::ReleaseFrame(layers::Image* aFrame) +{ + if (!aFrame) { + return; + } + + MutexAutoLock lock(mMutex); + + // Only pool frame if we haven't reached max capacity + if (mFramePool.length() < mMaxPoolSize) { + mFramePool.AppendElement(aFrame); + + // Track peak pool size + if (mFramePool.length() > mPeakPoolSize) { + mPeakPoolSize = mFramePool.length(); + } + + LOGV("Released frame to pool, pool size: %zu/%zu", + mFramePool.length(), mMaxPoolSize); + } else { + // Frame exceeds pool capacity, let it be released + LOGV("Frame pool at capacity, releasing frame"); + } +} + +void FramePool::Clear() +{ + MutexAutoLock lock(mMutex); + mFramePool.Clear(); + LOG("Frame pool cleared"); +} + +size_t FramePool::PoolSize() const +{ + MutexAutoLock lock(mMutex); + return mFramePool.length(); +} + +size_t FramePool::TotalFrames() const +{ + MutexAutoLock lock(mMutex); + return mTotalAllocated; +} + +} // namespace mozilla diff --git a/dom/media/FramePool.h b/dom/media/FramePool.h new file mode 100644 index 0000000000..56775702a4 --- /dev/null +++ b/dom/media/FramePool.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_FRAMEPOOL_H_ +#define MOZILLA_FRAMEPOOL_H_ + +#include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" +#include "nsTArray.h" +#include "gfxTypes.h" + +namespace mozilla { + +namespace layers { +class Image; +} // namespace layers + +/** + * FramePool manages a pool of reusable video frame buffers to reduce + * memory allocation/deallocation overhead during playback. + * + * Usage: + * RefPtr pool = new FramePool(max_frames); + * RefPtr frame = pool->AcquireFrame(size); + * // use frame... + * pool->ReleaseFrame(frame); // returns to pool or deallocates + */ +class FramePool { +public: + explicit FramePool(size_t aMaxFrames = 16); + ~FramePool(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FramePool) + + /** + * Acquire a frame buffer from the pool. If no pooled frames are available, + * allocates a new one. Thread-safe. + */ + RefPtr AcquireFrame(const gfx::IntSize& aSize); + + /** + * Return a frame to the pool for reuse. If pool is full, frame is released. + * Thread-safe. + */ + void ReleaseFrame(layers::Image* aFrame); + + /** + * Clear all pooled frames and release memory. + */ + void Clear(); + + /** + * Get current number of frames in pool. + */ + size_t PoolSize() const; + + /** + * Get total frames allocated (pooled + in use). + */ + size_t TotalFrames() const; + + /** + * Get statistics about pool usage. + */ + struct Stats { + size_t mPooledFrames; // Frames available in pool + size_t mAllocatedFrames; // Total frames ever allocated + size_t mReusedFrames; // Frames reused from pool + size_t mPeakPoolSize; // Maximum simultaneous frames in pool + }; + + Stats GetStats() const { + MutexAutoLock lock(mMutex); + return {mFramePool.length(), mTotalAllocated, mFramesReused, mPeakPoolSize}; + } + + /** + * Reset statistics counters. + */ + void ResetStats() { + MutexAutoLock lock(mMutex); + mFramesReused = 0; + mTotalAllocated = 0; + mPeakPoolSize = 0; + } + +private: + mutable Mutex mMutex; + nsTArray> mFramePool; + const size_t mMaxPoolSize; + + // Statistics + size_t mTotalAllocated = 0; + size_t mFramesReused = 0; + size_t mPeakPoolSize = 0; +}; + +} // namespace mozilla + +#endif // MOZILLA_FRAMEPOOL_H_ diff --git a/dom/media/VideoPlaybackStats.cpp b/dom/media/VideoPlaybackStats.cpp new file mode 100644 index 0000000000..07740b4af9 --- /dev/null +++ b/dom/media/VideoPlaybackStats.cpp @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VideoPlaybackStats.h" +#include "mozilla/Logging.h" +#include "nsPrintfCString.h" + +namespace mozilla { + +extern LazyLogModule gMediaDecoderLog; + +#define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("VideoPlaybackStats::%s: " arg, __func__, ##__VA_ARGS__)) + +VideoPlaybackStats::VideoPlaybackStats() +{ + LOG("VideoPlaybackStats created"); +} + +VideoPlaybackStats::~VideoPlaybackStats() +{ + LOG("VideoPlaybackStats destroyed - Total frames decoded: %llu, displayed: %llu, skipped: %llu", + mStats.mTotalFramesDecoded, + mStats.mTotalFramesDisplayed, + mStats.mTotalFramesSkipped); +} + +void VideoPlaybackStats::RecordFrameDecoded(const FrameStats& aStats) +{ + MutexAutoLock lock(mMutex); + + mStats.mTotalFramesDecoded++; + + if (aStats.mDecodeTimeUs > 0) { + mTotalDecodeTimeUs += aStats.mDecodeTimeUs; + mSampleCount++; + + // Update average decode time + mStats.mAverageDecodeTimeMs = (double)mTotalDecodeTimeUs / + (mSampleCount * 1000.0); + + // Track max decode time + double decodeTimeMs = aStats.mDecodeTimeUs / 1000.0; + if (decodeTimeMs > mStats.mMaxDecodeTimeMs) { + mStats.mMaxDecodeTimeMs = decodeTimeMs; + } + } + + if (aStats.mMemoryBytes > 0) { + if (aStats.mMemoryBytes > mStats.mPeakMemoryUsage) { + mStats.mPeakMemoryUsage = aStats.mMemoryBytes; + } + } +} + +void VideoPlaybackStats::RecordFrameDisplayed(int64_t aDisplayTimeUs, uint32_t aQueueSize) +{ + MutexAutoLock lock(mMutex); + + mStats.mTotalFramesDisplayed++; + + // Track queue statistics + if (aQueueSize > mStats.mPeakQueueSize) { + mStats.mPeakQueueSize = aQueueSize; + } + + // Update rolling average queue size + uint64_t total = mStats.mAverageQueueSize * mStats.mTotalFramesDisplayed; + mStats.mAverageQueueSize = (total + aQueueSize) / (mStats.mTotalFramesDisplayed + 1); +} + +void VideoPlaybackStats::RecordFrameSkipped(bool aIsKeyframe) +{ + MutexAutoLock lock(mMutex); + mStats.mTotalFramesSkipped++; +} + +void VideoPlaybackStats::RecordFrameDropped() +{ + MutexAutoLock lock(mMutex); + mStats.mTotalFramesDropped++; +} + +void VideoPlaybackStats::RecordMemoryUsage(uint64_t aBytesUsed) +{ + MutexAutoLock lock(mMutex); + + if (aBytesUsed > mStats.mPeakMemoryUsage) { + mStats.mPeakMemoryUsage = aBytesUsed; + } + + mMemoryAccumulator += aBytesUsed; + mTotalMemoryMeasurements++; + + if (mTotalMemoryMeasurements > 0) { + mStats.mAverageMemoryUsage = mMemoryAccumulator / mTotalMemoryMeasurements; + } +} + +VideoPlaybackStats::PlaybackStats VideoPlaybackStats::GetStats() const +{ + MutexAutoLock lock(mMutex); + return mStats; +} + +void VideoPlaybackStats::Reset() +{ + MutexAutoLock lock(mMutex); + mStats = PlaybackStats(); + mSampleCount = 0; + mTotalDecodeTimeUs = 0; + mTotalMemoryMeasurements = 0; + mMemoryAccumulator = 0; + LOG("Statistics reset"); +} + +void VideoPlaybackStats::GetDebugInfo(nsAString& aOutput) const +{ + MutexAutoLock lock(mMutex); + + nsPrintfCString info( + "Video Playback Statistics:\n" + " Frames decoded: %llu\n" + " Frames displayed: %llu\n" + " Frames skipped: %llu\n" + " Frames dropped: %llu\n" + " Average decode time: %.2f ms\n" + " Max decode time: %.2f ms\n" + " Peak memory usage: %llu MB\n" + " Average memory usage: %llu MB\n" + " Average queue size: %u frames\n" + " Peak queue size: %u frames\n" + " Hardware accelerated: %s\n", + mStats.mTotalFramesDecoded, + mStats.mTotalFramesDisplayed, + mStats.mTotalFramesSkipped, + mStats.mTotalFramesDropped, + mStats.mAverageDecodeTimeMs, + mStats.mMaxDecodeTimeMs, + mStats.mPeakMemoryUsage / (1024 * 1024), + mStats.mAverageMemoryUsage / (1024 * 1024), + mStats.mAverageQueueSize, + mStats.mPeakQueueSize, + mStats.mIsHardwareAccelerated ? "yes" : "no"); + + aOutput.AssignASCII(info.get()); +} + +} // namespace mozilla diff --git a/dom/media/VideoPlaybackStats.h b/dom/media/VideoPlaybackStats.h new file mode 100644 index 0000000000..3da0a4cac8 --- /dev/null +++ b/dom/media/VideoPlaybackStats.h @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_VIDEOPLAYBACKSTATS_H_ +#define MOZILLA_VIDEOPLAYBACKSTATS_H_ + +#include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" +#include "mozilla/TimeStamp.h" +#include "TimeUnits.h" + +namespace mozilla { + +/** + * Tracks performance and memory statistics for video playback. + * Useful for debugging performance issues and optimizing buffering policies. + */ +class VideoPlaybackStats { +public: + struct FrameStats { + // Timing information + TimeStamp mDecodeStart; + TimeStamp mDecodeEnd; + int64_t mDecodeTimeUs = 0; + int64_t mDisplayTimeUs = 0; + + // Frame properties + bool mIsKeyframe = false; + bool mWasSkipped = false; + uint32_t mWidth = 0; + uint32_t mHeight = 0; + uint64_t mMemoryBytes = 0; + }; + + struct PlaybackStats { + // Frame statistics + uint64_t mTotalFramesDecoded = 0; + uint64_t mTotalFramesDisplayed = 0; + uint64_t mTotalFramesSkipped = 0; + uint64_t mTotalFramesDropped = 0; + + // Memory statistics (peak values) + uint64_t mPeakMemoryUsage = 0; + uint64_t mAverageMemoryUsage = 0; + uint64_t mTotalMemoryAllocated = 0; + + // Performance metrics + double mAverageDecodeTimeMs = 0.0; + double mMaxDecodeTimeMs = 0.0; + int32_t mFramesLateByMs = 0; + + // Buffer statistics + uint32_t mAverageQueueSize = 0; + uint32_t mPeakQueueSize = 0; + + // Hardware acceleration info + bool mIsHardwareAccelerated = false; + uint32_t mHardwareSkipCount = 0; + }; + + VideoPlaybackStats(); + ~VideoPlaybackStats(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoPlaybackStats) + + /** + * Record a decoded frame and its timing information. + */ + void RecordFrameDecoded(const FrameStats& aStats); + + /** + * Record a displayed frame. + */ + void RecordFrameDisplayed(int64_t aDisplayTimeUs, uint32_t aQueueSize); + + /** + * Record a skipped frame. + */ + void RecordFrameSkipped(bool aIsKeyframe); + + /** + * Record a dropped frame. + */ + void RecordFrameDropped(); + + /** + * Record memory usage snapshot. + */ + void RecordMemoryUsage(uint64_t aBytesUsed); + + /** + * Set hardware acceleration status. + */ + void SetHardwareAccelerated(bool aIsHWAccel) { + mStats.mIsHardwareAccelerated = aIsHWAccel; + } + + /** + * Get current statistics snapshot. + */ + PlaybackStats GetStats() const; + + /** + * Reset all statistics (start fresh measurement). + */ + void Reset(); + + /** + * Get human-readable statistics summary for debugging. + */ + void GetDebugInfo(nsAString& aOutput) const; + +private: + mutable Mutex mMutex; + PlaybackStats mStats; + + uint64_t mSampleCount = 0; // For computing averages + uint64_t mTotalDecodeTimeUs = 0; + uint64_t mTotalMemoryMeasurements = 0; + uint64_t mMemoryAccumulator = 0; +}; + +} // namespace mozilla + +#endif // MOZILLA_VIDEOPLAYBACKSTATS_H_ diff --git a/dom/media/moz.build b/dom/media/moz.build index ca3cdac1ca..0f4066031c 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -94,6 +94,7 @@ EXPORTS += [ 'DOMMediaStream.h', 'EncodedBufferCache.h', 'FileBlockCache.h', + 'FramePool.h', 'FrameStatistics.h', 'Intervals.h', 'Latency.h', @@ -140,6 +141,7 @@ EXPORTS += [ 'TrackUnionStream.h', 'VideoFrameContainer.h', 'VideoLimits.h', + 'VideoPlaybackStats.h', 'VideoSegment.h', 'VideoUtils.h', 'VorbisUtils.h', @@ -219,6 +221,7 @@ UNIFIED_SOURCES += [ 'MediaFormatReader.cpp', 'MediaInfo.cpp', 'MediaManager.cpp', + 'AdaptiveBufferingPolicy.cpp', 'MediaPrefs.cpp', 'MediaRecorder.cpp', 'MediaResource.cpp', @@ -244,6 +247,7 @@ UNIFIED_SOURCES += [ 'TrackUnionStream.cpp', 'VideoFrameContainer.cpp', 'VideoPlaybackQuality.cpp', + 'VideoPlaybackStats.cpp', 'VideoSegment.cpp', 'VideoStreamTrack.cpp', 'VideoTrack.cpp', From acc73f5e9e767eef1ae8688ceda1aef968a7520d Mon Sep 17 00:00:00 2001 From: ownedbywuigi Date: Mon, 11 May 2026 08:26:22 -0700 Subject: [PATCH 2/4] Fuck --- dom/media/moz.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/moz.build b/dom/media/moz.build index 0f4066031c..9bb7756f5c 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -75,6 +75,7 @@ XPIDL_MODULE = 'dom_media' EXPORTS += [ 'AbstractMediaDecoder.h', 'AccurateSeekTask.h', + 'AdaptiveBufferingPolicy.cpp', 'ADTSDecoder.h', 'ADTSDemuxer.h', 'AudioBufferUtils.h', @@ -221,7 +222,6 @@ UNIFIED_SOURCES += [ 'MediaFormatReader.cpp', 'MediaInfo.cpp', 'MediaManager.cpp', - 'AdaptiveBufferingPolicy.cpp', 'MediaPrefs.cpp', 'MediaRecorder.cpp', 'MediaResource.cpp', From e022e3b109db67fd41ad1fff4a5bd7584c43778a Mon Sep 17 00:00:00 2001 From: ownedbywuigi Date: Mon, 11 May 2026 10:09:04 -0700 Subject: [PATCH 3/4] Fuck pt 2 --- dom/media/FramePool.cpp | 3 ++- dom/media/VideoPlaybackStats.cpp | 1 + dom/media/VideoPlaybackStats.h | 4 +++- dom/media/moz.build | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dom/media/FramePool.cpp b/dom/media/FramePool.cpp index 621eb3d28c..d128a78093 100644 --- a/dom/media/FramePool.cpp +++ b/dom/media/FramePool.cpp @@ -14,7 +14,8 @@ extern LazyLogModule gMediaDecoderLog; #define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("FramePool::%s: " arg, __func__, ##__VA_ARGS__)) FramePool::FramePool(size_t aMaxFrames) - : mMaxPoolSize(aMaxFrames > 0 ? aMaxFrames : 16) + : mMutex("FramePool::mMutex") + , mMaxPoolSize(aMaxFrames > 0 ? aMaxFrames : 16) { LOG("FramePool created with max size: %zu", mMaxPoolSize); } diff --git a/dom/media/VideoPlaybackStats.cpp b/dom/media/VideoPlaybackStats.cpp index 07740b4af9..ab5c23880b 100644 --- a/dom/media/VideoPlaybackStats.cpp +++ b/dom/media/VideoPlaybackStats.cpp @@ -14,6 +14,7 @@ extern LazyLogModule gMediaDecoderLog; #define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("VideoPlaybackStats::%s: " arg, __func__, ##__VA_ARGS__)) VideoPlaybackStats::VideoPlaybackStats() + : mMutex("VideoPlaybackStats::mMutex") { LOG("VideoPlaybackStats created"); } diff --git a/dom/media/VideoPlaybackStats.h b/dom/media/VideoPlaybackStats.h index 3da0a4cac8..3b5b48e890 100644 --- a/dom/media/VideoPlaybackStats.h +++ b/dom/media/VideoPlaybackStats.h @@ -11,6 +11,8 @@ #include "mozilla/TimeStamp.h" #include "TimeUnits.h" +class nsAString; + namespace mozilla { /** @@ -61,7 +63,6 @@ class VideoPlaybackStats { }; VideoPlaybackStats(); - ~VideoPlaybackStats(); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoPlaybackStats) @@ -113,6 +114,7 @@ class VideoPlaybackStats { void GetDebugInfo(nsAString& aOutput) const; private: + ~VideoPlaybackStats(); mutable Mutex mMutex; PlaybackStats mStats; diff --git a/dom/media/moz.build b/dom/media/moz.build index 9bb7756f5c..939755a483 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -75,7 +75,6 @@ XPIDL_MODULE = 'dom_media' EXPORTS += [ 'AbstractMediaDecoder.h', 'AccurateSeekTask.h', - 'AdaptiveBufferingPolicy.cpp', 'ADTSDecoder.h', 'ADTSDemuxer.h', 'AudioBufferUtils.h', @@ -189,6 +188,7 @@ EXPORTS.mozilla.dom += [ UNIFIED_SOURCES += [ 'AccurateSeekTask.cpp', + 'AdaptiveBufferingPolicy.cpp', 'ADTSDecoder.cpp', 'ADTSDemuxer.cpp', 'AudioCaptureStream.cpp', @@ -207,6 +207,7 @@ UNIFIED_SOURCES += [ 'DOMMediaStream.cpp', 'EncodedBufferCache.cpp', 'FileBlockCache.cpp', + 'FramePool.cpp', 'GetUserMediaRequest.cpp', 'GraphDriver.cpp', 'Latency.cpp', From b0fb20395a4d655c76eb57cc3698a0a76a5024cd Mon Sep 17 00:00:00 2001 From: ownedbywuigi Date: Mon, 11 May 2026 10:19:11 -0700 Subject: [PATCH 4/4] Fuck pt 3 --- dom/media/FramePool.cpp | 33 ++++++++++++++++++--------------- dom/media/FramePool.h | 15 ++++++++------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/dom/media/FramePool.cpp b/dom/media/FramePool.cpp index d128a78093..08e0fc39d6 100644 --- a/dom/media/FramePool.cpp +++ b/dom/media/FramePool.cpp @@ -11,7 +11,9 @@ namespace mozilla { extern LazyLogModule gMediaDecoderLog; +#undef LOG #define LOG(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, ("FramePool::%s: " arg, __func__, ##__VA_ARGS__)) +#define LOGV(arg, ...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Verbose, ("FramePool::%s: " arg, __func__, ##__VA_ARGS__)) FramePool::FramePool(size_t aMaxFrames) : mMutex("FramePool::mMutex") @@ -31,18 +33,19 @@ RefPtr FramePool::AcquireFrame(const gfx::IntSize& aSize) // Try to reuse a frame from pool if (!mFramePool.IsEmpty()) { - RefPtr frame = mFramePool.PopBack(); + RefPtr frame = mFramePool[mFramePool.Length() - 1]; + mFramePool.RemoveElementAt(mFramePool.Length() - 1); mFramesReused++; - LOGV("Reused frame from pool, pool size now: %zu", mFramePool.length()); + LOGV("Reused frame from pool, pool size now: %zu", mFramePool.Length()); return frame; } - // Allocate new frame - RefPtr frame = new layers::RecyclingPlanarYCbCrImage(); + // No pooled frame available; we cannot reliably construct a specific + // Image subclass here (requires platform-specific recycle bin). Return + // nullptr and let the caller allocate an appropriate Image if needed. mTotalAllocated++; - - LOG("Allocated new frame, total: %zu", mTotalAllocated); - return frame; + LOG("No pooled frame available, returning nullptr (alloc attempts: %zu)", mTotalAllocated); + return nullptr; } void FramePool::ReleaseFrame(layers::Image* aFrame) @@ -54,16 +57,16 @@ void FramePool::ReleaseFrame(layers::Image* aFrame) MutexAutoLock lock(mMutex); // Only pool frame if we haven't reached max capacity - if (mFramePool.length() < mMaxPoolSize) { - mFramePool.AppendElement(aFrame); - + if (mFramePool.Length() < mMaxPoolSize) { + mFramePool.AppendElement(RefPtr(aFrame)); + // Track peak pool size - if (mFramePool.length() > mPeakPoolSize) { - mPeakPoolSize = mFramePool.length(); + if (mFramePool.Length() > mPeakPoolSize) { + mPeakPoolSize = mFramePool.Length(); } - + LOGV("Released frame to pool, pool size: %zu/%zu", - mFramePool.length(), mMaxPoolSize); + mFramePool.Length(), mMaxPoolSize); } else { // Frame exceeds pool capacity, let it be released LOGV("Frame pool at capacity, releasing frame"); @@ -80,7 +83,7 @@ void FramePool::Clear() size_t FramePool::PoolSize() const { MutexAutoLock lock(mMutex); - return mFramePool.length(); + return mFramePool.Length(); } size_t FramePool::TotalFrames() const diff --git a/dom/media/FramePool.h b/dom/media/FramePool.h index 56775702a4..836edd7543 100644 --- a/dom/media/FramePool.h +++ b/dom/media/FramePool.h @@ -31,7 +31,8 @@ class Image; class FramePool { public: explicit FramePool(size_t aMaxFrames = 16); - ~FramePool(); + private: + ~FramePool(); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FramePool) @@ -73,18 +74,18 @@ class FramePool { }; Stats GetStats() const { - MutexAutoLock lock(mMutex); - return {mFramePool.length(), mTotalAllocated, mFramesReused, mPeakPoolSize}; + MutexAutoLock lock(mMutex); + return {mFramePool.Length(), mTotalAllocated, mFramesReused, mPeakPoolSize}; } /** * Reset statistics counters. */ void ResetStats() { - MutexAutoLock lock(mMutex); - mFramesReused = 0; - mTotalAllocated = 0; - mPeakPoolSize = 0; + MutexAutoLock lock(mMutex); + mFramesReused = 0; + mTotalAllocated = 0; + mPeakPoolSize = 0; } private: