2014-10-20 14 views
5

Я пытаюсь объединить изображения в ранее существовавшее видео, чтобы создать новый видеофайл с помощью AVFoundation на Mac.Смешивание изображений и видео с использованием AVFoundation

До сих пор я прочитал пример документации компании Apple,

ASSETWriterInput for making Video from UIImages on Iphone Issues

Mix video with static image in CALayer using AVVideoCompositionCoreAnimationTool

AVFoundation Tutorial: Adding Overlays and Animations to Videos и несколько других SO связывает

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

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

Ниже приведен код, который я использую для создания видеосегментов и анимаций слоев.

//https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/03_Editing.html#//apple_ref/doc/uid/TP40010188-CH8-SW7 

    // let's start by making our video composition 
    AVMutableComposition* mutableComposition = [AVMutableComposition composition]; 
    AVMutableCompositionTrack* mutableCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 

    AVMutableVideoComposition* mutableVideoComposition = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:gVideoAsset]; 

    // if the first point's frame doesn't start on 0 
    if (gFrames[0].startTime.value != 0) 
    { 
     DebugLog("Inserting vid at 0"); 
     // then add the video track to the composition track with a time range from 0 to the first point's startTime 
     [mutableCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, gFrames[0].startTime) ofTrack:gVideoTrack atTime:kCMTimeZero error:&gError]; 

    } 

    if(gError) 
    { 
     DebugLog("Error inserting original video segment"); 
     GetError(); 
    } 

    // create our parent layer and video layer 
    CALayer* parentLayer = [CALayer layer]; 
    CALayer* videoLayer = [CALayer layer]; 

    parentLayer.frame = CGRectMake(0, 0, 1280, 720); 
    videoLayer.frame = CGRectMake(0, 0, 1280, 720); 

    [parentLayer addSublayer:videoLayer]; 

    // create an offset value that should be added to each point where a new video segment should go 
    CMTime timeOffset = CMTimeMake(0, 600); 

    // loop through each additional frame 
    for(int i = 0; i < gFrames.size(); i++) 
    { 
    // create an animation layer and assign it's content to the CGImage of the frame 
     CALayer* Frame = [CALayer layer]; 
     Frame.contents = (__bridge id)gFrames[i].frameImage; 
     Frame.frame = CGRectMake(0, 720, 1280, -720); 

     DebugLog("inserting empty time range"); 
     // add frame point to the composition track starting at the point's start time 
     // insert an empty time range for the duration of the frame animation 
     [mutableCompositionTrack insertEmptyTimeRange:CMTimeRangeMake(CMTimeAdd(gFrames[i].startTime, timeOffset), gFrames[i].duration)]; 

     // update the time offset by the duration 
     timeOffset = CMTimeAdd(timeOffset, gFrames[i].duration); 

     // make the layer completely transparent 
     Frame.opacity = 0.0f; 

     // create an animation for setting opacity to 0 on start 
     CABasicAnimation* frameAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 
     frameAnim.duration = 1.0f; 
     frameAnim.repeatCount = 0; 
     frameAnim.autoreverses = NO; 

     frameAnim.fromValue = [NSNumber numberWithFloat:0.0]; 
     frameAnim.toValue = [NSNumber numberWithFloat:0.0]; 

     frameAnim.beginTime = AVCoreAnimationBeginTimeAtZero; 
     frameAnim.speed = 1.0f; 

     [Frame addAnimation:frameAnim forKey:@"animateOpacity"]; 

     // create an animation for setting opacity to 1 
     frameAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 
     frameAnim.duration = 1.0f; 
     frameAnim.repeatCount = 0; 
     frameAnim.autoreverses = NO; 

     frameAnim.fromValue = [NSNumber numberWithFloat:1.0]; 
     frameAnim.toValue = [NSNumber numberWithFloat:1.0]; 

     frameAnim.beginTime = AVCoreAnimationBeginTimeAtZero + CMTimeGetSeconds(gFrames[i].startTime); 
     frameAnim.speed = 1.0f; 

     [Frame addAnimation:frameAnim forKey:@"animateOpacity"]; 

     // create an animation for setting opacity to 0 
     frameAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 
     frameAnim.duration = 1.0f; 
     frameAnim.repeatCount = 0; 
     frameAnim.autoreverses = NO; 

     frameAnim.fromValue = [NSNumber numberWithFloat:0.0]; 
     frameAnim.toValue = [NSNumber numberWithFloat:0.0]; 

     frameAnim.beginTime = AVCoreAnimationBeginTimeAtZero + CMTimeGetSeconds(gFrames[i].endTime); 
     frameAnim.speed = 1.0f; 

     [Frame addAnimation:frameAnim forKey:@"animateOpacity"]; 

     // add the frame layer to our parent layer 
     [parentLayer addSublayer:Frame]; 

     gError = nil; 

     // if there's another point after this one 
     if(i < gFrames.size()-1) 
     { 
      // add our video file to the composition with a range of this point's end and the next point's start 
      [mutableCompositionTrack insertTimeRange:CMTimeRangeMake(gFrames[i].startTime, 
          CMTimeMake(gFrames[i+1].startTime.value - gFrames[i].startTime.value, 600)) 
          ofTrack:gVideoTrack 
          atTime:CMTimeAdd(gFrames[i].startTime, timeOffset) error:&gError]; 

     } 
     // else just add our video file with a range of this points end point and the videos duration 
     else 
     { 
      [mutableCompositionTrack insertTimeRange:CMTimeRangeMake(gFrames[i].startTime, CMTimeSubtract(gVideoAsset.duration, gFrames[i].startTime)) ofTrack:gVideoTrack atTime:CMTimeAdd(gFrames[i].startTime, timeOffset) error:&gError]; 
     } 

     if(gError) 
     { 
      char errorMsg[256]; 
      sprintf(errorMsg, "Error inserting original video segment at: %d", i); 
      DebugLog(errorMsg); 
      GetError(); 
     } 
    } 

В настоящее время в этом сегменте непрозрачность рамы установлена ​​на 0.0f, однако, когда я установил его на 1.0f все это делает, это просто место, последний из этих кадров в верхней части видео в течение всего срока.

После этого смотри на экспорт с использованием AVAssetExportSession, как показано ниже

mutableVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; 

    // create a layer instruction for our newly created animation tool 
    AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:gVideoTrack]; 

    AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 
    [instruction setTimeRange:CMTimeRangeMake(kCMTimeZero, [mutableComposition duration])]; 
    [layerInstruction setOpacity:1.0f atTime:kCMTimeZero]; 
    [layerInstruction setOpacity:0.0f atTime:mutableComposition.duration]; 
    instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction]; 

    // set the instructions on our videoComposition 
    mutableVideoComposition.instructions = [NSArray arrayWithObject:instruction]; 

    // export final composition to a video file 

    // convert the videopath into a url for our AVAssetWriter to create a file at 
    NSString* vidPath = CreateNSString(outputVideoPath); 
    NSURL* vidURL = [NSURL fileURLWithPath:vidPath]; 

    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPreset1280x720]; 

    exporter.outputFileType = AVFileTypeMPEG4; 

    exporter.outputURL = vidURL; 
    exporter.videoComposition = mutableVideoComposition; 
    exporter.timeRange = CMTimeRangeMake(kCMTimeZero, mutableComposition.duration); 

    // Asynchronously export the composition to a video file and save this file to the camera roll once export completes. 
    [exporter exportAsynchronouslyWithCompletionHandler:^{ 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      if (exporter.status == AVAssetExportSessionStatusCompleted) 
      { 
       DebugLog("!!!file created!!!"); 
       _Close(); 
      } 
      else if(exporter.status == AVAssetExportSessionStatusFailed) 
      { 
       DebugLog("failed damn"); 
       DebugLog(cStringCopy([[[exporter error] localizedDescription] UTF8String])); 
       DebugLog(cStringCopy([[[exporter error] description] UTF8String])); 
       _Close(); 
      } 
      else 
      { 
       DebugLog("NoIdea"); 
       _Close(); 
      } 
     }); 
    }]; 


} 

Я получаю ощущение, что анимация не началась, но я не знаю. Правильно ли я собираюсь объединить данные изображения в видео, подобное этому?

Любая помощь будет принята с благодарностью.

ответ

3

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

Начиная с первой ссылкой, которую я изначально разместил ASSETWriterInput for making Video from UIImages on Iphone Issues я создал следующую функцию, чтобы создать мое временного видео

void CreateFrameImageVideo(NSString* path) 
{ 
    NSLog(@"Creating writer at path %@", path); 
    NSError *error = nil; 
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL: 
            [NSURL fileURLWithPath:path] fileType:AVFileTypeMPEG4 
                   error:&error]; 

    NSLog(@"Creating video codec settings"); 
    NSDictionary *codecSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSNumber numberWithInt:gVideoTrack.estimatedDataRate/*128000*/], AVVideoAverageBitRateKey, 
            [NSNumber numberWithInt:gVideoTrack.nominalFrameRate],AVVideoMaxKeyFrameIntervalKey, 
            AVVideoProfileLevelH264MainAutoLevel, AVVideoProfileLevelKey, 
            nil]; 

    NSLog(@"Creating video settings"); 
    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            AVVideoCodecH264, AVVideoCodecKey, 
            codecSettings,AVVideoCompressionPropertiesKey, 
            [NSNumber numberWithInt:1280], AVVideoWidthKey, 
            [NSNumber numberWithInt:720], AVVideoHeightKey, 
            nil]; 

    NSLog(@"Creating writter input"); 
    AVAssetWriterInput* writerInput = [[AVAssetWriterInput 
             assetWriterInputWithMediaType:AVMediaTypeVideo 
             outputSettings:videoSettings] retain]; 

    NSLog(@"Creating adaptor"); 
    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor 
                assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput 
                sourcePixelBufferAttributes:nil]; 

    [videoWriter addInput:writerInput]; 

    NSLog(@"Starting session"); 
    //Start a session: 
    [videoWriter startWriting]; 
    [videoWriter startSessionAtSourceTime:kCMTimeZero]; 


    CMTime timeOffset = kCMTimeZero;//CMTimeMake(0, 600); 

    NSLog(@"Video Width %d, Height: %d, writing frame video to file", gWidth, gHeight); 

    CVPixelBufferRef buffer; 

    for(int i = 0; i< gAnalysisFrames.size(); i++) 
    { 
     while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) { 
      NSLog(@"Waiting inside a loop"); 
      NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:0.1]; 
      [[NSRunLoop currentRunLoop] runUntilDate:maxDate]; 
     } 

     //Write samples: 
     buffer = pixelBufferFromCGImage(gAnalysisFrames[i].frameImage, gWidth, gHeight); 

     [adaptor appendPixelBuffer:buffer withPresentationTime:timeOffset]; 



     timeOffset = CMTimeAdd(timeOffset, gAnalysisFrames[i].duration); 
    } 

    while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) { 
     NSLog(@"Waiting outside a loop"); 
     NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:0.1]; 
     [[NSRunLoop currentRunLoop] runUntilDate:maxDate]; 
    } 

    buffer = pixelBufferFromCGImage(gAnalysisFrames[gAnalysisFrames.size()-1].frameImage, gWidth, gHeight); 
    [adaptor appendPixelBuffer:buffer withPresentationTime:timeOffset]; 

    NSLog(@"Finishing session"); 
    //Finish the session: 
    [writerInput markAsFinished]; 
    [videoWriter endSessionAtSourceTime:timeOffset]; 
    BOOL successfulWrite = [videoWriter finishWriting]; 

    // if we failed to write the video 
    if(!successfulWrite) 
    { 

     NSLog(@"Session failed with error: %@", [[videoWriter error] description]); 

     // delete the temporary file created 
     NSFileManager *fileManager = [NSFileManager defaultManager]; 
     if ([fileManager fileExistsAtPath:path]) { 
      NSError *error; 
      if ([fileManager removeItemAtPath:path error:&error] == NO) { 
       NSLog(@"removeItemAtPath %@ error:%@", path, error); 
      } 
     } 
    } 
    else 
    { 
     NSLog(@"Session complete"); 
    } 

    [writerInput release]; 

} 

После того, как видео создается он затем загружается как AVAsset и это трек извлекается тем видео вставляются путем замены следующей строки (из первого кодового блока в исходном сообщении)

[mutableCompositionTrack insertEmptyTimeRange:CMTimeRangeMake(CMTimeAdd(gFrames[i].startTime, timeOffset), gFrames[i].duration)]; 

с:

[mutableCompositionTrack insertTimeRange:CMTimeRangeMake(timeOffset,gAnalysisFrames[i].duration) 
            ofTrack:gFramesTrack 
            atTime:CMTimeAdd(gAnalysisFrames[i].startTime, timeOffset) error:&gError]; 

где gFramesTrack - это AVAssetTrack, созданный из видео временного кадра.

весь код, относящийся к объектам CALayer и CABasicAnimation, удален, поскольку он просто не работал.

Не самое изящное решение, я не думаю, что оно, по крайней мере, работает. Я надеюсь, что кто-то найдет это полезным.

Этот код также работает на устройствах IOS (проверено с помощью IPad 3)

Примечание стороны: Функция DebugLog из первого поста просто обратный вызов функции, которая печатает сообщения журнала, они могут быть при необходимости заменяется вызовами NSLog().

 Смежные вопросы

  • Нет связанных вопросов^_^