2013-03-31 24 views
11

Używam AVAssetReaderOutput do odczytywania próbek z AVAsset, do przetwarzania na nich i odtwarzania wyniku za pomocą jednostki AU RemoteIO.Nie można kontynuować odczytu z AVAssetReaderOutput po przejściu do tła i z powrotem na pierwszy plan

Problem polega na tym, że po wywołaniu AudioOutputUnitStop, aby wstrzymać odtwarzanie, po przejściu do tła i z powrotem do pierwszego planu dźwięk nie rozpocznie się ponownie po wywołaniu AudioOutputUnitStart. Jest to spowodowane błędem zwróconym przez metodę z AVAssetReaderOutput, która jest wywoływana jako część potoku renderowania.

Obiekt status z AVAssetReader po wywołaniu copyNextSampleBuffer jest AVAssetReaderStatusFailed, a jego error nieruchomość jest Error Code AVFoundationErrorDomain Domain = -11847 = "Operacja przerwana" UserInfo = 0x1d8b6100 {NSLocalizedRecoverySuggestion = Zatrzymaj innych operacji i spróbuj ponownie., NSLocalizedDescription = Operacja przerwany}

szukam rozwiązania, które nie zmusi mnie do ponownego zainicjowania całego rurociągu po powrocie na pierwszym planie - Licząc jest takie rozwiązanie, że AVAssetReader s może przetrwać aplikację zamiar tle i wróć ...

Uwagi

  • Aplikacja jest uprawniony do odtwarzania dźwięku w tle.
  • Zajmuję się przerwaniami audio - ustawiam AVAudioSession jako aktywną zarówno na zdarzenie AVAudioSessionDelegate s, jak i za każdym razem, gdy aplikacja staje się aktywna. Nie ma znaczenia, czy to zrobię, czy nie, otrzymując ten sam błąd.

Niektóre kodu:

AudioPlayer

@implementation AudioPlayer 
    ... 
// Audio Unit Setup 
AudioComponentDescription desc; 
desc.componentType = kAudioUnitType_Output; 
desc.componentSubType = kAudioUnitSubType_RemoteIO; 
desc.componentManufacturer = kAudioUnitManufacturer_Apple; 
desc.componentFlags = 0; 
desc.componentFlagsMask = 0; 

AudioComponent defaultOutput = AudioComponentFindNext(NULL, &desc); 

AudioComponentInstanceNew(defaultOutput, &_audioUnit); 

AudioStreamBasicDescription audioFormat; 
    FillOutASBDForLPCM(audioFormat, 44100, 2, 16, 16, false, false); 

AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat)); 

AURenderCallbackStruct callbackStruct; 
callbackStruct.inputProc = RenderCallback; 
callbackStruct.inputProcRefCon = (__bridge void*)self; 
AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct)); 

AudioUnitInitialize(self.audioUnit); 

AudioReader Konfiguracja

@implementation AudioReader 
    ... 
NSError* error = nil; 
self.reader = [AVAssetReader assetReaderWithAsset:self.asset error:&error]; 
NSDictionary *outputSettings = ... 
self.readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[self.asset.tracks objectAtIndex:0] outputSettings:outputSettings]; 
[self.reader addOutput:self.readerOutput]; 
[self.reader startReading]; 

AudioReader Render metodzie zwanej ostatecznie przez funkcję RenderCallback

-(BOOL)readChunkIntoBuffer 
{ 
    CMSampleBufferRef sampleBuffer = [self.readerOutput copyNextSampleBuffer]; 
    if (sampleBuffer == NULL) 
    { 
     NSLog(@"Couldn't copy next sample buffer, reader status=%d error=%@, self.reader.status, self.reader.error); 
     return NO; 
    } 
... 
} 
+0

Czy jest to skompresowany typ audio? Niektóre ze sprzętowych dekompresorów dźwięku w niektórych modelach urządzeń nie mogą zostać przerwane bez konieczności ponownego uruchamiania. – hotpaw2

+0

Interesujące. Jest przeznaczony do odczytu "zestawu AVA" pobranego z "MPMediaItem", utworu z biblioteki iTunes użytkownika. Więc mp3/m4a, uważam, że oba są skompresowane. Odnośnie "modeli urządzeń" - jest to problem na iPhonie 5. Czy istnieje jakieś odniesienie do tego, co opisujesz? – Danra

Odpowiedz

4

Podstawy wykresu i AVReader to nie podłączone/połączone w dowolny sposób. Zamieszanie może wynikać z tego, że iOS nie będzie "tła" (hibernacji) procesu, jeśli widzi uruchomiony wykres audio (ponieważ wtedy dane audio nie mogłyby zostać wygenerowane). Dlatego po 2-3 minutach od zatrzymania wykresu audio system iOS będzie przetwarzał twój proces (podobnie jak iOS 9). Przypuszczalnie system iOS sprawdza, co dzieje się w twoim procesie i decyduje, kiedy twój proces powinien zostać zmuszony do przeniesienia się w tle (przez beginBackgroundTaskWithName:expirationHandler).

Twórcy Apple zdecydowali, bez względu na przyczynę, aby zatrzymał się AVReader po przejściu do trybu tła (moim zdaniem jest to QA). Dobrą wiadomością jest to, że możesz ją wykryć i odzyskać, gdy twój proces wychodzi z trybu tła, ale BĘDZIESZ ponownie uruchomić czytnik i czytnikOutput. Po pierwsze, gdy AVAssetReaderOutput zwraca copyNextSampleBuffer NULL, sprawdź AVAssetReader.error.code dla AVErrorOperationInterrupted.

Sposób, w jaki odzyskujemy bezszczelinową regenerację tutaj, kiedy dochodzimy do tego punktu, najpierw obliczamy dokładny czas, w którym skończyliśmy (łatwe do zrobienia - wystarczy zachować licznik całkowitej liczby próbek już wydrukowanych). Następnie będziesz musiał ponownie uruchomić swój przepływ AVAssetReader i AVAssetReaderOutput. Ale to nie problem, ponieważ twoje dobre praktyki rozwojowe byłyby zawarte w jednej funkcji, która ma czas poszukiwania jako argument. W tej funkcji użyj numeru AVAssetReader.timeRange, aby znaleźć czas potrzebny do wznowienia i do presto!

3

Pierwsza rzecz, którą chciałbym wypróbować to:

Nie dzwonię pod numer AudioOutputUnitStop? Dlaczego nie zostawić uruchomiony wykres i gdy dźwięk jest wstrzymana po prostu nie zadzwonić

[self.readerOutput copyNextSampleBuffer] 

Zamiast po prostu wypełnić bufor z 0 i/lub użyć działania render kAudioUnitRenderAction_OutputIsSilence dla buforów. Uruchamianie i zatrzymywanie wykresu zajmuje czas i prowadzi do braku odpowiedzi wykresu. Nigdy nie zatrzymuję wykresu podczas działania moich aplikacji. (O ile nie nastąpi poważna przerwa) Może to oznaczać, że czytnik aktywów będzie zadowolony.

Ale mam przeczucie, że jako AVAssetReader nie jest bezpośrednio podłączony do wykresu audio (po prostu poprosić o próbki i umieścić je w buforze dostarczone przez wywołanie renderowania) problem nie leży z wykresu audio w ogóle się zatrzymał i zrestartował.Dlatego spróbuję spróbować następnej linii ataku: pobrać próbki z zestawu AVA, umieścić aplikację w tle i przywrócić ją na pierwszy plan bez używania AUGraph na wszystkich. Pozwoli to określić, czy problem jest związany z AUGraph, czy też aplikacja wchodzi w stan tła.

Jeśli wydarzy się coś, co zmusi Cię do zburzenia twojego AVAssetReader, będziesz musiał ponownie połączyć się z zasobem. Ponieważ musisz napisać ten kod, opcja, na którą patrzę, to ta, której nie chcesz robić. Połącz się z AVAssetReader, poszukaj pozycji, w której się zatrzymałeś i kontynuuj.

+0

Właściwie to, co robię teraz w przerwie, jest dokładnie tak, jak napisałeś, wypełniając bufor zerami, i w ten sposób po przejściu do bg i powrocie do fg i ponownym rozpoczęciu odtwarzania odtwarzanie będzie kontynuowane poprawnie. Problem z tym podejściem polega na tym, że ikona "play" pozostaje na pasku stanu. – Danra

+0

Więc mówisz, że AVAssetReader jest połączony z AUGraph? –

+0

Nie wiesz, co masz na myśli przez link? Czytałem od niego w wywołaniu zwrotnym odtwarzania AU. – Danra

Powiązane problemy