iPhone - AVAssetWriter - Ошибка при создании фильма из фотографий с разрешением 1920×1080 пикселей

Я пытаюсь создать фильм из нескольких изображений. Он отлично работает с изображениями высокой четкости ({720, 1280}) или более низкими разрешениями. Но когда я пытаюсь создать фильм с полными изображениями высокой четкости {1080, 1920}, видео зашифровано. Вот ссылка, чтобы посмотреть, как это выглядит http://www.youtube.com/watch?v=BfYldb8e_18 . У вас есть идеи, что я могу делать неправильно?

- (void) createMovieWithOptions:(NSDictionary *) options
{
@autoreleasepool {
    NSString *path = [options valueForKey:@"path"];
    CGSize size =  [(NSValue *)[options valueForKey:@"size"] CGSizeValue];
    NSArray *imageArray = [options valueForKey:@"pictures"];
    NSInteger recordingFPS = [[options valueForKey:@"fps"] integerValue];
    BOOL success=YES;
    NSError *error = nil;

    AVAssetWriter *assetWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
                                                           fileType:AVFileTypeQuickTimeMovie
                                                              error:&error];
    NSParameterAssert(assetWriter);

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   AVVideoCodecH264, AVVideoCodecKey,
                                   [NSNumber numberWithFloat:size.width], AVVideoWidthKey,
                                   [NSNumber numberWithFloat:size.height], AVVideoHeightKey,
                                   nil];

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

    // Configure settings for the pixel buffer adaptor.
    NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput
                                                                                                                     sourcePixelBufferAttributes:bufferAttributes];

    NSParameterAssert(videoWriterInput);
    NSParameterAssert([assetWriter canAddInput:videoWriterInput]);

    videoWriterInput.expectsMediaDataInRealTime = NO;
    [assetWriter addInput:videoWriterInput];

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

    CVPixelBufferRef buffer = NULL;

    //convert uiimage to CGImage.

    int frameCount = 0;
    float progress = 0;
    float progressFromFrames = _progressView.progress; //only for create iflipbook movie

    for(UIImage * img in imageArray)
    {
        if([[NSThread currentThread] isCancelled])
        {
            [NSThread exit];
        }

        [condCreateMovie lock];
        if(isCreateMoviePaused)
        {
            [condCreateMovie wait];
        }

        uint64_t totalFreeSpace=[Utils getFreeDiskspace];
        if(((totalFreeSpace/1024ll)/1024ll)<50)
        {
            success=NO;
            break;
        }

        //        @autoreleasepool {
        NSLog(@"size:%@",NSStringFromCGSize(img.size));

        buffer = [[MovieWritter sharedMovieWritter] pixelBufferFromCGImage:[img CGImage] andSize:size];

        BOOL append_ok = NO;
        int j = 0;
        while (!append_ok && j < 60)
        {
            if(adaptor.assetWriterInput.readyForMoreMediaData)
            {
                CMTime frameTime = CMTimeMake(frameCount, recordingFPS);
                append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];

                CVPixelBufferRelease(buffer);

                [NSThread sleepForTimeInterval:0.1];


                if(isCreatingiFlipBookFromImported)
                    progress = (float)frameCount/(float)[imageArray count]/2.0 + progressFromFrames;
                else
                    progress = (float)frameCount/(float)[imageArray count];

                [[NSNotificationCenter defaultCenter] postNotificationName:@"movieCreationProgress" object:[NSNumber numberWithFloat:progress]];
            }
            else
            {
                [NSThread sleepForTimeInterval:0.5];
            }
            j++;
        }
        if (!append_ok)
        {
            NSLog(@"error appending image %d times %d\n", frameCount, j);
        }
        frameCount++;

        [condCreateMovie unlock];
    }

    //Finish the session:
    [videoWriterInput markAsFinished];
    [assetWriter finishWriting];

    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithBool:success], @"success",
                          path, @"path", nil];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"movieCreationFinished" object:dict];
}
}

*Редактировать . Вот код для [[MovieWritter sharedMovieWritter] pixelBufferFromCGImage:]

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

    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width,
                                          size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                                          &pxbuffer);
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

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

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, size.width,
                                                 size.height, 8, 4*size.width, rgbColorSpace,
                                                 kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                           CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

     return pxbuffer;
}
}

person flaviusilaghi    schedule 07.12.2012    source источник
comment
Пожалуйста, отправьте код для: [MovieWritter sharedMovieWritter] pixelBufferFromCGImage: тоже.   -  person Andrei Neacsu    schedule 07.12.2012


Ответы (4)


У меня была та же проблема, и этот ответ решил ее: размер видео должен быть кратен 16.

person Remy Cilia    schedule 03.12.2013
comment
Большое спасибо! Я боролся с различными битами этого в течение нескольких дней. Нелепое требование, но у Apple их полно. - person Linuxios; 19.05.2015

Почти уверен, что это либо ограничение HW, либо ошибка. Пожалуйста, отправьте радар.

person Cocoanetics    schedule 13.12.2012
comment
Я сам столкнулся с этой проблемой, и да, это на 90% аппаратное ограничение, так как в Симуляторе все работает как положено. - person Horatiu Paraschiv; 30.01.2013

как насчет того, чтобы получить пиксельный буфер

    //you could use a cgiimageref here instead
    CFDataRef imageData= CGDataProviderCopyData(CGImageGetDataProvider(imageView.image.CGImage));
    NSLog (@"copied image data");
    cvErr = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
                                         FRAME_WIDTH,
                                         FRAME_HEIGHT,
                                         kCVPixelFormatType_32BGRA,
                                         (void*)CFDataGetBytePtr(imageData),
                                         CGImageGetBytesPerRow(imageView.image.CGImage),
                                         NULL,
                                         NULL,
                                         NULL,
                                         &pixelBuffer);
    NSLog (@"CVPixelBufferCreateWithBytes returned %d", cvErr);

    CFAbsoluteTime thisFrameWallClockTime = CFAbsoluteTimeGetCurrent();  
    CFTimeInterval elapsedTime = thisFrameWallClockTime - firstFrameWallClockTime;  
    NSLog (@"elapsedTime: %f", elapsedTime);
    CMTime presentationTime =  CMTimeMake(elapsedTime * TIME_SCALE, TIME_SCALE);

    // write the sample
    BOOL appended = [assetWriterPixelBufferAdaptor  appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
    CVPixelBufferRelease(pixelBuffer);
    CFRelease(imageData);
    if (appended) {
        NSLog (@"appended sample at time %lf", CMTimeGetSeconds(presentationTime));
    } else {
        NSLog (@"failed to append");
        [self stopRecording];
        self.startStopButton.selected = NO;
    }
person Michelle Cannon    schedule 13.12.2012

Вы также можете установить предустановку параметров захвата, хотя обычно подходит высокое значение по умолчанию. */ Константы для определения предустановок параметров захвата с помощью свойства sessionPreset.

NSString *const AVCaptureSessionPresetPhoto;

NSString *const AVCaptureSessionPresetHigh;

NSString *const AVCaptureSessionPresetMedium;

NSString *const AVCaptureSessionPresetLow;

NSString *const AVCaptureSessionPreset352x288;

NSString *const AVCaptureSessionPreset640x480;

NSString *const AVCaptureSessionPreset1280x720;

NSString *const AVCaptureSessionPreset1920x1080;

NSString *const AVCaptureSessionPresetiFrame960x540;

NSString *const AVCaptureSessionPresetiFrame1280x720; */

//устанавливаем вот так

self.captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;

//или так, когда вы определяете avcapturesession

[self.captureSession setSessionPreset:AVCaptureSessionPreset1920x1080];

person Michelle Cannon    schedule 16.12.2012
comment
это самое неважное. Мы с коллегой должны исправить эту проблему, когда мы загружаем изображения FULL HD в AVAssetWriterInputPixelBufferAdaptor. Изображения можно брать из фотопленки... мы меняем их размер, чтобы они соответствовали разрешению FULL HD. Мы также захватываем кадры из буферов камеры, но они по-прежнему не работают с использованием AVAssetWriterInputPixelBufferAdaptor. Возможно, это ошибка или ограничение AVAssetWriterInputPixelBufferAdaptor. - person Andrei Neacsu; 17.12.2012
comment
У меня были некоторые проблемы, прежде чем я не установил предустановку, поэтому я упомянул об этом, плохое отношение не поможет вам быстрее. может ли формат пикселей быть неправильным, попробуйте изменить kCVPixelFormatType_32ARGB на kCVPixelFormatType_32BGRA, как в примере, который я показываю - person Michelle Cannon; 17.12.2012
comment
формат пикселя здесь ни при чем. Метод appendPixelBuffer ... также возвращает ответ YES. Мы проверили размер, настройки, все, но все равно не работает. - person Andrei Neacsu; 17.12.2012
comment
Что ж, с вашего YouTube похоже, что вы получаете очень искаженное изображение, и, поскольку я не вижу ничего плохого в вашем коде, я бы начал смотреть на ваш ввод, и проблемы с форматом являются логичным первым выбором. Вы говорите, что он отлично работает при более низких разрешениях, что озадачивает. и я не думаю, что это может быть что-то простое, например, использование int, где ожидается число с плавающей запятой, например, для высоты или ширины. - person Michelle Cannon; 17.12.2012
comment
как насчет этого, это вы вообще соотношение сторон учитываете. где вы тестируете более низкие форматы, используя то же соотношение сторон. stackoverflow.com/ вопросы/7327847/ - person Michelle Cannon; 17.12.2012
comment
соотношение сторон одинаковое. - person Andrei Neacsu; 18.12.2012