【问题标题】:iOS MOV rendering producing odd results (obj-c)iOS MOV 渲染产生奇怪的结果 (obj-c)
【发布时间】:2014-08-05 10:30:20
【问题描述】:

我正在创建一个 AS3 AIR 应用程序的扩展,它将以 ARGB32 形式接收像素数据,我想使用 H264 编解码器将其渲染到 .mov 容器,然后将其发送到用户的相机胶卷。我目前正在运行一个测试工具,该工具从本地路径(在我的桌面上)获取图像作为 .jpeg,获取像素数据并将其渲染 50 次成短片。我遇到的问题是,当我在 400x200 上渲染它时,它工作正常,但是当我使用任何其他尺寸的图像时,帧出来非常奇怪(图像对角展开,有时右下角有一个小黑条如果有缺失的像素)。

我感觉它与高度和宽度有关,因为这些是我所看到的唯一改变的东西。下面是我的代码:

   - (void)initFunction:(NSString *)context width:(int)widthData height:(int)heightData fps:(int)fpsData
     {
        NSLog(@"Initializing...");

        error = nil;
        frameCount = 0;                                     //Used for itterating
        fps = fpsData;                                      //FPS from AS3
        width = widthData;                                  //Width from AS3
        height = heightData;                                //Height from AS3
        numberOfSecondsPerFrame = 1.0f/(float)fps;          //Seconds per frame
        frameDuration = fps * numberOfSecondsPerFrame;      //Frame showing time
        imageSize = CGSizeMake(width, height);              //imageSize from AS3

        // Setup and remove pre-existing "render.mov"
        fileMgr = [NSFileManager defaultManager];
        documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
        videoOutputPath = [documentsDirectory stringByAppendingPathComponent:@"render.mov"];

        if ([fileMgr removeItemAtPath:videoOutputPath error:&error] != YES)
        {
            NSLog(@"This is the first VS render on this device: %@", [error localizedDescription]);
        } 
        else
        {
            NSLog(@"Removed previous render file in save path.");
        }

        NSLog(@"Starting render.");

        videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:videoOutputPath] fileType:AVFileTypeQuickTimeMovie error:&error];
        NSParameterAssert(videoWriter);

        videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                         AVVideoCodecH264, AVVideoCodecKey,
                         [NSNumber numberWithInt:width], AVVideoWidthKey,
                         [NSNumber numberWithInt:height], AVVideoHeightKey,
                         nil];

        videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

        NSDictionary *bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys
                                          [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];

        adaptor = [AVAssetWriterInputPixelBufferAdaptor
                   assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput
                   sourcePixelBufferAttributes:nil];

        NSParameterAssert(videoWriterInput);
        NSParameterAssert([videoWriter canAddInput:videoWriterInput]);
        videoWriterInput.expectsMediaDataInRealTime = YES;
        [videoWriter addInput:videoWriterInput];

        //Start a session:
        [videoWriter startWriting];
        [videoWriter startSessionAtSourceTime:kCMTimeZero];

        buffer = NULL;

        NSLog(@"**************************************************");
    }

    -(void) sendToCameraRoll:(NSString *)videoPath {
        UISaveVideoAtPathToSavedPhotosAlbum(videoPath, nil, NULL, nil);
    }

    //Take in URL from AIR
    - (void)addFrameFromBytes:(CGImageRef *)FrameBytes {

        UIImage *image = [UIImage imageWithCGImage:*FrameBytes];
        CGImageRef cgImage = [image CGImage];

        buffer = [self pixelBufferFromCGImage:cgImage];

        append_ok = NO;
        int j = 0;
        while (!append_ok) {
            if (adaptor.assetWriterInput.readyForMoreMediaData)  {

                frameCount = frameCount+1;
                NSLog(@"Processing video frame %d",frameCount);

                frameTime = CMTimeMake(frameCount*frameDuration,(int32_t) fps);
                append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
                [NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
                if(!append_ok){
                    error = videoWriter.error;
                    if(error!=nil) {
                        NSLog(@"Unresolved error %@,%@.", error, [error userInfo]);
                    }
                }
            } else {
                printf("Adaptor not ready %d, %d\n", frameCount, j);
                [NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
            }
            j++;
        }

        if (!append_ok) {
            printf("Error appending image %d times %d\n, with error.", frameCount, j);
        }
    }

    //Take in Path from AIR
    - (void)addFrameFromURL:(NSString *)FramePath {
        UIImage *image = [UIImage imageWithContentsOfFile:FramePath];
        CGImageRef cgImage = [image CGImage];

        buffer = [self pixelBufferFromCGImage:cgImage];

        append_ok = NO;
        int j = 0;
        while (!append_ok) {
            if (adaptor.assetWriterInput.readyForMoreMediaData)  {

                frameCount = frameCount+1;
                NSLog(@"Processing video frame %d",frameCount);

                frameTime = CMTimeMake(frameCount*frameDuration,(int32_t) fps);
                append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
                [NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
                if(!append_ok){
                    error = videoWriter.error;
                    if(error!=nil) {
                        NSLog(@"Unresolved error %@,%@.", error, [error userInfo]);
                    }
                }
            } else {
                printf("Adaptor not ready %d, %d\n", frameCount, j);
                [NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
            }
            j++;
        }

        if (!append_ok) {
            printf("Error appending image %d times %d\n, with error.", frameCount, j);
        }
    }

    - (void)saveVideoWithTrack:(NSString *)AudioTrackPath {

        NSLog(@"**************************************************");

        //Finish the session:
        [videoWriterInput markAsFinished];

        [videoWriter finishWritingWithCompletionHandler:^{
            NSLog(@"Render complete."); ///Used to satisfy 64-bit devices
        }];

        sleep(1); //Required to avoid crash due to overload

        NSLog(@"Render complete.");

        //Audio File Addition
        NSLog(@"Singing Tracks...");
        AVMutableComposition *mixComposition = [AVMutableComposition composition];
        //NSString *bundleDirectory = [[NSBundle mainBundle] bundlePath];
        //NSString *audio_inputFilePath = [bundleDirectory stringByAppendingPathComponent:@"30secs.mp3"];
        NSURL    *audio_inputFileUrl = [NSURL fileURLWithPath:AudioTrackPath];

        //Get the final video path
        NSURL    *video_inputFileUrl = [NSURL fileURLWithPath:videoOutputPath];

        //Create the final video amd export as MOV
        NSString *outputFilePath = [documentsDirectory stringByAppendingPathComponent:@"final_output.mov"];
        NSURL    *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];

        if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath]) {
            [[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
        }

        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];

        //nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
        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:AVAssetExportPresetHighestQuality];

        //Supported file types include...
        //NSLog(@"Supported file types are %@", [_assetExport supportedFileTypes]);

        _assetExport.outputFileType = @"com.apple.quicktime-movie";
        _assetExport.outputURL = outputFileUrl;

        [_assetExport exportAsynchronouslyWithCompletionHandler:^(){
            NSString *exported = [[NSString alloc] initWithString:[outputFileUrl path]];
            [self sendToCameraRoll:exported];
        }];

        NSLog(@"Saved to camera roll. %@", outputFilePath);

    }

    - (void)saveVideo {

        NSLog(@"**************************************************");

        //Finish the session:
        [videoWriterInput markAsFinished];

        [videoWriter finishWritingWithCompletionHandler:^{
            NSLog(@"Finished"); //Used to satisfy 64-bit systems.
        }];

        sleep(1); //Required to avoid system crash

        NSLog(@"Render complete.");

        [self sendToCameraRoll:videoOutputPath];

        NSLog(@"Saved to camera roll. %@", videoOutputPath);

    }

    - (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image {
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                                 [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                                 nil];
        CVPixelBufferRef pxbuffer = NULL;

        CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                              width,
                                              height,
                                              kCVPixelFormatType_32ARGB,
                                              ( CFDictionaryRef) options,
                                              &pxbuffer);
        if (status != kCVReturnSuccess){
            NSLog(@"Failed to create pixel buffer");
        }

        CVPixelBufferLockBaseAddress(pxbuffer, 0);
        void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);

        CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
        size_t bytesPerRow = 4*width; //BytesPerRow 4 bytes each pixel.
        CGContextRef imageContext = CGBitmapContextCreate(pxdata, width,
                                                          height, 8, bytesPerRow, rgbColorSpace,
                                                          kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipFirst);
        CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
        CGColorSpaceRelease(rgbColorSpace);
        CGContextRelease(imageContext);

        CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
        NSLog(@"bPR: %zd", pxbuffer);

        return pxbuffer;

    }

    - (void)viewDidLoad {
        [super viewDidLoad];

        //[self initFunction:nil width:1138 height:640 fps:25];
        //[self initFunction:nil width:910 height:512 fps:25];
        //[self initFunction:nil width:400 height:200 fps:25];
        [self initFunction:nil width:50 height:50 fps:25];

        //find url...
        //get pixel data...

        //loop through writing it from the pixel data
        for (int i = 1; i <= 50; i++) {
            //NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/sequence/%06d.jpg", i];
            //NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/ThisIsATest/ThisIsATest/image1.jpg"];
            //NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/ThisIsATest/ThisIsATest/image2.jpg"];
            //NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/mini.jpg"];
            NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/50x50.jpg"];
            NSLog(@"%@", string);
            [self addFrameFromURL:string];
        }

        [self saveVideo];

    }

编辑:如果图像为 400 x 200,则所有图像都可以使用。 所有这些都适用于 800 x 400、1600 x 800。 600 x 300 不起作用。 可能是纵横比?

【问题讨论】:

    标签: ios objective-c xcode codec movie


    【解决方案1】:

    使用特定维度解决了这个问题。 AVAssetWriter 仅支持特定的“有效”纵横比。

    【讨论】:

    • 这是一个很弱的答案。 OP 明确指出,600 x 300 不起作用。您能否描述“使用特定尺寸”的含义?也许甚至发布一些代码?
    • @Okuma.Scott 那是我的备用家庭帐户,我目前正在处理这个项目,但是我修复了参考本文中有效视频渲染纵横比的问题特定尺寸.. @987654321 @
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 2013-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 2015-10-04
    相关资源
    最近更新 更多