Как захватить только аудио или видео при потоковой передаче видео с URL-адреса?

Я хотел бы записывать только аудио и только видео (без звука) при потоковой передаче видео из Интернета в моем приложении iOS.

Я гуглил, но не смог найти ни одного похожего ресурса.

Это вообще возможно?

Любые указатели на некоторые связанные ресурсы?

Спасибо.

Обновление:
Спасибо за ответ, Линдси. Похоже, решение работает.
У меня есть еще один вопрос. Если мне нужно выполнить эту работу для части видео (например, когда пользователь нажимает «Начать запись» и «Остановить запись») во время воспроизведения (потоковой передачи) видео, как бы вы это сделали? Как вы думаете, это также возможно?


person technophyle    schedule 31.01.2015    source источник


Ответы (1)


Довольно интересный вопрос. Вот что я придумал:

Насколько мне известно, вы не можете напрямую сохранить воспроизводимый видеопоток с вашего MPMoviePlayerController, но вы можете сохранить данные во время потоковой передачи вашего видео с помощью dataWithContentsOfURL:. Затем, как только данные будут успешно сохранены, вы можете разделить их на видео и/или аудио компоненты, например:

- (void)saveFullVideo {

    // Create a temporary file path to hold the
    // complete version of the video
    NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
    NSURL *fileURL = [[tmpDirURL URLByAppendingPathComponent:@"temp"]
       URLByAppendingPathExtension:@"mov"]; // <-- assuming the streaming video's a .mov file

    NSError *error = nil;
    NSData *urlData = [NSData dataWithContentsOfURL:streamingURL];
    [urlData writeToURL:fileURL options:NSAtomicWrite error:&error];

    // If the data is written to the temporary file path
    // successfully, split it into video and audio components
    if (!error) {
        [self saveVideoComponent:fileURL];
        [self saveAudioComponent:fileURL];
    }
}

- (void)saveVideoComponent:(NSURL*)videoUrl {

    AVURLAsset* videoAsset  = [[AVURLAsset alloc]initWithURL:videoUrl options:nil];

    AVMutableComposition *composition = [AVMutableComposition composition];

    // Create a mutable track of type video
    AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

    NSError *error = nil;

    // Get the video portion of the track and insert it
    // into the mutable video composition track
    AVAssetTrack *video = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    [videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:video atTime:kCMTimeZero error:&error];

    // Create a session to export this composition
    AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];

    // Create the path to which you'll export the video
    NSString *docPath = [NSSearchPathForDirectoriesInDomains
                         (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *exportPath = [docPath stringByAppendingPathComponent:@"/video_path.mp4"];
    NSURL *exportUrl = [NSURL fileURLWithPath:exportPath];

    // Remove the old file at the export path if one exists
    if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath])
    {
        [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
    }

    assetExport.outputFileType = AVFileTypeMPEG4;

    assetExport.outputURL = exportUrl;
    assetExport.shouldOptimizeForNetworkUse = YES;

    [assetExport exportAsynchronouslyWithCompletionHandler:
     ^(void )
     {
         switch (assetExport.status)
         {
             case AVAssetExportSessionStatusCompleted: {
                 NSLog(@"Video export Saved to path: %@", exportUrl);
                 break;
             } case AVAssetExportSessionStatusFailed: {
                 NSLog(@"Export Failed");
                 NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]);
                 break;
             } case AVAssetExportSessionStatusCancelled: {
                 NSLog(@"Export Cancelled");
                 NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]);
                 break;
             } default: {
                 NSLog(@"Export Default condition");
             }    
         }
     }];
}

- (void)saveAudioComponent:(NSURL*)videoUrl {

    AVURLAsset* videoAsset  = [[AVURLAsset alloc]initWithURL:videoUrl options:nil];

    AVMutableComposition *composition = [AVMutableComposition composition];

    // Create a mutable track of type audio
    AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

    NSError *error = nil;

    // Get the audio portion of the track and insert it
    // into the mutable audio composition track
    AVAssetTrack *audio = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    [audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:audio atTime:kCMTimeZero error:&error];


    // Create a session to export this composition
    AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];

    // Create the path to which you'll export the audio
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"VideoFolder"];
    // Create folder if needed
    [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:nil];

    NSString *exportPath = [dataPath stringByAppendingPathComponent:@"audio_path.caf"];
    NSURL *exportUrl = [NSURL fileURLWithPath:exportPath];

    // Remove the old file at the export path if one exists
    if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath])
    {
        [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
    }

    assetExport.outputFileType = AVFileTypeCoreAudioFormat;

    assetExport.outputURL = exportUrl;
    assetExport.shouldOptimizeForNetworkUse = YES;

    [assetExport exportAsynchronouslyWithCompletionHandler:
     ^(void )
     {
         switch (assetExport.status)
         {
             case AVAssetExportSessionStatusCompleted: {
                 NSLog(@"Audio export Saved to path: %@", exportUrl);
                 //[self playAudio:exportUrl];
                 break;
             } case AVAssetExportSessionStatusFailed: {
                 NSLog(@"Export Failed");
                 NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]);
                 break;
             } case AVAssetExportSessionStatusCancelled: {
                 NSLog(@"Export Cancelled");
                 NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]);
                 break;
             } default: {
                 NSLog(@"Export Default condition");
             }
         }
     }];
}
person Lyndsey Scott    schedule 01.02.2015
comment
Спасибо за ответ Линдси! Я попробую и вернусь к вам. - person technophyle; 01.02.2015
comment
Спасибо за ответ, Линдси. Решение, кажется, работает. У меня есть еще один вопрос. Если мне нужно выполнить эту работу для части видео (например, когда пользователь нажимает «Начать запись» и «Остановить запись») во время воспроизведения (потоковой передачи) видео, как бы вы это сделали? Как вы думаете, это также возможно? - person technophyle; 01.02.2015
comment
@technophyle Вы должны иметь возможность изменить все экземпляры CMTimeRangeMake на любой необходимый вам диапазон. - person Lyndsey Scott; 02.02.2015