У меня есть вопрос об использовании AVFoundations AVPlayer (вероятно, применим как к iOS, так и к macOS). Я пытаюсь воспроизвести аудио (несжатые wav) данные, которые поступают из канала, отличного от стандартного потокового HTTP-потока.
Случай:
Пакеты аудиоданных сжимаются в канале вместе с другими данными, с которыми приложение должно работать. Например, видео и аудио поступают в один канал и разделяются заголовком.
После фильтрации я получаю аудиоданные и распаковываю их в WAV-формат (на данном этапе не содержит заголовков).
После того, как пакеты данных готовы (9600 байт для 24k, стерео 16-битный звук), они передаются в экземпляр AVPlayer (AVAudioPlayer в соответствии с Apple не подходит для потоковой передачи звука).
Учитывая, что AVPlayer (элемент или объект) не загружается из памяти (нет initWithData: (NSData)) и требует URL-адреса HTTP Live Stream или URL-адреса файла, я создаю файл на диске (либо macOS, либо iOS) добавьте WAV Headers и добавьте несжатые данные там.
Вернемся к AVPlayer, создав следующее:
AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:tempAudioFile] options:nil];
AVPlayerItem *audioItem = [[AVPlayerItem alloc] initWithAsset:audioAsset];
AVPlayer *audioPlayer = [[AVPlayer alloc] initWithPlayerItem:audioItem];
добавление KVO, а затем попытка начать воспроизведение:
[audioPlayer play];
В результате звук воспроизводится в течение 1-2 секунд, а затем останавливается (AVPlayerItemDidPlayToEndTimeNotification), а данные продолжают добавляться в файл. Поскольку все это в цикле, [воспроизведение аудиопользователя] запускается и приостанавливается (скорость == 0) несколько раз.
Вся концепция в упрощенной форме:
-(void)PlayAudioWithData:(NSData *data) //data in encoded format
{
NSData *decodedSound = [AudioDecoder DecodeData:data]; //decodes the data from the compressed format (Opus) to WAV
[Player CreateTemporaryFiles]; //This creates the temporary file by appending the header and waiting for input.
[Player SendDataToPlayer:decodedSound]; //this sends the decoded data to the Player to be stored to file. See below for appending.
Boolean prepared = [Player isPrepared]; //a check if AVPlayer, Item and Asset are initialized
if (!prepared)= [Player Prepare]; //creates the objects like above
Boolean playing = [Player isAudioPlaying]; //a check done on the AVPlayer if rate == 1
if (!playing) [Player startPlay]; //this is actually [audioPlayer play]; on AVPlayer Instance
}
-(void)SendDataToPlayer:(NSData *data)
{
//Two different methods here. First with NSFileHandle — not so sure about this though as it definitely locks the file.
//Initializations and deallocations happen elsewhere, just condensing code to give you an idea
NSFileHandle *audioFile = [NSFileHandle fileHandleForWritingAtPath:_tempAudioFile]; //happens else where
[audioFile seekToEndOfFile];
[audioFile writeData:data];
[audioFile closeFile]; //happens else where
//Second method is
NSOutputStream *audioFileStream = [NSOutputStream outputStreamWithURL:[NSURL fileURLWithPath:_tempStreamFile] append:YES];
[audioFileStream open];
[audioFileStream write:[data bytes] maxLength:data.length];
[audioFileStream close];
}
Оба NSFileHandle и NSOutputStream полностью работают с WAV файлами, исполняемыми QuickTime, iTunes, VLC и т.д. Кроме того, если я обойду [Player SendDataToPlayer: decodedSound] и имею временный аудиофайл, предварительно загруженный стандартным WAV, он также воспроизводится нормально.
До сих пор существуют две стороны: a) У меня звуковые данные распакованы и готовы к воспроизведению b) Я правильно сохраняю данные.
То, что я пытаюсь сделать, это send-write-read в строке. Это заставляет меня думать, что сохранение данных в файл, получает эксклюзивный доступ к файловому ресурсу и не позволяет AVPlayer продолжать воспроизведение.
Кто-нибудь имеет представление о том, как сохранить файл доступным как для NSFileHandle/NSOutputStream, так и для AVPlayer?
Или даже лучше... Есть ли AVPlayer initWithData? (Хе-хе...)
Любая помощь очень ценится! Спасибо заранее.