AVPlayerItem, состоящий из изображения

Мне нужно создать тихое «видео» переменной длины (т.е. это просто изображение), которое я могу использовать в AVPlayer на ios.

Кто-нибудь знает, как я могу создать AVPlayerItem, который просто состоит из изображения, которое длится n секунд?

Если мне нужно создать файл .mov, мне нужно, чтобы этот файл был очень маленьким.


person Goz    schedule 26.02.2015    source источник


Ответы (2)


Хорошо, я пошел с написанием моего собственного видео. Оказывается, если вы записываете видео с изображением, которое вы хотите, в первом и последнем ключевых кадрах (а это единственные ключевые кадры), то вы получаете хорошее компактное видео, которое не займет «слишком» много времени.

Мой код выглядит следующим образом:

- (CVPixelBufferRef) createPixelBufferOfSize: (CGSize) size fromUIImage: (UIImage*) pImage
{
    NSNumber*           numYes      = [NSNumber numberWithBool: YES];
    NSDictionary*       pOptions    = [NSDictionary dictionaryWithObjectsAndKeys:   numYes, kCVPixelBufferCGImageCompatibilityKey,
                                                                            numYes, kCVPixelBufferCGBitmapContextCompatibilityKey,
                                                                            nil];

    CVPixelBufferRef    retBuffer   = NULL;
    CVReturn            status      = CVPixelBufferCreate( kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef)pOptions, &retBuffer );

    CVPixelBufferLockBaseAddress( retBuffer, 0 );
    void*               pPixelData  = CVPixelBufferGetBaseAddress( retBuffer );

    CGColorSpaceRef     colourSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef        context     = CGBitmapContextCreate( pPixelData, size.width, size.height, 8, 4 * size.width, colourSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipFirst );

    CGSize              inSize      = pImage.size;
    float               inAspect    = inSize.width  / inSize.height;
    float               outAspect   = size.width    / size.height;

    CGRect drawRect;
    if ( inAspect > outAspect )
    {
        float   scale   = inSize.width / size.width;
        CGSize  outSize = CGSizeMake( size.width, inSize.height / scale );

        drawRect        = CGRectMake( 0, (size.height / 2) - (outSize.height / 2), outSize.width, outSize.height );
    }
    else
    {
        float   scale   = inSize.height / size.height;
        CGSize  outSize = CGSizeMake( inSize.width / scale, size.height );

        drawRect        = CGRectMake( (size.width / 2) - (outSize.width / 2), 0, outSize.width, outSize.height );
    }

    CGContextDrawImage( context, drawRect, [pImage CGImage] );

    CGColorSpaceRelease( colourSpace );
    CGContextRelease( context );

    CVPixelBufferUnlockBaseAddress( retBuffer, 0 );

    return retBuffer;
}

- (void) writeVideo: (NSURL*) pURL withImage: (UIImage*) pImage ofLength: (NSTimeInterval) length
{
    [[NSFileManager defaultManager] removeItemAtURL: pURL error: nil];

    NSError*                pError              = nil;
    AVAssetWriter*          pAssetWriter        = [AVAssetWriter assetWriterWithURL: pURL fileType: AVFileTypeQuickTimeMovie error: &pError];

    const int               kVidWidth           = 1920;//pImage.size.width;
    const int               kVidHeight          = 1080;//pImage.size.height;

    NSNumber*               numVidWidth         = [NSNumber numberWithInt: kVidWidth];
    NSNumber*               numVidHeight        = [NSNumber numberWithInt: kVidHeight];

    NSDictionary*           pVideoSettings      = [NSDictionary dictionaryWithObjectsAndKeys:   AVVideoCodecH264,   AVVideoCodecKey,
                                                                                                numVidWidth,        AVVideoWidthKey,
                                                                                                numVidHeight,       AVVideoHeightKey,
                                                                                                nil];

    AVAssetWriterInput*     pAssetWriterInput   = [AVAssetWriterInput assetWriterInputWithMediaType: AVMediaTypeVideo
                                                                                     outputSettings: pVideoSettings];
    [pAssetWriter addInput: pAssetWriterInput];

    AVAssetWriterInputPixelBufferAdaptor*    pAssetWriterInputPixelBufferAdaptor    =
                                                [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput: pAssetWriterInput
                                                                                                                 sourcePixelBufferAttributes: pVideoSettings];

    __block volatile int finished = 0;

    [pAssetWriter   startWriting];
    [pAssetWriter   startSessionAtSourceTime: kCMTimeZero];

    // Write the image.
    CVPixelBufferRef        pixelBuffer     = [self createPixelBufferOfSize: CGSizeMake( kVidWidth, kVidHeight ) fromUIImage: pImage];

    [pAssetWriterInputPixelBufferAdaptor appendPixelBuffer: pixelBuffer withPresentationTime: kCMTimeZero];
    [pAssetWriterInputPixelBufferAdaptor appendPixelBuffer: pixelBuffer withPresentationTime: CMTimeMake( length * 1000000, 1000000 )];

    CVPixelBufferRelease( pixelBuffer );

    [pAssetWriterInput  markAsFinished];

    // Set end time accurate to micro-seconds.
    [pAssetWriter   endSessionAtSourceTime: CMTimeMake( length * 1000000, 1000000 )];
    [pAssetWriter   finishWritingWithCompletionHandler: ^
        {
            OSAtomicIncrement32( &finished );
        }];

    // Wait for the writing to complete.
    while( finished == 0 )
    {
        [NSThread sleepForTimeInterval: 0.01];
    }
}

Вы можете заметить, что я устанавливаю для видео всегда разрешение 1920x1080 и размещаю изображение на месте.

person Goz    schedule 03.03.2015

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

yourplayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;

[[NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[yourplayer currentItem]];


- (void)playerItemDidReachEnd:(NSNotification *)notification {
        [[yourplayer currentItem] seekToTime:kCMTimeZero];
}

Если видео имеет продолжительность n секунд, то вы можете использовать счетчик в своем методе playerItemDidReachEnd и установить ограничение.

person kchromik    schedule 02.03.2015
comment
Хорошая идея. Но как бы я это сделал, если бы это не было кратно 1 секунде? - person Goz; 02.03.2015
comment
Почему бы вам просто не использовать и UIImageView для отображения изображения? - person kchromik; 02.03.2015
comment
если оно не было кратно 1 секунде? Вы имеете в виду, что видео длится всего 1 секунду? Я не понимаю вашего вопроса. - person kchromik; 02.03.2015
comment
Нет, это 1,5 секунды - person Goz; 02.03.2015
comment
Вы также можете создать .mov продолжительностью 0,5 секунды и зациклить его 3 раза. - person kchromik; 03.03.2015
comment
Хорошо, я на самом деле не знаю, как долго видео мне нужно заранее. Почти наверняка это будет не простое число. Мне тоже нужно несколько (до 6). Один может быть 2,3478, а другой может быть 15,942. - person Goz; 03.03.2015
comment
Этот веб-сайт позволяет загружать файлы изображений (png) и преобразовывать их в видео (mp4 и т. д.) видео .online-convert.com/convert-to-mp4 - person shim; 18.03.2016