Skip to content

Commit

Permalink
fix #528
Browse files Browse the repository at this point in the history
  • Loading branch information
kingslay committed Nov 10, 2023
1 parent 2630af4 commit fc0736f
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 123 deletions.
4 changes: 1 addition & 3 deletions Sources/KSPlayer/AVPlayer/KSAVPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public class KSAVPlayer {
}

public var naturalSize: CGSize = .zero
public var metadata = [String: String]()
public let dynamicInfo: DynamicInfo? = nil
@available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
public var playbackCoordinator: AVPlaybackCoordinator {
playerView.player.playbackCoordinator
Expand All @@ -126,8 +126,6 @@ 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 {
Expand Down
22 changes: 11 additions & 11 deletions Sources/KSPlayer/AVPlayer/KSOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -327,53 +327,53 @@ open class KSOptions {
}

// private var lastMediaTime = CACurrentMediaTime()
open func videoClockSync(main: KSClock, nextVideoTime: TimeInterval, fps: Float, frameCount: Int) -> ClockProcessType {
open func videoClockSync(main: KSClock, nextVideoTime: TimeInterval, fps: Float, frameCount: Int) -> (Double, ClockProcessType) {
var desire = main.getTime() - videoDelay
#if !os(macOS)
desire -= AVAudioSession.sharedInstance().outputLatency
#endif
let diff = nextVideoTime - desire
// print("[video] video diff \(diff) audio \(main.positionTime) interval \(CACurrentMediaTime() - main.lastMediaTime) render interval \(CACurrentMediaTime() - lastMediaTime)")
if diff > 10 || diff < -10 {
return .next
return (diff, .next)
} else if diff > 1 / Double(fps * 2) {
videoClockDelayCount = 0
return .remain
return (diff, .remain)
} else {
if diff < -4 / Double(fps) {
videoClockDelayCount += 1
let log = "[video] video delay=\(diff), clock=\(desire), delay count=\(videoClockDelayCount), frameCount=\(frameCount)"
if frameCount == 1 {
if diff < -1, videoClockDelayCount % 10 == 0 {
KSLog("\(log) drop gop Packet")
return .dropGOPPacket
return (diff, .dropGOPPacket)
} else if videoClockDelayCount % 5 == 0 {
KSLog("\(log) drop next frame")
return .dropNextFrame
return (diff, .dropNextFrame)
} else {
return .next
return (diff, .next)
}
} else {
if diff < -8, videoClockDelayCount % 100 == 0 {
KSLog("\(log) seek video track")
return .seek
return (diff, .seek)
}
if diff < -1, videoClockDelayCount % 10 == 0 {
KSLog("\(log) flush video track")
return .flush
return (diff, .flush)
}
if videoClockDelayCount % 2 == 0 {
KSLog("\(log) drop next frame")
return .dropNextFrame
return (diff, .dropNextFrame)
} else {
return .next
return (diff, .next)
}
}
} else {
videoClockDelayCount = 0
// print("[video] video interval \(CACurrentMediaTime() - lastMediaTime)")
// lastMediaTime = CACurrentMediaTime()
return .next
return (diff, .next)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/KSPlayer/AVPlayer/KSPlayerLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,10 @@ extension KSPlayerLayer {
} else {
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyPlaybackDuration] = player.duration
}
if MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyTitle] == nil, let title = player.metadata["title"] {
if MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyTitle] == nil, let title = player.dynamicInfo?.metadata["title"] {
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyTitle] = title
}
if MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyArtist] == nil, let artist = player.metadata["artist"] {
if MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyArtist] == nil, let artist = player.dynamicInfo?.metadata["artist"] {
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPMediaItemPropertyArtist] = artist
}
var current: [MPNowPlayingInfoLanguageOption] = []
Expand Down
107 changes: 70 additions & 37 deletions Sources/KSPlayer/AVPlayer/MediaPlayerProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,47 @@ import AppKit
public protocol MediaPlayback: AnyObject {
var duration: TimeInterval { get }
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()
func seek(time: TimeInterval, completion: @escaping ((Bool) -> Void))
}

public class DynamicInfo {
private let metadataBlock: () -> [String: String]
private let bytesReadBlock: () -> Int64
private let audioBitrateBlock: () -> Int
private let videoBitrateBlock: () -> Int
public var metadata: [String: String] {
metadataBlock()
}

public var bytesRead: Int64 {
bytesReadBlock()
}

public var audioBitrate: Int {
audioBitrateBlock()
}

public var videoBitrate: Int {
videoBitrateBlock()
}

@Published
public var displayFPS = 0.0
public var audioVideoSyncDiff = 0.0
public var droppedVideoFrameCount = UInt32(0)
public var droppedVideoPacketCount = UInt32(0)
init(metadata: @escaping () -> [String: String], bytesRead: @escaping () -> Int64, audioBitrate: @escaping () -> Int, videoBitrate: @escaping () -> Int) {
metadataBlock = metadata
bytesReadBlock = bytesRead
audioBitrateBlock = audioBitrate
videoBitrateBlock = videoBitrate
}
}

public protocol MediaPlayerProtocol: MediaPlayback {
var delegate: MediaPlayerDelegate? { get set }
var view: UIView? { get }
Expand All @@ -48,6 +79,7 @@ public protocol MediaPlayerProtocol: MediaPlayback {
var playbackCoordinator: AVPlaybackCoordinator { get }
@available(tvOS 14.0, *)
var pipController: KSPictureInPictureController? { get }
var dynamicInfo: DynamicInfo? { get }
init(url: URL, options: KSOptions)
func replace(url: URL, options: KSOptions)
func play()
Expand Down Expand Up @@ -173,40 +205,7 @@ public extension MediaPlayerTrack {
}

var naturalSize: CGSize {
formatDescription.map { description in
let dimensions = description.dimensions
let aspectRatio = aspectRatio
return CGSize(width: Int(dimensions.width), height: Int(CGFloat(dimensions.height) * aspectRatio.height / aspectRatio.width))
} ?? .zero
}

var aspectRatio: CGSize {
if let formatDescription, let dictionary = CMFormatDescriptionGetExtensions(formatDescription) as NSDictionary? {
if let ratio = dictionary[kCVImageBufferPixelAspectRatioKey] as? NSDictionary,
let horizontal = (ratio[kCVImageBufferPixelAspectRatioHorizontalSpacingKey] as? NSNumber)?.intValue,
let vertical = (ratio[kCVImageBufferPixelAspectRatioVerticalSpacingKey] as? NSNumber)?.intValue,
horizontal > 0, vertical > 0
{
return CGSize(width: horizontal, height: vertical)
}
}
return CGSize(width: 1, height: 1)
}

var depth: Int32 {
if let formatDescription, let dictionary = CMFormatDescriptionGetExtensions(formatDescription) as NSDictionary? {
return dictionary[kCMFormatDescriptionExtension_Depth] as? Int32 ?? 24
} else {
return 24
}
}

var fullRangeVideo: Bool {
if let formatDescription, let dictionary = CMFormatDescriptionGetExtensions(formatDescription) as NSDictionary? {
return dictionary[kCMFormatDescriptionExtension_FullRangeVideo] as? Bool ?? false
} else {
return false
}
formatDescription?.naturalSize ?? .zero
}

var colorPrimaries: String? {
Expand Down Expand Up @@ -264,6 +263,40 @@ public extension CMFormatDescription {
return nil
}
}

var naturalSize: CGSize {
let aspectRatio = aspectRatio
return CGSize(width: Int(dimensions.width), height: Int(CGFloat(dimensions.height) * aspectRatio.height / aspectRatio.width))
}

var aspectRatio: CGSize {
if let dictionary = CMFormatDescriptionGetExtensions(self) as NSDictionary? {
if let ratio = dictionary[kCVImageBufferPixelAspectRatioKey] as? NSDictionary,
let horizontal = (ratio[kCVImageBufferPixelAspectRatioHorizontalSpacingKey] as? NSNumber)?.intValue,
let vertical = (ratio[kCVImageBufferPixelAspectRatioVerticalSpacingKey] as? NSNumber)?.intValue,
horizontal > 0, vertical > 0
{
return CGSize(width: horizontal, height: vertical)
}
}
return CGSize(width: 1, height: 1)
}

var depth: Int32 {
if let dictionary = CMFormatDescriptionGetExtensions(self) as NSDictionary? {
return dictionary[kCMFormatDescriptionExtension_Depth] as? Int32 ?? 24
} else {
return 24
}
}

var fullRangeVideo: Bool {
if let dictionary = CMFormatDescriptionGetExtensions(self) as NSDictionary? {
return dictionary[kCMFormatDescriptionExtension_FullRangeVideo] as? Bool ?? false
} else {
return false
}
}
}

func setHttpProxy() {
Expand Down
71 changes: 48 additions & 23 deletions Sources/KSPlayer/MEPlayer/FFmpegAssetTrack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import FFmpegKit
import Libavformat
public class FFmpegAssetTrack: MediaPlayerTrack {
public private(set) var trackID: Int32 = 0
public let codecName: String
public var name: String = ""
public private(set) var language: String?
public private(set) var nominalFrameRate: Float = 0
public private(set) var bitRate: Int64 = 0
public private(set) var description: String
public let mediaType: AVFoundation.AVMediaType
public let formatName: String?
private var stream: UnsafeMutablePointer<AVStream>?
var startTime = TimeInterval(0)
var codecpar: AVCodecParameters
var timebase: Timebase = .defaultValue
let bitsPerRawSample: Int32
// audio
public let audioDescriptor: AudioDescriptor?
// subtitle
Expand All @@ -34,6 +35,32 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
public let formatDescription: CMFormatDescription?
var closedCaptionsTrack: FFmpegAssetTrack?
let isConvertNALSize: Bool
public var description: String {
var description = codecName
if let audioDescriptor {
description += ", \(audioDescriptor.sampleRate)Hz"
description += ", \(audioDescriptor.channel.description)"
}
if let formatDescription {
if mediaType == .video {
let naturalSize = formatDescription.naturalSize
description += ", \(Int(naturalSize.width))x\(Int(naturalSize.height))"
description += ", \(nominalFrameRate) fps"
}
}
if let formatName {
description += ", \(formatName)"
}

if bitRate > 0 {
description += ", \(bitRate)BPS"
}
if bitsPerRawSample > 0 {
description += ", (\(bitsPerRawSample) bit)"
}
return description
}

convenience init?(stream: UnsafeMutablePointer<AVStream>) {
let codecpar = stream.pointee.codecpar.pointee
self.init(codecpar: codecpar)
Expand All @@ -42,9 +69,7 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
if let value = metadata["variant_bitrate"] ?? metadata["BPS"], let bitRate = Int64(value) {
self.bitRate = bitRate
}
if bitRate > 0 {
description += ", \(bitRate)BPS"
}

if stream.pointee.side_data?.pointee.type == AV_PKT_DATA_DOVI_CONF {
dovi = stream.pointee.side_data?.pointee.data.withMemoryRebound(to: DOVIDecoderConfigurationRecord.self, capacity: 1) { $0 }.pointee
}
Expand Down Expand Up @@ -80,19 +105,25 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
}
}

if codecpar.codec_type == AVMEDIA_TYPE_VIDEO {
description += ", \(nominalFrameRate) fps"
if let value = metadata["language"] {
language = Locale.current.localizedString(forLanguageCode: value)
} else {
language = nil
}
language = metadata["language"]
if let value = metadata["title"] {
name = value
} else {
name = language ?? mediaType.rawValue
name = codecName
if let language {
name += "(\(language))"
}
}
description = name + ", " + description
// AV_DISPOSITION_DEFAULT
if mediaType == .subtitle {
isEnabled = !isImageSubtitle || stream.pointee.disposition & AV_DISPOSITION_FORCED == AV_DISPOSITION_FORCED
if stream.pointee.disposition & AV_DISPOSITION_HEARING_IMPAIRED == AV_DISPOSITION_HEARING_IMPAIRED {
name += "(hearing impaired)"
}
}
// var buf = [Int8](repeating: 0, count: 256)
// avcodec_string(&buf, buf.count, codecpar, 0)
Expand All @@ -103,13 +134,16 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
bitRate = codecpar.bit_rate
// codec_tag byte order is LSB first CMFormatDescription.MediaSubType(rawValue: codecpar.codec_tag.bigEndian)
let codecType = codecpar.codec_id.mediaSubType
var description = ""
var codecName = ""
if let descriptor = avcodec_descriptor_get(codecpar.codec_id) {
description += String(cString: descriptor.pointee.name)
codecName += String(cString: descriptor.pointee.name)
if let profile = descriptor.pointee.profiles {
description += " (\(String(cString: profile.pointee.name)))"
codecName += " (\(String(cString: profile.pointee.name)))"
}
} else {
codecName = ""
}
self.codecName = codecName
fieldOrder = FFmpegFieldOrder(rawValue: UInt8(codecpar.field_order.rawValue)) ?? .unknown
var formatDescriptionOut: CMFormatDescription?
if codecpar.codec_type == AVMEDIA_TYPE_AUDIO {
Expand All @@ -123,8 +157,6 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
let formatFlags = ((sampleFormat == AV_SAMPLE_FMT_FLT || sampleFormat == AV_SAMPLE_FMT_DBL) ? kAudioFormatFlagIsFloat : sampleFormat == AV_SAMPLE_FMT_U8 ? 0 : kAudioFormatFlagIsSignedInteger) | kAudioFormatFlagIsPacked
var audioStreamBasicDescription = AudioStreamBasicDescription(mSampleRate: Float64(codecpar.sample_rate), mFormatID: codecType.rawValue, mFormatFlags: formatFlags, mBytesPerPacket: bytesPerSample * channelsPerFrame, mFramesPerPacket: 1, mBytesPerFrame: bytesPerSample * channelsPerFrame, mChannelsPerFrame: channelsPerFrame, mBitsPerChannel: bytesPerSample * 8, mReserved: 0)
_ = CMAudioFormatDescriptionCreate(allocator: kCFAllocatorDefault, asbd: &audioStreamBasicDescription, layoutSize: 0, layout: nil, magicCookieSize: 0, magicCookie: nil, extensions: nil, formatDescriptionOut: &formatDescriptionOut)
description += ", \(codecpar.sample_rate)Hz"
description += ", \(codecpar.ch_layout.description)"
if let name = av_get_sample_fmt_name(sampleFormat) {
formatName = String(cString: name)
} else {
Expand Down Expand Up @@ -195,7 +227,6 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
formatName = nil
}
let naturalSize = CGSize(width: Int(codecpar.width), height: Int(CGFloat(codecpar.height) * sar.height / sar.width))
description += ", \(Int(naturalSize.width))x\(Int(naturalSize.height))"
} else if codecpar.codec_type == AVMEDIA_TYPE_SUBTITLE {
mediaType = .subtitle
audioDescriptor = nil
Expand All @@ -206,14 +237,8 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
return nil
}
formatDescription = formatDescriptionOut
if let formatName {
description += ", \(formatName)"
}
if codecpar.bits_per_raw_sample != 0 {
description += ", (\(codecpar.bits_per_raw_sample) bit)"
}
bitsPerRawSample = codecpar.bits_per_raw_sample
isImageSubtitle = [AV_CODEC_ID_DVD_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT, AV_CODEC_ID_HDMV_PGS_SUBTITLE].contains(codecpar.codec_id)
self.description = description
trackID = 0
}

Expand All @@ -238,6 +263,6 @@ public class FFmpegAssetTrack: MediaPlayerTrack {
extension FFmpegAssetTrack {
var pixelFormatType: OSType? {
let format = AVPixelFormat(codecpar.format)
return format.osType(fullRange: fullRangeVideo)
return format.osType(fullRange: formatDescription?.fullRangeVideo ?? false)
}
}
Loading

0 comments on commit fc0736f

Please sign in to comment.