Soru AVAssetWriter kullanarak video VE ses ile bir film nasıl yazılır?


İle bir film vermek istiyorum AVAssetWriter ve senkronize olarak video ve ses parçalarının nasıl ekleneceğini anlayamıyorum. Yalnızca videoların dışa aktarımı iyi çalışıyor, ancak ses eklediğimde ortaya çıkan film şunun gibi görünüyor:

Önce videoyu (ses olmadan) görüyorum, sonra video donuyor (son görüntü çerçevesini sonuna kadar gösteriliyor) ve birkaç saniye sonra sesi duyuyorum.

Bazı şeyleri denedim CMSampleBufferSetOutputPresentationTimeStamp (ilkini çıkararak CMSampleBufferGetPresentationTimeStamp ses için, ama hepsi işe yaramadı ve ben doğru yön olduğunu düşünmüyorum, çünkü kaynak filmde video ve ses zaten senkronize olmalıdır ...

Kurulumum kısa: Ben bir AVAssetReader ve 2 AVAssetReaderTrackOutput (biri video için, ses için bir tane) ve bunları AVAssetReadersonra bir AVAssetWriter ve 2 AVAssetWriterInput (video ve ses) ve bunları AVAssetWriter... baştan başladım:

[assetReader startReading];
[assetWriter startWriting];
[assetWriter startSessionAtSourceTime:kCMTimeZero];

Sonra, örnek arabellek şeyler yapmak için 2 sıra çalıştırıyorum:

dispatch_queue_t queueVideo=dispatch_queue_create("assetVideoWriterQueue", NULL);
[assetWriterVideoInput requestMediaDataWhenReadyOnQueue:queueVideo usingBlock:^
{
     while([assetWriterVideoInput isReadyForMoreMediaData])
     {
         CMSampleBufferRef sampleBuffer=[assetReaderVideoOutput copyNextSampleBuffer];
         if(sampleBuffer)
         {
             [assetWriterVideoInput appendSampleBuffer:sampleBuffer];
             CFRelease(sampleBuffer);
         } else
         {
             [assetWriterVideoInput markAsFinished];
             dispatch_release(queueVideo);
             videoFinished=YES;
             break;
         }
     }
}];

dispatch_queue_t queueAudio=dispatch_queue_create("assetAudioWriterQueue", NULL);
[assetWriterAudioInput requestMediaDataWhenReadyOnQueue:queueAudio usingBlock:^
{
    while([assetWriterAudioInput isReadyForMoreMediaData])
    {
        CMSampleBufferRef sampleBuffer=[assetReaderAudioOutput copyNextSampleBuffer];
        if(sampleBuffer)
        {
            [assetWriterAudioInput appendSampleBuffer:sampleBuffer];
            CFRelease(sampleBuffer);
        } else
        {
            [assetWriterAudioInput markAsFinished];
            dispatch_release(queueAudio);
            audioFinished=YES;
            break;
        }
    }
}];

Ana döngüde, bitene kadar her iki kuyruğu bekliyorum:

while(!videoFinished && !audioFinished)
{
    sleep(1);
}
[assetWriter finishWriting];

Ayrıca ortaya çıkan dosyayı kütüphanede aşağıdaki kod ile kaydetmeye çalışıyorum ...

NSURL *url=[[NSURL alloc] initFileURLWithPath:path];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if([library videoAtPathIsCompatibleWithSavedPhotosAlbum:url])
{
    [library writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error)
     {
         if(error)
             NSLog(@"error=%@",error.localizedDescription);
         else
             NSLog(@"completed...");
     }];
} else
    NSLog(@"error, video not saved...");

[library release];
[url release];

... ama hatayı alıyorum:

Video /Users/cb/Library/Application Support/iPhone Simulator/4.2/Applications/E9865BF9-D190-4912-9248-66768B1AB635/Documents/export.mp4 cannot be saved to the saved photos album: Error Domain=NSOSStatusErrorDomain Code=-12950 "Movie could not be played." UserInfo=0x5e4fb90 {NSLocalizedDescription=Movie could not be played.}

Kod başka bir programda sorunsuz çalışır. Yani filmde bir sorun mu var?


18
2018-03-30 02:36


Menşei


Bir ses dosyasından ve bir resim dizisinden bir film oluşturmak için stackoverflow üzerinde bir yazı yazdım. Belki de kodun bir kısmı size yardımcı olabilir. stackoverflow.com/questions/6061092/... - TheRonin


Cevaplar:


-(void)mergeAudioVideo
{

    NSString *videoOutputPath=[_documentsDirectory stringByAppendingPathComponent:@"dummy_video.mp4"];
    NSString *outputFilePath = [_documentsDirectory stringByAppendingPathComponent:@"final_video.mp4"];
    if ([[NSFileManager defaultManager]fileExistsAtPath:outputFilePath])
        [[NSFileManager defaultManager]removeItemAtPath:outputFilePath error:nil];


    NSURL    *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
    NSString *filePath = [_documentsDirectory stringByAppendingPathComponent:@"newFile.m4a"];
    AVMutableComposition* mixComposition = [AVMutableComposition composition];

    NSURL    *audio_inputFileUrl = [NSURL fileURLWithPath:filePath];
    NSURL    *video_inputFileUrl = [NSURL fileURLWithPath:videoOutputPath];

    CMTime nextClipStartTime = kCMTimeZero;

    AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:video_inputFileUrl options:nil];
    CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration);

    AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];

    AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audio_inputFileUrl options:nil];
    CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
    AVMutableCompositionTrack *b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:nextClipStartTime error:nil];

    AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];
    _assetExport.outputFileType = @"com.apple.quicktime-movie";
    _assetExport.outputURL = outputFileUrl;

    [_assetExport exportAsynchronouslyWithCompletionHandler:
     ^(void ) {
         if (_assetExport.status == AVAssetExportSessionStatusCompleted) {

          //Write Code Here to Continue
         }
         else {
            //Write Fail Code here     
         }
     }
     ];



}

Ses ve video birleştirmek için bu kodu kullanabilirsiniz.


9
2017-10-19 09:58





Bu assetWriterAudioInput ses yazma için örnek arabellek zamanı yok sayar. Bu şekilde yap.

1) Video parçasını yaz.

2) İşiniz bittiğinde, tamamlandı olarak işaretleyin, yani [videoWriterInput markAsFinished];

3) [assetWriter startSessionAtSourceTime: timeRangeStart] işlemini yapın;

3) sesli okuyucuyu başlat ve ses yazmaya başla.


-3
2018-06-17 07:57



startSessionAtSourceTime, bir girdi için değil varlık yazıcısı içindir. Girdileri yönetmek için kullanmak imkansız. - AlexeyVMP