From 6c24a94d5997789b5396efbb0644d3adcf74da7c Mon Sep 17 00:00:00 2001
From: kintan <kingslay@icloud.com>
Date: Sun, 5 Nov 2023 22:56:57 +0800
Subject: [PATCH] add videoBitrate and audioBitrate

---
 Sources/KSPlayer/AVPlayer/KSAVPlayer.swift    |  3 ++-
 .../AVPlayer/MediaPlayerProtocol.swift        |  2 ++
 Sources/KSPlayer/MEPlayer/KSMEPlayer.swift    |  4 ++++
 Sources/KSPlayer/MEPlayer/MEPlayerItem.swift  | 20 +++++++++----------
 .../KSPlayer/MEPlayer/MEPlayerItemTrack.swift | 17 ++++++++++++++++
 Sources/KSPlayer/MEPlayer/Model.swift         | 12 +++++++++++
 .../MEPlayer/VideoToolboxDecode.swift         |  6 +++---
 .../KSPlayer/SwiftUI/KSVideoPlayerView.swift  |  2 ++
 8 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/Sources/KSPlayer/AVPlayer/KSAVPlayer.swift b/Sources/KSPlayer/AVPlayer/KSAVPlayer.swift
index 786bfa60c..a37c866f2 100644
--- a/Sources/KSPlayer/AVPlayer/KSAVPlayer.swift
+++ b/Sources/KSPlayer/AVPlayer/KSAVPlayer.swift
@@ -126,7 +126,8 @@ public class KSAVPlayer {
     public private(set) var duration: TimeInterval = 0
     public private(set) var fileSize: Double = 0
     public private(set) var playableTime: TimeInterval = 0
-
+    public var audioBitrate: Int = 0
+    public var videoBitrate: Int = 0
     public var playbackRate: Float = 1 {
         didSet {
             if playbackState == .playing {
diff --git a/Sources/KSPlayer/AVPlayer/MediaPlayerProtocol.swift b/Sources/KSPlayer/AVPlayer/MediaPlayerProtocol.swift
index 248e58f0c..0a0ed68ee 100644
--- a/Sources/KSPlayer/AVPlayer/MediaPlayerProtocol.swift
+++ b/Sources/KSPlayer/AVPlayer/MediaPlayerProtocol.swift
@@ -18,6 +18,8 @@ public protocol MediaPlayback: AnyObject {
     var fileSize: Double { get }
     var metadata: [String: String] { get }
     var naturalSize: CGSize { get }
+    var audioBitrate: Int { get }
+    var videoBitrate: Int { get }
     var currentPlaybackTime: TimeInterval { get }
     func prepareToPlay()
     func shutdown()
diff --git a/Sources/KSPlayer/MEPlayer/KSMEPlayer.swift b/Sources/KSPlayer/MEPlayer/KSMEPlayer.swift
index 64b3dddc0..748730884 100644
--- a/Sources/KSPlayer/MEPlayer/KSMEPlayer.swift
+++ b/Sources/KSPlayer/MEPlayer/KSMEPlayer.swift
@@ -327,6 +327,10 @@ extension KSMEPlayer: MediaPlayerProtocol {
 
     public var seekable: Bool { playerItem.seekable }
 
+    public var videoBitrate: Int { playerItem.videoBitrate }
+
+    public var audioBitrate: Int { playerItem.audioBitrate }
+
     public func seek(time: TimeInterval, completion: @escaping ((Bool) -> Void)) {
         let time = max(time, 0)
         playbackState = .seeking
diff --git a/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift b/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift
index f8ed4c18d..89815bc30 100644
--- a/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift
+++ b/Sources/KSPlayer/MEPlayer/MEPlayerItem.swift
@@ -569,6 +569,14 @@ extension MEPlayerItem {
 // MARK: MediaPlayback
 
 extension MEPlayerItem: MediaPlayback {
+    var videoBitrate: Int {
+        Int(8 * (videoTrack?.bitrate ?? 0))
+    }
+
+    var audioBitrate: Int {
+        Int(8 * (audioTrack?.bitrate ?? 0))
+    }
+
     var seekable: Bool {
         guard let formatCtx else {
             return false
@@ -766,11 +774,7 @@ extension MEPlayerItem: OutputRenderSourceDelegate {
         case .dropNextPacket:
             if let videoTrack = videoTrack as? AsyncPlayerItemTrack {
                 _ = videoTrack.packetQueue.pop { item, _ -> Bool in
-                    if let corePacket = item.corePacket {
-                        return corePacket.pointee.flags & AV_PKT_FLAG_KEY != AV_PKT_FLAG_KEY
-                    } else {
-                        return false
-                    }
+                    !item.isKeyFrame
                 }
             }
         case .dropGOPPacket:
@@ -778,11 +782,7 @@ extension MEPlayerItem: OutputRenderSourceDelegate {
                 var packet: Packet? = nil
                 repeat {
                     packet = videoTrack.packetQueue.pop { item, _ -> Bool in
-                        if let corePacket = item.corePacket {
-                            return corePacket.pointee.flags & AV_PKT_FLAG_KEY != AV_PKT_FLAG_KEY
-                        } else {
-                            return false
-                        }
+                        !item.isKeyFrame
                     }
                 } while packet != nil
             }
diff --git a/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift b/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift
index 96368488d..dfbe6f03f 100644
--- a/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift
+++ b/Sources/KSPlayer/MEPlayer/MEPlayerItemTrack.swift
@@ -107,7 +107,24 @@ class SyncPlayerItemTrack<Frame: MEFrame>: PlayerItemTrackProtocol, CustomString
         outputRenderQueue.shutdown()
     }
 
+    private var lastPacketBytes = Int32(0)
+    private var lastPacketSeconds = Double(-1)
+    var bitrate = Double(0)
     fileprivate func doDecode(packet: Packet) {
+        if packet.isKeyFrame, packet.assetTrack.mediaType != .subtitle {
+            let seconds = packet.seconds
+            let diff = seconds - lastPacketSeconds
+            if lastPacketSeconds < 0 || diff < 0 {
+                bitrate = 0
+                lastPacketBytes = 0
+                lastPacketSeconds = seconds
+            } else if diff > 0.5 {
+                bitrate = Double(lastPacketBytes) / diff
+                lastPacketBytes = 0
+                lastPacketSeconds = seconds
+            }
+        }
+        lastPacketBytes += packet.size
         let decoder = decoderMap.value(for: packet.assetTrack.trackID, default: makeDecode(assetTrack: packet.assetTrack))
         decoder.decodeFrame(from: packet) { [weak self] result in
             guard let self else {
diff --git a/Sources/KSPlayer/MEPlayer/Model.swift b/Sources/KSPlayer/MEPlayer/Model.swift
index f21c96840..54bad2d21 100644
--- a/Sources/KSPlayer/MEPlayer/Model.swift
+++ b/Sources/KSPlayer/MEPlayer/Model.swift
@@ -176,6 +176,18 @@ final class Packet: ObjectQueueItem {
     var size: Int32 = 0
     var assetTrack: FFmpegAssetTrack!
     private(set) var corePacket = av_packet_alloc()
+    var isKeyFrame: Bool {
+        if let corePacket {
+            return corePacket.pointee.flags & AV_PKT_FLAG_KEY == AV_PKT_FLAG_KEY
+        } else {
+            return false
+        }
+    }
+
+    var seconds: Double {
+        assetTrack.timebase.cmtime(for: position).seconds
+    }
+
     func fill() {
         guard let corePacket else {
             return
diff --git a/Sources/KSPlayer/MEPlayer/VideoToolboxDecode.swift b/Sources/KSPlayer/MEPlayer/VideoToolboxDecode.swift
index f673c096e..b8d7b40f5 100644
--- a/Sources/KSPlayer/MEPlayer/VideoToolboxDecode.swift
+++ b/Sources/KSPlayer/MEPlayer/VideoToolboxDecode.swift
@@ -50,7 +50,7 @@ class VideoToolboxDecode: DecodeProtocol {
                 }
                 guard status == noErr else {
                     if status == kVTInvalidSessionErr || status == kVTVideoDecoderMalfunctionErr || status == kVTVideoDecoderBadDataErr {
-                        if corePacket.flags & AV_PKT_FLAG_KEY == 1 {
+                        if packet.isKeyFrame {
                             completionHandler(.failure(NSError(errorCode: .codecVideoReceiveFrame, avErrorCode: status)))
                         }
                     }
@@ -59,7 +59,7 @@ class VideoToolboxDecode: DecodeProtocol {
                 let frame = VideoVTBFrame(fps: session.assetTrack.nominalFrameRate)
                 frame.corePixelBuffer = imageBuffer
                 frame.timebase = session.assetTrack.timebase
-                if packetFlags & AV_PKT_FLAG_KEY == 1, packetFlags & AV_PKT_FLAG_DISCARD != 0, self.lastPosition > 0 {
+                if packet.isKeyFrame, packetFlags & AV_PKT_FLAG_DISCARD != 0, self.lastPosition > 0 {
                     self.startTime = self.lastPosition - pts
                 }
                 self.lastPosition = max(self.lastPosition, pts)
@@ -72,7 +72,7 @@ class VideoToolboxDecode: DecodeProtocol {
             if status == noErr {
                 VTDecompressionSessionWaitForAsynchronousFrames(session.decompressionSession)
             } else if status == kVTInvalidSessionErr || status == kVTVideoDecoderMalfunctionErr || status == kVTVideoDecoderBadDataErr {
-                if corePacket.flags & AV_PKT_FLAG_KEY == 1 {
+                if packet.isKeyFrame {
                     throw NSError(errorCode: .codecVideoReceiveFrame, avErrorCode: status)
                 } else {
                     // 解决从后台切换到前台,解码失败的问题
diff --git a/Sources/KSPlayer/SwiftUI/KSVideoPlayerView.swift b/Sources/KSPlayer/SwiftUI/KSVideoPlayerView.swift
index 1b6a67784..8642da4e5 100644
--- a/Sources/KSPlayer/SwiftUI/KSVideoPlayerView.swift
+++ b/Sources/KSPlayer/SwiftUI/KSVideoPlayerView.swift
@@ -523,6 +523,8 @@ struct VideoSettingView: View {
                 subtitleModel.searchSubtitle(query: subtitleTitle, languages: ["zh-cn"])
             }
             Text("Stream Type: \((videoTracks?.first { $0.isEnabled }?.fieldOrder ?? .progressive).description)")
+            Text("Audio bitrate: \(config.playerLayer?.player.audioBitrate ?? 0) b/s")
+            Text("Video bitrate: \(config.playerLayer?.player.videoBitrate ?? 0) b/s")
             if let fileSize = config.playerLayer?.player.fileSize, fileSize > 0 {
                 Text("File Size: \(String(format: "%.1f", fileSize / 1_000_000))MB")
             }