IOS: аудио и видео кран от Airplay

Я сделал видеопроигрыватель, который анализирует аудио и видео треки в реальном времени с видео, которое в данный момент воспроизводится. Видеозаписи хранятся на устройстве iOS (в папке "Документы приложений" ).

Все это прекрасно работает. Я использую MTAudioProcessingTap, чтобы получить все звуковые сэмплы и сделать несколько FFT, и я анализирую видео, просто скопировав пиксельные буферы из текущего воспроизводимого CMTime (свойство AVTlayer currentTime). Как я уже сказал, это прекрасно работает.

Но теперь я хочу поддержать Airplay. Просто сама трансляция не сложна, но мои краны перестают работать, как только Airplay переключается, и видео воспроизводится на ATV. Так или иначе, MTAudioProcessingTap не будет обрабатываться, а пиксельные буферы все пустые... Я не могу добраться до данных.

Есть ли способ добраться до этих данных?

Чтобы получить пиксельные буферы, я просто запускаю событие каждые несколько миллисекунд и извлекаю текущее время игрока. Тогда:

CVPixelBufferRef imageBuffer = [videoOutput copyPixelBufferForItemTime:time itemTimeForDisplay:nil];
CVPixelBufferLockBaseAddress(imageBuffer,0);

uint8_t *tempAddress = (uint8_t *) CVPixelBufferGetBaseAddress(imageBuffer);

size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);

CVPixelBufferUnlockBaseAddress(imageBuffer,0);

Где tempAddress - мой пиксельный буфер, а videoOutput - это экземпляр AVPlayerItemVideoOutput.

Для аудио, я использую:

AVMutableAudioMixInputParameters *inputParams = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioTrack];

// Create a processing tap for the input parameters
MTAudioProcessingTapCallbacks callbacks;

callbacks.version = kMTAudioProcessingTapCallbacksVersion_0;
callbacks.clientInfo = (__bridge void *)(self);
callbacks.init = init;
callbacks.prepare = prepare;
callbacks.process = process;
callbacks.unprepare = unprepare;
callbacks.finalize = finalize;

MTAudioProcessingTapRef tap;
OSStatus err = MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks,
                                          kMTAudioProcessingTapCreationFlag_PostEffects, &tap);
if (err || !tap) {
    NSLog(@"Unable to create the Audio Processing Tap");
    return;
}

inputParams.audioTapProcessor = tap;

// Create a new AVAudioMix and assign it to our AVPlayerItem
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
audioMix.inputParameters = @[inputParams];
playerItem.audioMix = audioMix;

С уважением, Niek

Ответ 1

К сожалению, по моему опыту, невозможно получить информацию об аудио/видео во время Airplay, так как воспроизведение выполняется на Apple TV, поэтому на устройстве iOS нет никакой информации.

У меня была та же проблема с получением данных субтитров SMPTE из timedMetaData, который перестает получать отчеты во время Airplay.

Ответ 2

Здесь решение:

это реализовать AirPlay, я использую этот код только для аудио в своем приложении. Я не знаю, можете ли вы улучшить видео, но можете попробовать;)

В AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [RADStyle applyStyle];
    [radiosound superclass];
    [self downloadZip];

    NSError *sessionError = nil;
    [[AVAudioSession sharedInstance] setDelegate:self];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];

    UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);

    UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
    AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}

А, если вы используете трансляцию в режиме nice для реализации элемента управления LockScreen, ArtWork, Stop/play, Title ecc.

В DetailViewController вашего игрока используйте этот код:

- (BOOL)canBecomeFirstResponder {

    return YES;
}
- (void)viewDidAppear:(BOOL)animated {

    [super viewDidAppear:animated];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];

    NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: (self.saved)[@"image"]]];

    if (imageData == nil){

    MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
    MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"lockScreen.png"]];

    infoCenter.nowPlayingInfo = @{MPMediaItemPropertyTitle: saved[@"web"],MPMediaItemPropertyArtist: saved[@"title"], MPMediaItemPropertyArtwork:albumArt};

    } else {

        MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithData:imageData]];

        infoCenter.nowPlayingInfo = @{MPMediaItemPropertyTitle: saved[@"link"],MPMediaItemPropertyArtist: saved[@"title"], MPMediaItemPropertyArtwork:albumArt};

    }

}

Надеюсь, что этот код вам поможет;)