Skip to content

Commit

Permalink
Gapless playback fixes (seek to completion still broken)
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-venugopal committed Dec 21, 2024
1 parent 4fd725a commit c695b51
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 88 deletions.
Binary file not shown.
26 changes: 0 additions & 26 deletions Source/Core/FFmpeg/Wrappers/Decoding/FFmpegAudioCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -239,30 +239,4 @@ class FFmpegAudioCodec {
func flushBuffers() {
context.flushBuffers()
}

#if DEBUG

///
/// Print some codec info to the console.
/// May be used to verify that the codec was properly read / initialized.
/// Useful for debugging purposes.
///
func printInfo() {

print("\n---------- Codec Info ----------\n")

print(String(format: "Codec ID: %d", self.id))
print(String(format: "Codec Name: %@", self.name))
print(String(format: "Codec Long Name: %@", longName))
print(String(format: "Sample Rate: %7d", sampleRate))
print(String(format: "Sample Format: %7@", sampleFormat.name))
print(String(format: "Planar Samples ?: %7@", String(sampleFormat.isPlanar)))
print(String(format: "Sample Size: %7d", sampleFormat.size))
print(String(format: "Channels: %7d", channelCount))

print("---------------------------------\n")
}

#endif

}
22 changes: 0 additions & 22 deletions Source/Core/FFmpeg/Wrappers/Demuxing/FFmpegAudioStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,26 +104,4 @@ class FFmpegAudioStream: FFmpegStreamProtocol {
self.index = pointer.pointee.index
self.duration = timeBaseDuration > 0 ? Double(timeBaseDuration) * timeBaseRatio : nil
}

#if DEBUG

///
/// Print some stream info to the console.
/// May be used to verify that the stream was properly read / initialized.
/// Useful for debugging purposes.
///
func printInfo() {

print("\n---------- Audio Stream Info ----------\n")

print(String(format: "Index: %7d", index))
print(String(format: "Duration: %@", duration != nil ? String(format: "%7.2lf", duration!) : "<Unknown>" ))
print(String(format: "Time Base: %d / %d", timeBase.num, timeBase.den))
print(String(format: "Total Frames: %7ld", timeBaseDuration))

print("---------------------------------\n")
}

#endif

}
20 changes: 0 additions & 20 deletions Source/Core/FFmpeg/Wrappers/Demuxing/FFmpegImageStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,4 @@ class FFmpegImageStream: FFmpegStreamProtocol {
self.pointer = pointer
self.index = pointer.pointee.index
}

#if DEBUG

///
/// Print some stream info to the console.
/// May be used to verify that the stream was properly read / initialized.
/// Useful for debugging purposes.
///
func printInfo() {

print("\n---------- Stream Info ----------\n")

print(String(format: "Index: %7d", index))
print(String(format: "Media Type: %7d", mediaType.rawValue))

print("---------------------------------\n")
}

#endif

}
2 changes: 1 addition & 1 deletion Source/Core/Globals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ let appPersistentState: AppPersistentState = {
if let legacyPersistentState: LegacyAppPersistentState = persistenceManager.load(objectOfType: LegacyAppPersistentState.self) {

// Attempt migration and return the mapped instance.
print("Mapped persistent state from app version: \(appVersionString)\n")
// print("Mapped persistent state from app version: \(appVersionString)\n")
return AppPersistentState(legacyAppPersistentState: legacyPersistentState)
}
}
Expand Down
24 changes: 20 additions & 4 deletions Source/Core/Playback/Delegates/PlaybackDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,26 @@ class PlaybackDelegate: PlaybackDelegateProtocol {

if isInGaplessPlaybackMode {

if let currentSession = PlaybackSession.currentSession {
gaplessTrackPlaybackCompleted(currentSession)
let beginTrack = playQueueDelegate.currentTrack
let beginState = player.state

if let subsequentTrack = playQueueDelegate.subsequent() {

do {
try trackReader.prepareForPlayback(track: subsequentTrack)

} catch {

Messenger.publish(TrackNotPlayedNotification(oldTrack: beginTrack, errorTrack: subsequentTrack,
error: error as? DisplayableError ?? TrackNotPlayableError(subsequentTrack.file)))
}

player.playGapless(tracks: playQueueDelegate.tracksPendingPlayback)
}

messenger.publish(TrackTransitionNotification(beginTrack: beginTrack, beginState: beginState,
endTrack: playQueueDelegate.currentTrack, endState: player.state))

} else {
doTrackPlaybackCompleted()
}
Expand Down Expand Up @@ -501,15 +517,15 @@ class PlaybackDelegate: PlaybackDelegateProtocol {

if playQueueDelegate.repeatMode == .all {

print("\(Date.nowTimestampString) - finishedLastTrack, repeating all ...")
// print("\(Date.nowTimestampString) - finishedLastTrack, repeating all ...")

doBeginGaplessPlayback(currentTrack: beginTrack)
return
}

} else if let subsequentTrack = playQueueDelegate.subsequent() {

print("\(Date.nowTimestampString) - subsequentTrack: \(subsequentTrack)")
// print("\(Date.nowTimestampString) - subsequentTrack: \(subsequentTrack)")

session.track = subsequentTrack
trackReader.loadArtAsync(for: subsequentTrack, immediate: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension FFmpegScheduler {
currentSession: currentSession)
}

fileprivate func doPlayGapless(firstTrack: Track, fromTime time: Double? = nil, otherTracks: [Track], currentSession: PlaybackSession) {
fileprivate func doPlayGapless(firstTrack: Track, fromTime time: Double? = nil, otherTracks: [Track], currentSession: PlaybackSession, beginPlayback: Bool = true) {

guard let thePlaybackCtx = firstTrack.playbackContext as? FFmpegPlaybackContext,
let decoder = thePlaybackCtx.decoder else {
Expand All @@ -41,8 +41,11 @@ extension FFmpegScheduler {
initiateGaplessDecodingAndScheduling(for: currentSession, context: thePlaybackCtx, decoder: decoder, from: time)

// Check that at least one audio buffer was successfully scheduled, before beginning playback.
if let bufferCount = gaplessScheduledBufferCounts[firstTrack], bufferCount.isPositive {
playerNode.play()
if let bufferCount = gaplessScheduledBufferCounts[firstTrack], bufferCount.isPositive {

if beginPlayback {
playerNode.play()
}

} else {

Expand All @@ -54,10 +57,10 @@ extension FFmpegScheduler {

func seekGapless(toTime seconds: Double, currentSession: PlaybackSession, beginPlayback: Bool, otherTracksToSchedule: [Track]) {

print("\n\(Date.nowTimestampString) - seekGapless() toTime: \(seconds), session: \(currentSession.id)")
// print("\n\(Date.nowTimestampString) - seekGapless() toTime: \(seconds), session: \(currentSession.id)")

stop()
doPlayGapless(firstTrack: currentSession.track, fromTime: seconds, otherTracks: otherTracksToSchedule, currentSession: currentSession)
doPlayGapless(firstTrack: currentSession.track, fromTime: seconds, otherTracks: otherTracksToSchedule, currentSession: currentSession, beginPlayback: beginPlayback)
}

// MARK: Support functions
Expand All @@ -83,10 +86,11 @@ extension FFmpegScheduler {
} else {

playerNode.seekToEndOfTrack(session, frameCount: context.frameCount)
gaplessTrackCompletedWhilePaused = false
gaplessTrackCompletedWhilePaused = true
}

// TODO: Continue scheduling the next track !!!
continueSchedulingGaplessAsync(for: session)
return
}
}

Expand Down Expand Up @@ -119,7 +123,7 @@ extension FFmpegScheduler {

if decoder.eof {

print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... decoder EOF")
// print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... decoder EOF")

decoder.stop()

Expand All @@ -140,7 +144,7 @@ extension FFmpegScheduler {
guard let newContext = nextTrack.playbackContext as? FFmpegPlaybackContext,
let newDecoder = newContext.decoder else {

print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... NO CONTEXT, returning ...")
// print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... NO CONTEXT, returning ...")
return
}

Expand All @@ -163,7 +167,7 @@ extension FFmpegScheduler {
theDecoder = newDecoder
}

print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... theDecoder: \(theDecoder === decoder), theTrack: \(theTrack)")
// print("\(Date.nowTimestampString) - continueSchedulingGaplessAsync() ... theDecoder: \(theDecoder === decoder), theTrack: \(theTrack)")

self.schedulingOpQueue.addOperation {

Expand All @@ -187,7 +191,7 @@ extension FFmpegScheduler {
return
}

print("\(Date.nowTimestampString) - Scheduling one buffer for: \(context.file.lastPathComponent), fromPos: \(seekPosition)")
// print("\(Date.nowTimestampString) - Scheduling one buffer for: \(context.file.lastPathComponent), fromPos: \(seekPosition)")

playerNode.scheduleBuffer(playbackBuffer, for: session, completionHandler: self.gaplessBufferCompletionHandler(session),
seekPosition, immediatePlayback)
Expand All @@ -205,7 +209,7 @@ extension FFmpegScheduler {

fileprivate func gaplessBufferCompleted(_ session: PlaybackSession) {

print("\n\(Date.nowTimestampString) - gaplessBufferCompleted() for: \(session.track). isCurrent ? \(PlaybackSession.isCurrent(session))")
// print("\n\(Date.nowTimestampString) - gaplessBufferCompleted() for: \(session.track). isCurrent ? \(PlaybackSession.isCurrent(session))")

// If the buffer-associated session is not the same as the current session
// (possible if stop() was called, eg. old buffers that complete when seeking), don't do anything.
Expand All @@ -219,7 +223,7 @@ extension FFmpegScheduler {

playbackCtx.close()

print("\n\(Date.nowTimestampString) - Decoder EOF for track: \(session.track), curGT: \(currentGaplessTrack), queueSize: \(gaplessTracksQueue.size)")
// print("\n\(Date.nowTimestampString) - Decoder EOF for track: \(session.track), curGT: \(currentGaplessTrack), queueSize: \(gaplessTracksQueue.size)")

// EOF has been reached, and all buffers have completed playback.
// Signal playback completion (on the main thread).
Expand All @@ -233,7 +237,7 @@ extension FFmpegScheduler {

// Reached end of sequence. No more scheduling.
if doneWithGaplessSequence {
print("\(Date.nowTimestampString) - No more tracks to schedule. Returning ...")
// print("\(Date.nowTimestampString) - No more tracks to schedule. Returning ...")
return
}
}
Expand All @@ -242,7 +246,7 @@ extension FFmpegScheduler {
self.continueSchedulingGaplessAsync(for: session)
}

fileprivate func gaplessTrackCompleted(_ session: PlaybackSession) {
func gaplessTrackCompleted(_ session: PlaybackSession) {
Messenger.publish(.Player.gaplessTrackPlaybackCompleted, payload: session)
}
}
7 changes: 7 additions & 0 deletions Source/Core/Playback/Scheduling/FFmpeg/FFmpegScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ class FFmpegScheduler: PlaybackSchedulerProtocol {
trackCompletedWhilePaused = false
trackCompleted(curSession)

} else if gaplessTrackCompletedWhilePaused, let curSession = PlaybackSession.currentSession {

// print("\n\(Date.nowTimestampString) - gaplessTrackCompletedWhilePaused: \(curSession.track)")

gaplessTrackCompletedWhilePaused = false
gaplessTrackCompleted(curSession)

} else {
playerNode.play()
}
Expand Down

0 comments on commit c695b51

Please sign in to comment.