Подтвердить что ты не робот

IOS Stream Audio с одного устройства iOS на другое

Я получаю песню из библиотеки iTunes устройства и вставляю ее в AVAsset:

- (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
    NSArray *arr = mediaItemCollection.items;

    MPMediaItem *song = [arr objectAtIndex:0];

    NSData *songData = [NSData dataWithContentsOfURL:[song valueForProperty:MPMediaItemPropertyAssetURL]];
}

Тогда у меня есть метод Game Center для приема данных:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID

У меня много проблем с выяснением того, как отправить этот AVAsset через GameCenter, а затем запустить его на принимающем устройстве.

Я прочитал: http://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioStreamReference/Reference/reference.html#//apple_ref/doc/uid/TP40006162

http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html#//apple_ref/doc/uid/TP40009767-CH2-SW5

http://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html

http://developer.apple.com/library/mac/#documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/Introduction/Introduction.html

Я просто потерялся. Информационная перегрузка.

Я реализовал Cocoa с кодом Love Audio Stream, но я не могу понять, как взять NSData, которую я получаю через GameCenter, и вставить его в свой код. http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html

Может кто-нибудь, пожалуйста, помогите мне понять это? Поэтому снова нужна часть, в которой мне нужна помощь, просто разбивать данные песни на пакеты (или, тем не менее, это работает), затем повторять эти пакеты и отправлять их через gamekit, а затем анализировать эти данные, как они поступают на принимающее устройство, как PLAY it AS он приходит.

4b9b3361

Ответ 1

API, который вам нужно посмотреть, это "Службы очереди аудио" .

Правильно, здесь приведен общий обзор того, что вам нужно сделать.

При воспроизведении звука вы настраиваете очередь или службу. Эта очередь будет запрашивать некоторые аудиоданные. Затем, когда он сыграет все это, он попросит еще немного. Это продолжается до тех пор, пока вы не остановите очередь, или больше нет данных для воспроизведения.

Двумя основными API нижнего уровня в iOS являются Audio Unit и Audio Queue. На более низком уровне я имею в виду API, который немного более nitty gritty, чем "просто воспроизводить этот mp3" или что-то еще.

Мой опыт заключается в том, что Audio Unit является более низкой задержкой, но эта очередь аудио больше подходит для потоковой передачи звука. Итак, я думаю, что для вас это лучший вариант.

Ключевой частью того, что вам нужно сделать, является буферизация. Это означает, что загрузка данных достаточно, чтобы в вашем воспроизведении не было пробелов. Возможно, вам захочется обработать это, предварительно загрузив больший объем данных. Затем вы играете вперед. У вас будет достаточно большой буфер в памяти, одновременно получая больше данных в фоновом потоке.

Примерный проект, который я бы рекомендовал внимательно изучить, - SpeakHere. В частности, посмотрите классы SpeakHereController.mm и AQPlayer.mm.

Контроллер обрабатывает такие функции, как запуск и остановка AQPlayer. AQPlayer представляет <<24 > . Посмотрите внимательно на AQPlayer::AQBufferCallback. Это метод обратного вызова, который вызывается, когда очередь хочет больше данных.

Вам нужно убедиться, что настройка данных очереди и формат данных, которые вы получаете, точно совпадают. Проверяйте такие вещи, как количество каналов (моно или стерео?), Количество кадров, целые числа или поплавки и частота дискретизации. Если что-то не совпадает, вы либо получите ошибки EXC_BAD_ACCESS, когда будете работать через соответствующие буферы, либо вы получите белый шум, либо - в случае неправильной частоты дискретизации - звук, который звучит замедлительно или ускорился.

Обратите внимание, что SpeakHere запускает две очереди аудио; один для записи и один для воспроизведения. Все аудиофайлы работают с использованием буферов данных. Таким образом, вы всегда проходите круглые указатели на буферы. Так, например, во время воспроизведения вы будете иметь буфер памяти, который имеет 20 секунд аудио. Возможно, каждую секунду ваш обратный вызов будет вызван в очередь, по сути говоря, "дайте мне еще вторую ценность данных". Вы могли бы подумать об этом как о головке воспроизведения, которая перемещается по вашим данным, запрашивая дополнительную информацию.

Посмотрите на это чуть подробнее. В отличие от SpeakHere, вы будете работать с буферами памяти, а не записывать аудио во временный файл.

Обратите внимание, что если вы имеете дело с большими объемами данных, на устройстве iOS у вас не останется выбора, кроме как сохранить основную часть данных на диске. Особенно, если пользователь может воспроизвести звук, перемотать его и т.д., Вам нужно все это где-то держать!

В любом случае, предполагая, что AQPlayer будет считываться из памяти, нам нужно будет изменить его следующим образом.

Сначала, где-то для хранения данных, в AQPlayer.h:

void SetAudioBuffer(float *inAudioBuffer) { mMyAudioBuffer = inAudioBuffer; }

У вас уже есть эти данные в объекте NSData, поэтому вы можете просто передать указатель, возвращенный из вызова, на [myData bytes].

Что предоставляет эти данные в очередь аудио? То, что метод обратного вызова настроен в AQPlayer:

void AQPlayer::AQBufferCallback(void *                  inUserData,
                            AudioQueueRef           inAQ,
                            AudioQueueBufferRef     inCompleteAQBuffer) 

Метод, который мы будем использовать для добавления части наших данных в очередь аудио: AudioQueueEnqueueBuffer:

AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL);

inAQ - это ссылка на очередь, полученную нашим обратным вызовом. inCompleteAQBuffer - это указатель на буфер очереди аудио.

Итак, как вы получаете свои данные - это указатель, возвращаемый вызовом метода bytes объекта NSData - в буфер очереди аудио inCompleteAQBuffer?

Используя memcpy:

memcpy(inCompleteAQBuffer->mAudioData, THIS->mMyAudioBuffer + (THIS->mMyPlayBufferPosition / sizeof(float)), numBytesToCopy);

Вам также необходимо установить размер буфера:

        inCompleteAQBuffer->mAudioDataByteSize =  numBytesToCopy;   

numBytesToCopy всегда будет таким же, если только у вас не будет исчерпания данных. Например, если ваш буфер составляет 2 секунды аудиоданных, и у вас есть 9 секунд для воспроизведения, то для первых четырех обратных вызовов вы получите 2 секунды. Для окончательного обратного вызова у вас останется только 1 секунда данных. numBytesToCopy должен отражать это.

    // Calculate how many bytes are remaining? It could be less than a normal buffer
    // size. For example, if the buffer size is 0.5 seconds and recording stopped
    // halfway through that. In which case, we copy across only the recorded bytes
    // and we don't enqueue any more buffers.
    SInt64 numRemainingBytes = THIS->mPlayBufferEndPosition - THIS->mPlayBufferPosition;

    SInt64 numBytesToCopy =  numRemainingBytes < THIS->mBufferByteSize ? numRemainingBytes : THIS->mBufferByteSize;

Наконец, мы продвигаем головку воспроизведения. В нашем обратном вызове мы предоставили очереди некоторые данные для воспроизведения. Что произойдет в следующий раз, когда мы получим обратный вызов? Мы не хотим давать те же данные снова. Нет, если вы не делаете какой-нибудь фанк-ди-джей!

Итак, мы продвигаем голову, которая в основном является указателем на наш звуковой буфер. Указатель перемещается по буфере, как игла в записи:

    SELF->mPlayBufferPosition += numBytesToCopy;

Что это! Там есть и другая логика, но вы можете получить это от изучения полного метода обратного вызова в SpeakHere.

Несколько моментов, которые я должен сделать. Во-первых, не просто скопируйте и вставьте мой код выше. Абсолютно убедитесь, что вы понимаете, что делаете. Несомненно, вы столкнетесь с проблемами, и вам нужно будет понять, что происходит.

Во-вторых, убедитесь, что аудиоформаты совпадают, и даже лучше, чем вы понимаете аудиоформат. Это описано в Руководстве по программированию служб очереди аудио в записи аудио. Посмотрите на листинг 2-8. Задание формата аудиоданных аудиопотоков.

Очень важно понять, что у вас есть самая примитивная единица данных - целое или плавающее. Моно или стерео у вас есть один или два канала в кадре. Это определяет, сколько целых чисел или поплавков находится в этом фрейме. Тогда у вас есть кадры на пакет (возможно, 1). Ваша частота выборки определяет, сколько из этих пакетов у вас есть в секунду.

Все это описано в документах. Просто убедитесь, что все соответствует, или у вас появятся довольно странные звуки!

Удачи!

Ответ 2

Какова ваша цель? и как вы называете [match sendDataToAllPlayers:...]? Он решает, как вы получаете AVAsset от полученных данных.

Указаны следующие шаги:

  • получить NSData от mediaPicker для музыки
  • создать AVAsset (вы действительно это делаете?)
  • отправить в GKPlayers?
  • получение NSData из gamecenter
  • верните AVAsset, если вы дон 2.
  • используйте AVPlayer: playerWithPlayItem, чтобы получить AVPlayer и воспроизвести

Если вы делаете некоторую кодировку и передачу по своему собственному методу, например, используйте AVAssetReader для получения необработанных данных и отправки, а затем просто делайте это обратное.