问题
I was able to record and play opus using AVFoundation. The problem is I got a custom opus audio file (coming from server, also processed in server) as follows:
| header 1 (1 byte) | opus data 1 (1~255 bytes) | header 2 (1 byte) | opus data 2 (1~255 bytes) | ... | ... |
Each header indicates size of the opus data i.e. if header 1 is 200 (Int) then opus data 1 is 200 bytes
So, I am extracting opus data and appending to Data buffer as following:
guard let url = Bundle.main.url(forResource: "test_16kbps", withExtension: "opus") else { return }
do {
let fileData = try Data(contentsOf: url)
while index < fileData.count {
headerData = fileData[index...(index + HEADER_SIZE)]
let opusBytesFromHeader = Int([UInt8](headerData)[0])
start = index + HEADER_SIZE
end = start + opusBytesFromHeader
opusData = fileData[start..<end]
opusAudioData.append(opusData)
index += (HEADER_SIZE + opusBytesFromHeader)
}
} catch let err {
print(err)
}
Then I tried to play using AVAudioPlayer as following
// ... ... ...
playData(audioData: opusAudioData)
// ... ... ...
func playData(audioData: Data){
var avAudioPlayer: AVAudioPlayer?
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
avAudioPlayer = try AVAudioPlayer.init(data: audioData)
if let player = avAudioPlayer {
player.play()
} else {
print("failed to create player from data")
}
} catch let error {
print(error.localizedDescription)
}
}
Gives error: The operation couldn’t be completed. (OSStatus error 1954115647.)
I also tried MobileVLCKit pod as following:
if let opusFilePath = createNewDirPath() { // creates "foo.opus"
do {
try opusAudioData.write(to: opusFilePath)
let opusMedia = VLCMedia(url: opusFilePath)
vlcPlayer.media = opusMedia
vlcPlayer.play()
} catch let err {
print(err)
}
}
Gives following error:
2020-01-05 14:03:41.421270+0900 AppPlay[8695:4077367] creating player instance using shared library
[mp3 @ 0x10881e600] Failed to read frame size: Could not seek to 40126.
TagLib: Ogg::File::packet() -- Could not find the requested packet.
TagLib: Opus::File::read() -- invalid Opus identification header
Android team was able to play using libopus (JNI) and AudioTrack. So, I tried to decode opus data using OpusKit pod as following:
if let decoded = OpusKit.shared.decodeData(opusData) {
decodedOpusData.append(decoded)
} else {
print("failed to decode")
}
And then tried to play decodedOpusData
using AVAudioPlayer but gives same error: The operation couldn’t be completed. (OSStatus error 1954115647.)
.
I don't know what to do to play that audio file (I am new to opus).
About opus data
Sample: 16000 | Frame size: 320 (16 * 20 ms) | Channel: 1
回答1:
Per your other comment/question, if you can get decoded PCM samples, you can add a RIFF/WAV using bytes similar to this C const below (taken from WAV_HEADER_TEMPLATE)
// Header for a 48 kHz, stereo, 32-bit float WAV.
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
static const unsigned char WAV_HEADER_TEMPLATE[44]={
'R','I','F','F',
0xFF,0xFF,0xFF,0x7F, // file size
'W','A','V','E',
'f','m','t',' ', // Chunk ID
0x10,0x00,0x00,0x00, // Chunk Size - length of format above
0x03,0x00, // Format Code: 1 is PCM, 3 is IEEE float
0x02,0x00, // Number of Channels (e.g. 2)
0x80,0xBB,0x00,0x00, // Samples per Second, Sample Rate (e.g. 48000)
0x00,0xDC,0x05,0x00, // Bytes per second, byte rate = sample rate * bits per sample * channels / 8
0x08,0x00, // Bytes per Sample Frame, block align = bits per sample * channels / 8
0x20,0x00, // bits per sample (16 for PCM, 32 for float)
'd','a','t','a',
0xFF,0xFF,0xFF,0x7F // size of data section
};
You'll need to go adjust bytes for "file size" (offset 4) and "size of data section" (offset 40) in a simlar way to this C function.
来源:https://stackoverflow.com/questions/59597315/playing-custom-opus-audio-file-in-ios