Skip to content

Commit b833f37

Browse files
Working with new and improved code from Aural Player v3.12.0
1 parent c0f03ec commit b833f37

File tree

151 files changed

+1339
-1305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+1339
-1305
lines changed

FFmpegPlayer/FFmpegPlayer.xcodeproj/project.pbxproj

Lines changed: 116 additions & 118 deletions
Large diffs are not rendered by default.

FFmpegPlayer/Sources/AppDelegate.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
1010

1111
super.init()
1212
configureLoggingToAFile()
13-
14-
// let ctx = MetadataEditingContext(forFile: URL(fileURLWithPath: "/Users/kven/Music/TagEdit/Bourne.mp3"))
1513
}
1614

1715
private func configureLoggingToAFile() {

FFmpegPlayer/Sources/Player/Utils/ChannelLayouts.swift renamed to FFmpegPlayer/Sources/FFmpeg/Utils/FFmpegChannelLayoutsMapper.swift

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1+
//
2+
// FFmpegChannelLayoutsMapper.swift
3+
// Aural
4+
//
5+
// Copyright © 2021 Kartik Venugopal. All rights reserved.
6+
//
7+
// This software is licensed under the MIT software license.
8+
// See the file "LICENSE" in the project root directory for license terms.
9+
//
110
import AVFoundation
211

312
///
4-
/// Helps map ffmpeg channel layout identifiers to their corresponding AVFoundation channel layout tags.
13+
/// Helps map ffmpeg channel layout identifiers to their corresponding **AVFoundation** channel layout tags.
514
///
615
/// This is required when setting the format for an audio buffer that is to be scheduled for playback, so that
716
/// upmixing / downmixing can be performed correctly.
817
///
9-
struct ChannelLayouts {
18+
struct FFmpegChannelLayoutsMapper {
1019

1120
///
1221
/// A comprehensive mapping of ffmpeg layout identifiers to their corresponding AVFoundation channel layout tags.
@@ -69,11 +78,9 @@ struct ChannelLayouts {
6978
// L R C LFE Rls Rrs Ls Rs
7079
CH_LAYOUT_7POINT1: kAudioChannelLayoutTag_WAVE_7_1,
7180

72-
// MARK: The following mappings are not exact, but the closest possible matches. ---------------------------------------
81+
// MARK: The following mappings are not exact, but the closest possible matches.
7382
// NOTE - Some channels may be dropped entirely.
7483

75-
// TODO: Create custom AudioChannelLayouts and AVAudioChannelLayouts with exact channel mappings
76-
7784
// L R C Cs Ls Rs -> L R C Cs
7885
CH_LAYOUT_6POINT0: kAudioChannelLayoutTag_DVD_8,
7986

@@ -121,74 +128,18 @@ struct ChannelLayouts {
121128
static func mapLayout(ffmpegLayout: Int) -> AVAudioChannelLayout? {
122129

123130
if let layoutTag = layoutsMap[ffmpegLayout] {
124-
125-
// NOTE - It's safe to force unwrap the optional AVAudioChannelLayout
126-
// because we are using only valid pre-defined channel layout tags.
127-
return AVAudioChannelLayout(layoutTag: layoutTag)!
131+
return AVAudioChannelLayout(layoutTag: layoutTag)
128132
}
129133

130134
return nil
131135
}
132136

133-
///
134-
/// Provides a human-readable string for a given channel layout.
135-
///
136-
/// - Parameter channelLayout: The identifier for an ffmpeg channel layout.
137-
///
138-
/// - Parameter channelCount: The number of channels in **channelLayout**.
139-
///
140-
/// - returns: A human-readable string describing the given channel layout.
141-
///
142137
static func readableString(for channelLayout: Int64, channelCount: Int32) -> String {
143138

144139
let layoutStringPointer = UnsafeMutablePointer<Int8>.allocate(capacity: 100)
145-
av_get_channel_layout_string(layoutStringPointer, 100, channelCount, UInt64(channelLayout))
146-
147140
defer {layoutStringPointer.deallocate()}
148141

142+
av_get_channel_layout_string(layoutStringPointer, 100, channelCount, UInt64(channelLayout))
149143
return String(cString: layoutStringPointer).replacingOccurrences(of: "(", with: " (").capitalized
150144
}
151-
152-
// MARK: Debugging functions ------------------------------------------------------
153-
154-
static func printLayouts() {
155-
156-
for layout in layoutsMap.keys.sorted(by: {$0 < $1}).map({UInt64($0)}) {
157-
printLayout(layout, av_get_channel_layout_nb_channels(layout))
158-
}
159-
}
160-
161-
static func printLayout(_ layout: UInt64, _ channelCount: Int32) {
162-
163-
let layoutString = UnsafeMutablePointer<Int8>.allocate(capacity: 100)
164-
av_get_channel_layout_string(layoutString, 100, channelCount, layout)
165-
166-
var channelNames: [String] = []
167-
for index in 0..<channelCount {
168-
channelNames.append(String(cString: av_get_channel_name(av_channel_layout_extract_channel(UInt64(layout), index))))
169-
}
170-
171-
let ls = String(cString: layoutString)
172-
let ffLay = channelNames.joined(separator: " ")
173-
let avfLay = AVFLayout(ffLay)
174-
175-
print("\nLayout:", layout, ls, ffLay)
176-
print("AVF Layout:", avfLay)
177-
}
178-
179-
static func AVFLayout(_ lyt: String) -> String {
180-
181-
return lyt
182-
.replacingOccurrences(of: "BL", with: "Rls")
183-
.replacingOccurrences(of: "BR", with: "Rrs")
184-
.replacingOccurrences(of: "BC", with: "Cs")
185-
.replacingOccurrences(of: "SL", with: "Ls")
186-
.replacingOccurrences(of: "SR", with: "Rs")
187-
.replacingOccurrences(of: "FLC", with: "Lc")
188-
.replacingOccurrences(of: "FRC", with: "Rc")
189-
.replacingOccurrences(of: "FL", with: "L")
190-
.replacingOccurrences(of: "FR", with: "R")
191-
.replacingOccurrences(of: "FC", with: "C")
192-
193-
}
194145
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//
2+
// FFmpegSampleConverter.swift
3+
// Aural
4+
//
5+
// Copyright © 2021 Kartik Venugopal. All rights reserved.
6+
//
7+
// This software is licensed under the MIT software license.
8+
// See the file "LICENSE" in the project root directory for license terms.
9+
//
10+
import AVFoundation
11+
import Accelerate
12+
13+
fileprivate let bytesInAFloat: Int = MemoryLayout<Float>.size / MemoryLayout<UInt8>.size
14+
15+
///
16+
/// Performs conversion of PCM audio samples to the standard format suitable for playback in an **AVAudioEngine**,
17+
/// i.e. 32-bit floating point non-interleaved (aka planar).
18+
///
19+
/// Uses **libswresample** to do the actual conversion.
20+
///
21+
extension FFmpegDecoder {
22+
23+
///
24+
/// Transfer the decoded samples into an audio buffer that the audio engine can schedule for playback.
25+
///
26+
func transferSamplesToPCMBuffer(from frameBuffer: FFmpegFrameBuffer, outputFormat: AVAudioFormat) -> AVAudioPCMBuffer? {
27+
28+
// Transfer the decoded samples into an audio buffer that the audio engine can schedule for playback.
29+
guard let playbackBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat,
30+
frameCapacity: AVAudioFrameCount(frameBuffer.sampleCount)) else {return nil}
31+
32+
// The audio buffer will always be filled to capacity.
33+
playbackBuffer.frameLength = playbackBuffer.frameCapacity
34+
35+
if frameBuffer.needsFormatConversion {
36+
convert(samplesIn: frameBuffer, andCopyTo: playbackBuffer)
37+
38+
} else {
39+
copy(samplesIn: frameBuffer, into: playbackBuffer)
40+
}
41+
42+
return playbackBuffer
43+
}
44+
45+
func copy(samplesIn frameBuffer: FFmpegFrameBuffer, into audioBuffer: AVAudioPCMBuffer) {
46+
47+
guard let destPointers = audioBuffer.floatChannelData else {return}
48+
49+
// Keeps track of how many samples have been copied over so far.
50+
// This will be used as an offset when performing each copy operation.
51+
var sampleCountSoFar: Int = 0
52+
53+
for frame in frameBuffer.frames {
54+
55+
let sampleCount = frame.sampleCount
56+
let firstSampleIndex = Int(frame.firstSampleIndex)
57+
58+
// NOTE - The following copy operation assumes a non-interleaved output format (i.e. the standard Core Audio format).
59+
60+
// Temporarily bind the input sample buffers as floating point numbers, and perform the copy.
61+
frame.dataPointers.withMemoryRebound(to: UnsafeMutablePointer<Float>.self, capacity: channelCount) {srcPointers in
62+
63+
// Iterate through all the channels.
64+
for channelIndex in 0..<channelCount {
65+
66+
// Use Accelerate to perform the copy optimally, starting at the given offset.
67+
cblas_scopy(sampleCount,
68+
srcPointers[channelIndex].advanced(by: firstSampleIndex), 1,
69+
destPointers[channelIndex].advanced(by: sampleCountSoFar), 1)
70+
}
71+
}
72+
73+
sampleCountSoFar += frame.intSampleCount
74+
}
75+
}
76+
77+
func convert(samplesIn frameBuffer: FFmpegFrameBuffer, andCopyTo audioBuffer: AVAudioPCMBuffer) {
78+
79+
guard let resampleCtx = self.resampleCtx, let destPointers = audioBuffer.floatChannelData else {return}
80+
81+
var sampleCountSoFar: Int = 0
82+
let outputData: UnsafeMutablePointer<UnsafeMutablePointer<UInt8>?>! = .allocate(capacity: channelCount)
83+
defer {outputData.deallocate()}
84+
85+
destPointers.withMemoryRebound(to: UnsafeMutablePointer<UInt8>.self, capacity: channelCount) {outChannelPointers in
86+
87+
// Convert one frame at a time.
88+
for frame in frameBuffer.frames {
89+
90+
let offset = sampleCountSoFar * bytesInAFloat
91+
92+
for ch in 0..<channelCount {
93+
outputData[ch] = outChannelPointers[ch].advanced(by: offset)
94+
}
95+
96+
resampleCtx.convertFrame(frame, andStoreIn: outputData)
97+
sampleCountSoFar += frame.intSampleCount
98+
}
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)