সিনেমা হিসাবে আমি কীভাবে ইউআইআইমেজ অ্যারে রফতানি করব?


186

আমার একটি মারাত্মক সমস্যা আছে: আমার কাছে NSArrayবেশ কয়েকটি UIImageঅবজেক্ট রয়েছে। আমি এখন যা করতে চাই, সেগুলি থেকে চলচ্চিত্র তৈরি করা UIImages। তবে কীভাবে করব তা আমার কোনও ধারণা নেই।

আমি আশা করি কেউ আমাকে সহায়তা করতে পারে বা একটি কোড স্নিপেট পাঠাতে পারে যা আমি চাই এমন কিছু করে।

সম্পাদনা করুন: ভবিষ্যতের রেফারেন্সের জন্য - সমাধানটি প্রয়োগের পরে, ভিডিওটি যদি বিকৃত দেখাচ্ছে তবে নিশ্চিত করুন যে আপনি যে চিত্রগুলি / ক্ষেত্রটি তুলছেন তা প্রস্থটি 16 এর একাধিক here এখানে বহু ঘন্টা লড়াইয়ের পরে পাওয়া গেছে:
আমার চলচ্চিত্রটি ইউআইআইমেজ থেকে কেন আসে? বিকৃত?

এখানে সম্পূর্ণ সমাধান রয়েছে (কেবলমাত্র প্রস্থটি 16 টির একাধিক হবে তা নিশ্চিত করুন)
http://codethink.no-ip.org/wordpress/archives/673


@ জোল: সম্ভাব্য সমাধান নয়, প্রশ্নটি কী সম্পর্কিত তা ট্যাগগুলিতে coverেকে রাখা উচিত।
জর্জি ফ্রিত্শে

4
কেন না? উভয় AVFoundationএবং এফএফপিপেইগের জন্য ইতিমধ্যে একটি পোস্ট রয়েছে । আপনি যদি কিছু এভিফাউন্ডেশন সম্পর্কিত তথ্য সন্ধান করছিলেন তবে আপনি কি এই থ্রেডটি দেখতে পছন্দ করবেন না? (বা এটি মেটা থেকে
conকমত্য

@ জোল: ট্যাগগুলি প্রশ্নটিকে সঙ্কুচিত করে তোলে ( "একটি ট্যাগ একটি কীওয়ার্ড বা লেবেল যা আপনার প্রশ্নকে শ্রেণিবদ্ধ করে" ), সেই দুটি যুক্ত করে আপনি প্রসঙ্গটি পরিবর্তন করবেন। আমি ভেবেছিলাম এটি সুস্পষ্ট বলে মনে হয় তবে আমি যদি মেটাতে কিছু নিয়ে হোঁচট খায় তবে আমি আপনাকে জানিয়ে দেব। বিকল্পভাবে সেখানে একটি আলোচনা শুরু করুন।
জর্জি ফ্রিত্শে

3
হয়তো কারো জন্য উপযোগী হতে হবে - GitHub উপর আমার কোড github.com/sakrist/One-minute
SAKrisT

ডানা নেই, আছে কেবল জুল। [অফ-টপিকের জন্য দুঃখিত, তবে আমি প্রতিহত করতে পারিনি]
রব ভিএস

উত্তর:


217

কটাক্ষপাত AVAssetWriter এবং বাকি AV ফাউন্ডেশন ফ্রেমওয়ার্ক । লেখকের AVAssetWriterInput টাইপের একটি ইনপুট রয়েছে , যার ফলস্বরূপ অ্যাপেনডেম্পলবারফার নামে একটি পদ্ধতি রয়েছে: যা আপনাকে একটি ভিডিও স্ট্রিমে স্বতন্ত্র ফ্রেম যুক্ত করতে দেয়। মূলত আপনাকে:

1) তারে লেখক:

NSError *error = nil;
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:
    [NSURL fileURLWithPath:somePath] fileType:AVFileTypeQuickTimeMovie
    error:&error];
NSParameterAssert(videoWriter);

NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
    AVVideoCodecH264, AVVideoCodecKey,
    [NSNumber numberWithInt:640], AVVideoWidthKey,
    [NSNumber numberWithInt:480], AVVideoHeightKey,
    nil];
AVAssetWriterInput* writerInput = [[AVAssetWriterInput
    assetWriterInputWithMediaType:AVMediaTypeVideo
    outputSettings:videoSettings] retain]; //retain should be removed if ARC

NSParameterAssert(writerInput);
NSParameterAssert([videoWriter canAddInput:writerInput]);
[videoWriter addInput:writerInput];

2) একটি অধিবেশন শুরু করুন:

[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:…] //use kCMTimeZero if unsure

3) কিছু নমুনা লিখুন:

// Or you can use AVAssetWriterInputPixelBufferAdaptor.
// That lets you feed the writer input data from a CVPixelBuffer
// that’s quite easy to create from a CGImage.
[writerInput appendSampleBuffer:sampleBuffer];

4) অধিবেশন সমাপ্ত:

[writerInput markAsFinished];
[videoWriter endSessionAtSourceTime:…]; //optional can call finishWriting without specifying endTime
[videoWriter finishWriting]; //deprecated in ios6
/*
[videoWriter finishWritingWithCompletionHandler:...]; //ios 6.0+
*/

আপনাকে এখনও অনেকগুলি শূন্যস্থান পূরণ করতে হবে, তবে আমি মনে করি যে কেবলমাত্র শক্তিশালী অংশটি একটি থেকে পিক্সেল বাফার পাচ্ছে CGImage:

- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef) image
{
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
        [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
        nil];
    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width,
        frameSize.height, kCVPixelFormatType_32ARGB, (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, frameSize.width,
        frameSize.height, 8, 4*frameSize.width, rgbColorSpace, 
        kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextConcatCTM(context, frameTransform);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), 
        CGImageGetHeight(image)), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

frameSizeএটি CGSizeআপনার টার্গেট ফ্রেমের আকারের বর্ণনা দেয় এবং frameTransformএটি এমন একটি CGAffineTransformযা আপনি যখন চিত্রগুলিকে ফ্রেমে আঁকেন তখন আপনাকে রূপান্তর করতে দেয়।


2
যদিও এটি কাজ করে তবে কেবল এটির CGImageজন্য একটি CGBitmapContextআঁকাই ব্যর্থ CVPixelBufferis একইভাবে, এর পরিবর্তে একটি তৈরি করার CVPixelBufferপ্রতিটি সময়, AVAssetWriterInputPixelBufferAdaptorএর pixelBufferPoolরিসাইকেল বাফার করতে ব্যবহার করা উচিত।
রাপ্টেরিচ

8
ঠিক আছে, তখন আপনার কী করা উচিত, যখন আপনার কাছে নিয়মিত চিত্র ফাইল হিসাবে উত্স ডেটা থাকে?
জুল

3
আমি জোলের সাথে আছি ... আমাদের ডেটা উত্সে অনেকগুলি চিত্র থাকে তবে আমাদের কী করা উচিত? আমি .mov এ এনকোডিংয়ের চিত্রগুলির প্রায় 30 বা তার কম সেকেন্ডের স্মৃতি সতর্কতার মধ্যে দৌড়ে যাই এবং মেমরিটি কোথায় তৈরি হচ্ছে তা আমি বুঝতে পারি না
জেমস

9
@ আরপেটরিচ @ জোল আমি জিজ্ঞাসা করতে পারি কেন সিএমএস নমুনা বাফারআরফের পরিবর্তে সিভিপিক্সেলবফার ব্যবহার করবেন ? সিএমএস নমুনা বাফার প্যারামিটার ধরণের নয় appendSampleBuffer:? বিটিডাব্লু, আমি ওএস এক্স-এ এভিফাউন্ডেশনটি ব্যবহার করছি
অ্যান্ড্রু চ্যাং

12
এই ইচ্ছার সাহায্যের কেউ একদিন stackoverflow.com/questions/9691646/...
DogCoffee

41

এখানে উদ্দেশ্য 8-এ iOS8- এ সর্বশেষতম কোড রয়েছে in

এক্সকোড এবং আইওএস 8 এর সর্বশেষতম সংস্করণে এটি কাজ করতে আমাদের উপরের @ জোলের উত্তরে বিভিন্ন ধরণের টুইট করতে হয়েছিল। এখানে আমাদের সম্পূর্ণ কার্যকারী কোড যা ইউআইআইমেজেজের একটি অ্যারে নেয়, তাদের একটি। এমওভি ফাইল হিসাবে তৈরি করে, এটি একটি অস্থায়ী ডিরেক্টরিতে সংরক্ষণ করে, তারপরে এটি ক্যামেরার রোলে স্থানান্তরিত করে। আমরা এই কাজ পেতে একাধিক বিভিন্ন পোস্ট থেকে কোড একত্রিত। কোডটি আমাদের মন্তব্যে কাজ করার জন্য আমাদের যে ট্র্যাপগুলি সমাধান করতে হয়েছিল তা হাইলাইট করেছি।

(1) ইউআইআইমেজেসের সংগ্রহ তৈরি করুন

[self saveMovieToLibrary]


- (IBAction)saveMovieToLibrary
{
    // You just need the height and width of the video here
    // For us, our input and output video was 640 height x 480 width
    // which is what we get from the iOS front camera
    ATHSingleton *singleton = [ATHSingleton singletons];
    int height = singleton.screenHeight;
    int width = singleton.screenWidth;

    // You can save a .mov or a .mp4 file        
    //NSString *fileNameOut = @"temp.mp4";
    NSString *fileNameOut = @"temp.mov";

    // We chose to save in the tmp/ directory on the device initially
    NSString *directoryOut = @"tmp/";
    NSString *outFile = [NSString stringWithFormat:@"%@%@",directoryOut,fileNameOut];
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:outFile]];
    NSURL *videoTempURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), fileNameOut]];

    // WARNING: AVAssetWriter does not overwrite files for us, so remove the destination file if it already exists
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager removeItemAtPath:[videoTempURL path]  error:NULL];


    // Create your own array of UIImages        
    NSMutableArray *images = [NSMutableArray array];
    for (int i=0; i<singleton.numberOfScreenshots; i++)
    {
        // This was our routine that returned a UIImage. Just use your own.
        UIImage *image =[self uiimageFromCopyOfPixelBuffersUsingIndex:i];
        // We used a routine to write text onto every image 
        // so we could validate the images were actually being written when testing. This was it below. 
        image = [self writeToImage:image Text:[NSString stringWithFormat:@"%i",i ]];
        [images addObject:image];     
    }

// If you just want to manually add a few images - here is code you can uncomment
// NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/movie.mp4"]];
//    NSArray *images = [[NSArray alloc] initWithObjects:
//                      [UIImage imageNamed:@"add_ar.png"],
//                      [UIImage imageNamed:@"add_ja.png"],
//                      [UIImage imageNamed:@"add_ru.png"],
//                      [UIImage imageNamed:@"add_ru.png"],
//                      [UIImage imageNamed:@"add_ar.png"],
//                      [UIImage imageNamed:@"add_ja.png"],
//                      [UIImage imageNamed:@"add_ru.png"],
//                      [UIImage imageNamed:@"add_ar.png"],
//                      [UIImage imageNamed:@"add_en.png"], nil];



    [self writeImageAsMovie:images toPath:path size:CGSizeMake(height, width)];
}

এটিই হ'ল প্রধান পদ্ধতি যা আপনার সম্পদ রচয়িতা তৈরি করে এবং লেখার জন্য এতে চিত্র যুক্ত করে।

(2) একটি AVAssetWriter তারের আপ

-(void)writeImageAsMovie:(NSArray *)array toPath:(NSString*)path size:(CGSize)size
{

    NSError *error = nil;

    // FIRST, start up an AVAssetWriter instance to write your video
    // Give it a destination path (for us: tmp/temp.mov)
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
                                                           fileType:AVFileTypeQuickTimeMovie
                                                              error:&error];


    NSParameterAssert(videoWriter);

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

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

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                                                                                     sourcePixelBufferAttributes:nil];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    [videoWriter addInput:writerInput];

(3) একটি লিখন সেশন শুরু করুন (দ্রষ্টব্য: পদ্ধতিটি উপরে থেকে অব্যাহত রয়েছে)

    //Start a SESSION of writing. 
    // After you start a session, you will keep adding image frames 
    // until you are complete - then you will tell it you are done.
    [videoWriter startWriting];
    // This starts your video at time = 0
    [videoWriter startSessionAtSourceTime:kCMTimeZero];

    CVPixelBufferRef buffer = NULL;

    // This was just our utility class to get screen sizes etc.    
    ATHSingleton *singleton = [ATHSingleton singletons];

    int i = 0;
    while (1)
    {
        // Check if the writer is ready for more data, if not, just wait
        if(writerInput.readyForMoreMediaData){

            CMTime frameTime = CMTimeMake(150, 600);
            // CMTime = Value and Timescale.
            // Timescale = the number of tics per second you want
            // Value is the number of tics
            // For us - each frame we add will be 1/4th of a second
            // Apple recommend 600 tics per second for video because it is a 
            // multiple of the standard video rates 24, 30, 60 fps etc.
            CMTime lastTime=CMTimeMake(i*150, 600);
            CMTime presentTime=CMTimeAdd(lastTime, frameTime);

            if (i == 0) {presentTime = CMTimeMake(0, 600);} 
            // This ensures the first frame starts at 0.


            if (i >= [array count])
            {
                buffer = NULL;
            }
            else
            {
                // This command grabs the next UIImage and converts it to a CGImage
                buffer = [self pixelBufferFromCGImage:[[array objectAtIndex:i] CGImage]];
            }


            if (buffer)
            {
                // Give the CGImage to the AVAssetWriter to add to your video
                [adaptor appendPixelBuffer:buffer withPresentationTime:presentTime];
                i++;
            }
            else
            {

(4) অধিবেশন শেষ করুন (দ্রষ্টব্য: পদ্ধতিটি উপরে থেকে অবিরত থাকে)

                //Finish the session:
                // This is important to be done exactly in this order
                [writerInput markAsFinished];
                // WARNING: finishWriting in the solution above is deprecated. 
                // You now need to give a completion handler.
                [videoWriter finishWritingWithCompletionHandler:^{
                    NSLog(@"Finished writing...checking completion status...");
                    if (videoWriter.status != AVAssetWriterStatusFailed && videoWriter.status == AVAssetWriterStatusCompleted)
                    {
                        NSLog(@"Video writing succeeded.");

                        // Move video to camera roll
                        // NOTE: You cannot write directly to the camera roll. 
                        // You must first write to an iOS directory then move it!
                        NSURL *videoTempURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@", path]];
                        [self saveToCameraRoll:videoTempURL];

                    } else
                    {
                        NSLog(@"Video writing failed: %@", videoWriter.error);
                    }

                }]; // end videoWriter finishWriting Block

                CVPixelBufferPoolRelease(adaptor.pixelBufferPool);

                NSLog (@"Done");
                break;
            }
        }
    }    
}

(5) আপনার ইউআইআইএমেজগুলি একটি সিভিপিক্সেলবফারআরেফে রূপান্তর করুন
এই পদ্ধতিটি আপনাকে একটি সিভি পিক্সেল বাফার রেফারেন্স দেবে যা অ্যাসেট রাইটার দ্বারা প্রয়োজনীয়। এটি একটি সিজিআইমেজআরফ থেকে পাওয়া যায় যা আপনি আপনার ইউআইআইমেজ (উপরে) থেকে পান।

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image
{
    // This again was just our utility class for the height & width of the
    // incoming video (640 height x 480 width)
    ATHSingleton *singleton = [ATHSingleton singletons];
    int height = singleton.screenHeight;
    int width = singleton.screenWidth;

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    CVPixelBufferRef pxbuffer = NULL;

    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,
                                          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, width,
                                                 height, 8, 4*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;
}

()) আপনার ভিডিওটিকে ক্যামেরা রোলে নিয়ে যান কারণ AVAssetWriter সরাসরি ক্যামেরা রোলটিতে লিখতে পারে না, এটি ভিডিওটিকে "tmp / temp.mov" (অথবা আপনি যে নামের ফাইল নামটি উপরে দিয়েছিলেন) থেকে ক্যামেরা রোলে নিয়ে যান।

- (void) saveToCameraRoll:(NSURL *)srcURL
{
    NSLog(@"srcURL: %@", srcURL);

    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock =
    ^(NSURL *newURL, NSError *error) {
        if (error) {
            NSLog( @"Error writing image with metadata to Photo Library: %@", error );
        } else {
            NSLog( @"Wrote image with metadata to Photo Library %@", newURL.absoluteString);
        }
    };

    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:srcURL])
    {
        [library writeVideoAtPathToSavedPhotosAlbum:srcURL
                                    completionBlock:videoWriteCompletionBlock];
    }
}

উপরের জলের উত্তরটি আপনি কী করছেন তার একটি সুন্দর রূপরেখা দেয়। আমরা এই কোডটি ব্যাপকভাবে মন্তব্য করেছি যাতে আপনি দেখতে পাচ্ছেন কীভাবে এটি কার্য কোড ব্যবহার করে করা হয়েছিল।


3
সুইফট ২.১ এ কারও সমাধান হতে পারে?
কাইল কিম

2
সুইফটে কারও কি সমাধান আছে?
স্যাম

2
@ কাইলকিম কি আপনি সুইফটে সমাধান খুঁজে পেয়েছেন?
স্যাম

2
@ মিজারি পাঠ্য যুক্ত করতে, আপনি ভিডিওগুলিতে যে চিত্রগুলি যুক্ত করছেন সেগুলিতে কেবল পাঠ্য লিখুন তবে সেগুলি যুক্ত করার আগে। এসও-তে চিত্রগুলিতে কীভাবে পাঠ্য লিখবেন তার প্রচুর উদাহরণ রয়েছে। অডিওর জন্য, আমি আশা করি আপনি ভিডিওর দিকটি সম্পূর্ণ হওয়ার পরে একটি পদক্ষেপ হিসাবে ভিডিওটিতে যুক্ত করতে পারেন।
প্রেক্সাইটেলস

3
স্মৃতি সমস্যার জন্য কারও সমাধান আছে? প্রায় 3 বা 4 শতাধিক চিত্র, আমার অ্যাপ্লিকেশনটি মারা যায়। আমি ইমেজ ফাইলের পাথের একটি অ্যারে
পেরিয়ে যাচ্ছি

31

দ্রষ্টব্য: এটি একটি সুইফট 2.1 সমাধান (আইওএস 8 +, এক্সকোড 7.2)

গত সপ্তাহে আমি চিত্রগুলি থেকে একটি ভিডিও তৈরি করতে আইওএস কোডটি লিখতে বেরিয়েছি। আমার কিছুটা এভিফাউন্ডেশন অভিজ্ঞতা ছিল, তবে কোনও সিভিপিক্সেলবফারও শুনিনি। আমি এই পৃষ্ঠায় উত্তরগুলি এসেছি এবং এখানেও । সমস্ত কিছু ছত্রাক ছড়িয়ে ফেলতে এবং সুইফটে এগুলি আবার একসাথে রেখে আমার মস্তিষ্কের বোধগম্য হতে বেশ কয়েক দিন সময় লেগেছিল। নীচে আমি যা নিয়ে এসেছি is

দ্রষ্টব্য: আপনি নীচের সমস্ত কোড একটি একক সুইফ্ট ফাইলে অনুলিপি / পেস্ট করলে এটি সংকলন করা উচিত। আপনার কেবল টুইঙ্ক loadImages()এবং RenderSettingsমানগুলি দরকার।

পর্ব 1: জিনিস সেট আপ

এখানে আমি রফতানি সংক্রান্ত সমস্ত সেটিংসকে একটি একক RenderSettingsস্ট্রাক্টে গ্রুপ করি।

import AVFoundation
import UIKit
import Photos

struct RenderSettings {

    var width: CGFloat = 1280
    var height: CGFloat = 720
    var fps: Int32 = 2   // 2 frames per second
    var avCodecKey = AVVideoCodecH264
    var videoFilename = "render"
    var videoFilenameExt = "mp4"

    var size: CGSize {
        return CGSize(width: width, height: height)
    }

    var outputURL: NSURL {
        // Use the CachesDirectory so the rendered video file sticks around as long as we need it to.
        // Using the CachesDirectory ensures the file won't be included in a backup of the app.
        let fileManager = NSFileManager.defaultManager()
        if let tmpDirURL = try? fileManager.URLForDirectory(.CachesDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) {
            return tmpDirURL.URLByAppendingPathComponent(videoFilename).URLByAppendingPathExtension(videoFilenameExt)
        }
        fatalError("URLForDirectory() failed")
    }
}

পার্ট 2: ইমেজএনিমেটার

ImageAnimatorবর্গ আপনার ছবি সম্পর্কে জানেন এবং ব্যবহার VideoWriterউপস্থাপনা সম্পাদন করতে বর্গ। ভাবনাটি হ'ল ভিডিও কনটেন্ট কোডটি নিম্ন-স্তরের AVFoundation কোড থেকে আলাদা রাখা। আমি saveToLibrary()এখানে ক্লাস ফাংশন হিসাবে যুক্ত করেছি যা ফটো লাইব্রেরিতে ভিডিওটি সংরক্ষণের জন্য চেইনের শেষে ডাকা হয়।

class ImageAnimator {

    // Apple suggests a timescale of 600 because it's a multiple of standard video rates 24, 25, 30, 60 fps etc.
    static let kTimescale: Int32 = 600

    let settings: RenderSettings
    let videoWriter: VideoWriter
    var images: [UIImage]!

    var frameNum = 0

    class func saveToLibrary(videoURL: NSURL) {
        PHPhotoLibrary.requestAuthorization { status in
            guard status == .Authorized else { return }

            PHPhotoLibrary.sharedPhotoLibrary().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoURL)
                }) { success, error in
                    if !success {
                        print("Could not save video to photo library:", error)
                    }
            }
        }
    }

    class func removeFileAtURL(fileURL: NSURL) {
        do {
            try NSFileManager.defaultManager().removeItemAtPath(fileURL.path!)
        }
        catch _ as NSError {
            // Assume file doesn't exist.
        }
    }

    init(renderSettings: RenderSettings) {
        settings = renderSettings
        videoWriter = VideoWriter(renderSettings: settings)
        images = loadImages()
    }

    func render(completion: ()->Void) {

        // The VideoWriter will fail if a file exists at the URL, so clear it out first.
        ImageAnimator.removeFileAtURL(settings.outputURL)

        videoWriter.start()
        videoWriter.render(appendPixelBuffers) {
            ImageAnimator.saveToLibrary(self.settings.outputURL)
            completion()
        }

    }

    // Replace this logic with your own.
    func loadImages() -> [UIImage] {
        var images = [UIImage]()
        for index in 1...10 {
            let filename = "\(index).jpg"
            images.append(UIImage(named: filename)!)
        }
        return images
    }

    // This is the callback function for VideoWriter.render()
    func appendPixelBuffers(writer: VideoWriter) -> Bool {

        let frameDuration = CMTimeMake(Int64(ImageAnimator.kTimescale / settings.fps), ImageAnimator.kTimescale)

        while !images.isEmpty {

            if writer.isReadyForData == false {
                // Inform writer we have more buffers to write.
                return false
            }

            let image = images.removeFirst()
            let presentationTime = CMTimeMultiply(frameDuration, Int32(frameNum))
            let success = videoWriter.addImage(image, withPresentationTime: presentationTime)
            if success == false {
                fatalError("addImage() failed")
            }

            frameNum++
        }

        // Inform writer all buffers have been written.
        return true
    }

}

পার্ট 3: ভিডিও রাইটার

VideoWriterবর্গ সব AV ফাউন্ডেশন ভারী উত্তোলন করে। এটি বেশিরভাগ অংশের চারপাশে একটি মোড়ক AVAssetWriterএবং AVAssetWriterInput। এটিতে আমার দ্বারা লিখিত অভিনব কোডও রয়েছে যা কোনও চিত্রকে কীভাবে অনুবাদ করতে হয় তা জানে CVPixelBuffer

class VideoWriter {

    let renderSettings: RenderSettings

    var videoWriter: AVAssetWriter!
    var videoWriterInput: AVAssetWriterInput!
    var pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor!

    var isReadyForData: Bool {
        return videoWriterInput?.readyForMoreMediaData ?? false
    }

    class func pixelBufferFromImage(image: UIImage, pixelBufferPool: CVPixelBufferPool, size: CGSize) -> CVPixelBuffer {

        var pixelBufferOut: CVPixelBuffer?

        let status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBufferOut)
        if status != kCVReturnSuccess {
            fatalError("CVPixelBufferPoolCreatePixelBuffer() failed")
        }

        let pixelBuffer = pixelBufferOut!

        CVPixelBufferLockBaseAddress(pixelBuffer, 0)

        let data = CVPixelBufferGetBaseAddress(pixelBuffer)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGBitmapContextCreate(data, Int(size.width), Int(size.height),
            8, CVPixelBufferGetBytesPerRow(pixelBuffer), rgbColorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue)

        CGContextClearRect(context, CGRectMake(0, 0, size.width, size.height))

        let horizontalRatio = size.width / image.size.width
        let verticalRatio = size.height / image.size.height
        //aspectRatio = max(horizontalRatio, verticalRatio) // ScaleAspectFill
        let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit

        let newSize = CGSize(width: image.size.width * aspectRatio, height: image.size.height * aspectRatio)

        let x = newSize.width < size.width ? (size.width - newSize.width) / 2 : 0
        let y = newSize.height < size.height ? (size.height - newSize.height) / 2 : 0

        CGContextDrawImage(context, CGRectMake(x, y, newSize.width, newSize.height), image.CGImage)
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0)

        return pixelBuffer
    }

    init(renderSettings: RenderSettings) {
        self.renderSettings = renderSettings
    }

    func start() {

        let avOutputSettings: [String: AnyObject] = [
            AVVideoCodecKey: renderSettings.avCodecKey,
            AVVideoWidthKey: NSNumber(float: Float(renderSettings.width)),
            AVVideoHeightKey: NSNumber(float: Float(renderSettings.height))
        ]

        func createPixelBufferAdaptor() {
            let sourcePixelBufferAttributesDictionary = [
                kCVPixelBufferPixelFormatTypeKey as String: NSNumber(unsignedInt: kCVPixelFormatType_32ARGB),
                kCVPixelBufferWidthKey as String: NSNumber(float: Float(renderSettings.width)),
                kCVPixelBufferHeightKey as String: NSNumber(float: Float(renderSettings.height))
            ]
            pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput,
                sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
        }

        func createAssetWriter(outputURL: NSURL) -> AVAssetWriter {
            guard let assetWriter = try? AVAssetWriter(URL: outputURL, fileType: AVFileTypeMPEG4) else {
                fatalError("AVAssetWriter() failed")
            }

            guard assetWriter.canApplyOutputSettings(avOutputSettings, forMediaType: AVMediaTypeVideo) else {
                fatalError("canApplyOutputSettings() failed")
            }

            return assetWriter
        }

        videoWriter = createAssetWriter(renderSettings.outputURL)
        videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: avOutputSettings)

        if videoWriter.canAddInput(videoWriterInput) {
            videoWriter.addInput(videoWriterInput)
        }
        else {
            fatalError("canAddInput() returned false")
        }

        // The pixel buffer adaptor must be created before we start writing.
        createPixelBufferAdaptor()

        if videoWriter.startWriting() == false {
            fatalError("startWriting() failed")
        }

        videoWriter.startSessionAtSourceTime(kCMTimeZero)

        precondition(pixelBufferAdaptor.pixelBufferPool != nil, "nil pixelBufferPool")
    }

    func render(appendPixelBuffers: (VideoWriter)->Bool, completion: ()->Void) {

        precondition(videoWriter != nil, "Call start() to initialze the writer")

        let queue = dispatch_queue_create("mediaInputQueue", nil)
        videoWriterInput.requestMediaDataWhenReadyOnQueue(queue) {
            let isFinished = appendPixelBuffers(self)
            if isFinished {
                self.videoWriterInput.markAsFinished()
                self.videoWriter.finishWritingWithCompletionHandler() {
                    dispatch_async(dispatch_get_main_queue()) {
                        completion()
                    }
                }
            }
            else {
                // Fall through. The closure will be called again when the writer is ready.
            }
        }
    }

    func addImage(image: UIImage, withPresentationTime presentationTime: CMTime) -> Bool {

        precondition(pixelBufferAdaptor != nil, "Call start() to initialze the writer")

        let pixelBuffer = VideoWriter.pixelBufferFromImage(image, pixelBufferPool: pixelBufferAdaptor.pixelBufferPool!, size: renderSettings.size)
        return pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
    }

}

অংশ 4: এটি ঘটায়

একবার সবকিছু ঠিকঠাক হয়ে গেলে এগুলি আপনার 3 টি যাদু লাইন:

let settings = RenderSettings()
let imageAnimator = ImageAnimator(renderSettings: settings)
imageAnimator.render() {
    print("yes")
}

ওহে মানুষ, আপনি যদি একদিন তাড়াতাড়ি পোস্ট করতেন। :) এই থ্রেডে সবেমাত্র একটি সুইফ্ট সংস্করণ পোর্টিং শেষ হয়েছে। আপনার সংরক্ষণাগারটি ফাংশনে অন্তর্ভুক্ত করা হয়েছে, আশা করি আপনার আপত্তি নেই।
ক্র্যাশলোট

@ ক্রশলোট - আমি শুনছি! আমার মনে হয়েছিল এটি নিয়ে কাজ করা হচ্ছে। তবুও, এটি সমস্ত কিছু বোঝার এবং বিভিন্ন বিটগুলি ভেঙে ফেলার একটি ভাল অনুশীলন ছিল।
স্কুটার্ম্গ

ক্র্যাশলোট এবং @ স্কট রাপোসা। এটি এই প্রশ্নের সাথে সম্পর্কিত একটি মন্তব্য তবে একটি বিশেষ পরীক্ষার ক্ষেত্রে। আপনার কোডটি একেবারে অভূতপূর্ব এবং আশ্চর্যজনক, তবে মনে হচ্ছে এটি কোনও একক চিত্রের জন্য কাজ করছে না, যেখানে চিত্রগুলি। কোড == ১। এই সমস্যাটি সমাধান করার জন্য আমি কোডটি পরিবর্তন করেছি, তবে মনে হচ্ছে এটি অবিশ্বাস্যরকম কঠিন। আপনাকে বলছি যে কোনও সহায়তা সম্পূর্ণ আশ্চর্যজনক হবে। আমি এখানে একটি প্রশ্ন জিজ্ঞাসা করেছি স্ট্যাকওভারফ্লো . com/ প্রশ্ন / 80৮০35809/ ……। আমি আশা করছিলাম ছবিগুলির বিশেষ কেস === 1 মোকাবিলা করা যেতে পারে! ধন্যবাদ!
ছাপ

@ ইমপ্রেশন 7vx আমার কাছে এই মুহুর্তে কোড চলছে না তাই আমি এই ক্ষেত্রে বিশেষত এটি পরীক্ষা করতে পারি না। আমি নিশ্চিত না কেন এটি কাজ করবে না কেন। আপনি যখন বলছেন এটি কাজ করে না, আপনি কী দেখছেন? হতে পারে fps সেটিং সঙ্গে খেলুন। এটিকে ১ এ আনুন বা আপনার যদি এটিতে 2 টি চিত্রের সাথে কাজ করে থাকে তবে কেবল চিত্রটির নকল করুন।
স্কুটার্ম্গ

বলুন আমার 1 টি চিত্র রয়েছে, এখনই আমি এটির সদৃশ করছি এবং এটি কার্যকর। তবে যে কোনও কারণেই এটি কোনও একক চিত্র নিয়ে কাজ করে না। আমি ভেবেছিলাম আমি আপনাকে কেবল অবহিত করব! images = [image, image]কাজ করে কিন্তু একক চিত্র ব্যবহার করে না। ধরা যাক আমি সময়টি 10 ​​সেকেন্ডে পরিবর্তন করি, তারপরে আমি খুব শেষ ফ্রেম পর্যন্ত একটি কালো পর্দা দেখছি। বেশ আশ্চর্য।
ইমপ্রেশন

21

আমি জুলের মূল ধারণাগুলি গ্রহণ করেছি এবং AVAssetWriterInputPixelBufferAdaptor পদ্ধতিটি অন্তর্ভুক্ত করেছি এবং এটিকে থেকে সামান্য ফ্রেমওয়ার্কের সূচনা করেছি।

এটি পরীক্ষা করে দেখতে এবং এটি উন্নত করতে নির্দ্বিধায়! CEMovieMaker


2
@ ক্যামেরন ভাল তবে আমার একটি সমস্যা আছে, আমার ভিডিও যদি 1080 * 1920 হয়? আইফোন 5 এস, 6,6 প্লাস রিয়ার ক্যামেরা রেজোলিউশন, এই পরিস্থিতিতে ভিডিও গন্ডগোল করুন দয়া করে আমাকে সহায়তা করুন।
দীপেন চুদাসামা

1
হাই, আপনি কি আমাকে জানাতে পারবেন যেভাবে ভিডিওতে চিত্রগুলি প্রদর্শিত হচ্ছে তাতে আমি কীভাবে কম গতি সেট করতে পারি?
হিমা

ভিডিওতে কিছুটা বিলম্ব কীভাবে যুক্ত করা যায় যা সময়ের ফ্রেম প্রদর্শিত হবে তার পরিবর্তনের জন্য?
ভাইরুম্যাক্স

13

এখানে আইওএস ৮-তে পরীক্ষা করা একটি সুইফট ২.x সংস্করণ রয়েছে যা এটি @ স্কট রাপোসা এবং @ প্রক্সাইটেলস এর উত্তরগুলি এবং @acj এর কোডের সাথে অন্য প্রশ্নের জন্য অবদান রেখেছে comb @Acj থেকে কোডটি এখানে: https://gist.github.com/acj/6ae90aa1ebb8cad6b47b । @ টিমবুল কোডও সরবরাহ করেছিল।

@ স্কট রাপোসার মতো, আমি কখনও শুনিনি CVPixelBufferPoolCreatePixelBufferএবং অন্যান্য বিভিন্ন ফাংশন, সেগুলি কীভাবে ব্যবহার করতে হয় তা বুঝতে দেওয়া যাক।

আপনি নীচে যা দেখছেন তা বেশিরভাগই পরীক্ষার এবং ত্রুটির দ্বারা এবং অ্যাপল ডক্সটি পড়ে একত্রে আবদ্ধ হয়েছিল। সাবধানতার সাথে ব্যবহার করুন, এবং যদি ভুল থাকে তবে পরামর্শ দিন।

ব্যবহার:

import UIKit
import AVFoundation
import Photos

writeImagesAsMovie(yourImages, videoPath: yourPath, videoSize: yourSize, videoFPS: 30)

কোড:

func writeImagesAsMovie(allImages: [UIImage], videoPath: String, videoSize: CGSize, videoFPS: Int32) {
    // Create AVAssetWriter to write video
    guard let assetWriter = createAssetWriter(videoPath, size: videoSize) else {
        print("Error converting images to video: AVAssetWriter not created")
        return
    }

    // If here, AVAssetWriter exists so create AVAssetWriterInputPixelBufferAdaptor
    let writerInput = assetWriter.inputs.filter{ $0.mediaType == AVMediaTypeVideo }.first!
    let sourceBufferAttributes : [String : AnyObject] = [
        kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32ARGB),
        kCVPixelBufferWidthKey as String : videoSize.width,
        kCVPixelBufferHeightKey as String : videoSize.height,
        ]
    let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: sourceBufferAttributes)

    // Start writing session
    assetWriter.startWriting()
    assetWriter.startSessionAtSourceTime(kCMTimeZero)
    if (pixelBufferAdaptor.pixelBufferPool == nil) {
        print("Error converting images to video: pixelBufferPool nil after starting session")
        return
    }

    // -- Create queue for <requestMediaDataWhenReadyOnQueue>
    let mediaQueue = dispatch_queue_create("mediaInputQueue", nil)

    // -- Set video parameters
    let frameDuration = CMTimeMake(1, videoFPS)
    var frameCount = 0

    // -- Add images to video
    let numImages = allImages.count
    writerInput.requestMediaDataWhenReadyOnQueue(mediaQueue, usingBlock: { () -> Void in
        // Append unadded images to video but only while input ready
        while (writerInput.readyForMoreMediaData && frameCount < numImages) {
            let lastFrameTime = CMTimeMake(Int64(frameCount), videoFPS)
            let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)

            if !self.appendPixelBufferForImageAtURL(allImages[frameCount], pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) {
                print("Error converting images to video: AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer")
                return
            }

            frameCount += 1
        }

        // No more images to add? End video.
        if (frameCount >= numImages) {
            writerInput.markAsFinished()
            assetWriter.finishWritingWithCompletionHandler {
                if (assetWriter.error != nil) {
                    print("Error converting images to video: \(assetWriter.error)")
                } else {
                    self.saveVideoToLibrary(NSURL(fileURLWithPath: videoPath))
                    print("Converted images to movie @ \(videoPath)")
                }
            }
        }
    })
}


func createAssetWriter(path: String, size: CGSize) -> AVAssetWriter? {
    // Convert <path> to NSURL object
    let pathURL = NSURL(fileURLWithPath: path)

    // Return new asset writer or nil
    do {
        // Create asset writer
        let newWriter = try AVAssetWriter(URL: pathURL, fileType: AVFileTypeMPEG4)

        // Define settings for video input
        let videoSettings: [String : AnyObject] = [
            AVVideoCodecKey  : AVVideoCodecH264,
            AVVideoWidthKey  : size.width,
            AVVideoHeightKey : size.height,
            ]

        // Add video input to writer
        let assetWriterVideoInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
        newWriter.addInput(assetWriterVideoInput)

        // Return writer
        print("Created asset writer for \(size.width)x\(size.height) video")
        return newWriter
    } catch {
        print("Error creating asset writer: \(error)")
        return nil
    }
}


func appendPixelBufferForImageAtURL(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool {
    var appendSucceeded = false

    autoreleasepool {
        if  let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool {
            let pixelBufferPointer = UnsafeMutablePointer<CVPixelBuffer?>.alloc(1)
            let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
                kCFAllocatorDefault,
                pixelBufferPool,
                pixelBufferPointer
            )

            if let pixelBuffer = pixelBufferPointer.memory where status == 0 {
                fillPixelBufferFromImage(image, pixelBuffer: pixelBuffer)
                appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
                pixelBufferPointer.destroy()
            } else {
                NSLog("Error: Failed to allocate pixel buffer from pool")
            }

            pixelBufferPointer.dealloc(1)
        }
    }

    return appendSucceeded
}


func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBufferRef) {
    CVPixelBufferLockBaseAddress(pixelBuffer, 0)

    let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer)
    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()

    // Create CGBitmapContext
    let context = CGBitmapContextCreate(
        pixelData,
        Int(image.size.width),
        Int(image.size.height),
        8,
        CVPixelBufferGetBytesPerRow(pixelBuffer),
        rgbColorSpace,
        CGImageAlphaInfo.PremultipliedFirst.rawValue
    )

    // Draw image into context
    CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage)

    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0)
}


func saveVideoToLibrary(videoURL: NSURL) {
    PHPhotoLibrary.requestAuthorization { status in
        // Return if unauthorized
        guard status == .Authorized else {
            print("Error saving video: unauthorized access")
            return
        }

        // If here, save video to library
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoURL)
        }) { success, error in
            if !success {
                print("Error saving video: \(error)")
            }
        }
    }
}

এটির জন্য একটি সমাপ্তি কলব্যাক দরকার। অন্যথায় এটি লেখার আগেই ফিরে আসে। আমি এটি পরিবর্তন করেছি, এবং এটি কাজ করে। ধন্যবাদ!
sudo


1
আমি এই বার্তাটি পেয়েছি "সিজিবিটম্যাপকন্টেক্সটক্রিয়াট: অবৈধ ডেটা বাইট / সারি: 8 ইন্টিজার বিট / উপাদান, 3 উপাদান, কেসিজিমেজএলফেপ্রিমিটুলিপ্লাইড ফার্স্টের জন্য কমপক্ষে 13056 হওয়া উচিত।" ফাংশনে ফিলপিক্সেলবফারফ্রিমআইমেজ: সিজিবিটম্যাপকন্টেক্সটক্রিয়েট তৈরি করার সময়। কোন ধারণা কেন এই ঘটছে??
লিংকন সিড

3
@oneyenjug ওহে মানুষ, এর জন্য আপনাকে ধন্যবাদ, তবে আমি নিম্নলিখিত ত্রুটিটি পেয়েছি: "চিত্রগুলিকে ভিডিওতে রূপান্তর করার সময় ত্রুটি: পিক্সেলফুফরপুলটি অধিবেশন শুরুর পরে"। কোন ধারণা আমি ভুল হতে পারে কি?
টিম ভার্মুলেন

1
"চিত্রগুলিকে ভিডিওতে রূপান্তর করার ক্ষেত্রে ত্রুটি: সেশন শুরু করার পরে পিক্সেলফুফারপুল শূন্য" জন্য উপরের কোডটি ব্যবহার করে আপনি ব্যবহারকারীকে ফটো লাইব্রেরি অ্যাক্সেস করতে এবং ফাইলটি তৈরি করার অনুমতি চাইতে বলবেন, আপনি আবার চেষ্টা করবেন এবং পথের ফাইলটি ইতিমধ্যে থাকবে তৈরি করা হবে, তাই কোনও ফাইল ম্যানেজারের সাথে পরীক্ষা করুন এবং ফাইলটি সংরক্ষণের আগে উপস্থিত থাকলে তা মুছুন।
স্যাম বিং

7

চিত্রগুলিকে অ্যারেতে কীভাবে রূপান্তর করতে হয় তা এখানে সুইফট 3 সংস্করণ

import Foundation
import AVFoundation
import UIKit

typealias CXEMovieMakerCompletion = (URL) -> Void
typealias CXEMovieMakerUIImageExtractor = (AnyObject) -> UIImage?


public class ImagesToVideoUtils: NSObject {

    static let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    static let tempPath = paths[0] + "/exprotvideo.mp4"
    static let fileURL = URL(fileURLWithPath: tempPath)
//    static let tempPath = NSTemporaryDirectory() + "/exprotvideo.mp4"
//    static let fileURL = URL(fileURLWithPath: tempPath)


    var assetWriter:AVAssetWriter!
    var writeInput:AVAssetWriterInput!
    var bufferAdapter:AVAssetWriterInputPixelBufferAdaptor!
    var videoSettings:[String : Any]!
    var frameTime:CMTime!
    //var fileURL:URL!

    var completionBlock: CXEMovieMakerCompletion?
    var movieMakerUIImageExtractor:CXEMovieMakerUIImageExtractor?


    public class func videoSettings(codec:String, width:Int, height:Int) -> [String: Any]{
        if(Int(width) % 16 != 0){
            print("warning: video settings width must be divisible by 16")
        }

        let videoSettings:[String: Any] = [AVVideoCodecKey: AVVideoCodecJPEG, //AVVideoCodecH264,
                                           AVVideoWidthKey: width,
                                           AVVideoHeightKey: height]

        return videoSettings
    }

    public init(videoSettings: [String: Any]) {
        super.init()


        if(FileManager.default.fileExists(atPath: ImagesToVideoUtils.tempPath)){
            guard (try? FileManager.default.removeItem(atPath: ImagesToVideoUtils.tempPath)) != nil else {
                print("remove path failed")
                return
            }
        }


        self.assetWriter = try! AVAssetWriter(url: ImagesToVideoUtils.fileURL, fileType: AVFileTypeQuickTimeMovie)

        self.videoSettings = videoSettings
        self.writeInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
        assert(self.assetWriter.canAdd(self.writeInput), "add failed")

        self.assetWriter.add(self.writeInput)
        let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
        self.bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: self.writeInput, sourcePixelBufferAttributes: bufferAttributes)
        self.frameTime = CMTimeMake(1, 5)
    }

    func createMovieFrom(urls: [URL], withCompletion: @escaping CXEMovieMakerCompletion){
        self.createMovieFromSource(images: urls as [AnyObject], extractor:{(inputObject:AnyObject) ->UIImage? in
            return UIImage(data: try! Data(contentsOf: inputObject as! URL))}, withCompletion: withCompletion)
    }

    func createMovieFrom(images: [UIImage], withCompletion: @escaping CXEMovieMakerCompletion){
        self.createMovieFromSource(images: images, extractor: {(inputObject:AnyObject) -> UIImage? in
            return inputObject as? UIImage}, withCompletion: withCompletion)
    }

    func createMovieFromSource(images: [AnyObject], extractor: @escaping CXEMovieMakerUIImageExtractor, withCompletion: @escaping CXEMovieMakerCompletion){
        self.completionBlock = withCompletion

        self.assetWriter.startWriting()
        self.assetWriter.startSession(atSourceTime: kCMTimeZero)

        let mediaInputQueue = DispatchQueue(label: "mediaInputQueue")
        var i = 0
        let frameNumber = images.count

        self.writeInput.requestMediaDataWhenReady(on: mediaInputQueue){
            while(true){
                if(i >= frameNumber){
                    break
                }

                if (self.writeInput.isReadyForMoreMediaData){
                    var sampleBuffer:CVPixelBuffer?
                    autoreleasepool{
                        let img = extractor(images[i])
                        if img == nil{
                            i += 1
                            print("Warning: counld not extract one of the frames")
                            //continue
                        }
                        sampleBuffer = self.newPixelBufferFrom(cgImage: img!.cgImage!)
                    }
                    if (sampleBuffer != nil){
                        if(i == 0){
                            self.bufferAdapter.append(sampleBuffer!, withPresentationTime: kCMTimeZero)
                        }else{
                            let value = i - 1
                            let lastTime = CMTimeMake(Int64(value), self.frameTime.timescale)
                            let presentTime = CMTimeAdd(lastTime, self.frameTime)
                            self.bufferAdapter.append(sampleBuffer!, withPresentationTime: presentTime)
                        }
                        i = i + 1
                    }
                }
            }
            self.writeInput.markAsFinished()
            self.assetWriter.finishWriting {
                DispatchQueue.main.sync {
                    self.completionBlock!(ImagesToVideoUtils.fileURL)
                }
            }
        }
    }

    func newPixelBufferFrom(cgImage:CGImage) -> CVPixelBuffer?{
        let options:[String: Any] = [kCVPixelBufferCGImageCompatibilityKey as String: true, kCVPixelBufferCGBitmapContextCompatibilityKey as String: true]
        var pxbuffer:CVPixelBuffer?
        let frameWidth = self.videoSettings[AVVideoWidthKey] as! Int
        let frameHeight = self.videoSettings[AVVideoHeightKey] as! Int

        let status = CVPixelBufferCreate(kCFAllocatorDefault, frameWidth, frameHeight, kCVPixelFormatType_32ARGB, options as CFDictionary?, &pxbuffer)
        assert(status == kCVReturnSuccess && pxbuffer != nil, "newPixelBuffer failed")

        CVPixelBufferLockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let pxdata = CVPixelBufferGetBaseAddress(pxbuffer!)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: pxdata, width: frameWidth, height: frameHeight, bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pxbuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)
        assert(context != nil, "context is nil")

        context!.concatenate(CGAffineTransform.identity)
        context!.draw(cgImage, in: CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height))
        CVPixelBufferUnlockBaseAddress(pxbuffer!, CVPixelBufferLockFlags(rawValue: 0))
        return pxbuffer
    }
}

আমি এটি স্ক্রিন ক্যাপচারের সাথে একসাথে ব্যবহার করি, মূলত স্ক্রিন ক্যাপচারের একটি ভিডিও তৈরি করতে, এখানে সম্পূর্ণ গল্প / সম্পূর্ণ উদাহরণ


এটি বেশ দরিদ্র সুইফট কোড। এটি একটি কার্যকর উদাহরণ তাই আমি ডাউনকে কম করি নি, তবে ভবিষ্যতে আনার্প চাপিয়ে দেবে না (এমনকি যখন আপনি জানেন যে এটি ব্যর্থ হতে পারে না, এটি কেবল একটি খারাপ অভ্যাস এবং কডকে কম পাঠযোগ্য করে তোলে), গার্ড স্টেটমেন্ট ব্যবহার করুন এবং " আনরোপডভ্যালু = অপশনালভ্যালু "কোডকে আরও অনেক বেশি যুক্তিসঙ্গত এবং স্পষ্টতই সঠিক করে তুলুন। সুইফটে শর্তসাপেক্ষে পেরেনগুলি রাখবেন না। এবং কিছু স্পেস আপনার পরামিতি / পরিবর্তনশীল সংজ্ঞা পাঠযোগ্যতার সাথে সহায়তা করবে, তবে এটি কোনও সুইফ্ট সমস্যা নয়।
SafeFastExpressive

7

স্যুইফ্ট 3-তে স্লোট রাপোসা জবাবটি অনুবাদ করেছেন (কিছুটা খুব সামান্য পরিবর্তন সহ):

import AVFoundation
import UIKit
import Photos

struct RenderSettings {

    var size : CGSize = .zero
    var fps: Int32 = 6   // frames per second
    var avCodecKey = AVVideoCodecH264
    var videoFilename = "render"
    var videoFilenameExt = "mp4"


    var outputURL: URL {
        // Use the CachesDirectory so the rendered video file sticks around as long as we need it to.
        // Using the CachesDirectory ensures the file won't be included in a backup of the app.
        let fileManager = FileManager.default
        if let tmpDirURL = try? fileManager.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) {
            return tmpDirURL.appendingPathComponent(videoFilename).appendingPathExtension(videoFilenameExt)
        }
        fatalError("URLForDirectory() failed")
    }
}


class ImageAnimator {

    // Apple suggests a timescale of 600 because it's a multiple of standard video rates 24, 25, 30, 60 fps etc.
    static let kTimescale: Int32 = 600

    let settings: RenderSettings
    let videoWriter: VideoWriter
    var images: [UIImage]!

    var frameNum = 0

    class func saveToLibrary(videoURL: URL) {
        PHPhotoLibrary.requestAuthorization { status in
            guard status == .authorized else { return }

            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
            }) { success, error in
                if !success {
                    print("Could not save video to photo library:", error)
                }
            }
        }
    }

    class func removeFileAtURL(fileURL: URL) {
        do {
            try FileManager.default.removeItem(atPath: fileURL.path)
        }
        catch _ as NSError {
            // Assume file doesn't exist.
        }
    }

    init(renderSettings: RenderSettings) {
        settings = renderSettings
        videoWriter = VideoWriter(renderSettings: settings)
//        images = loadImages()
    }

    func render(completion: (()->Void)?) {

        // The VideoWriter will fail if a file exists at the URL, so clear it out first.
        ImageAnimator.removeFileAtURL(fileURL: settings.outputURL)

        videoWriter.start()
        videoWriter.render(appendPixelBuffers: appendPixelBuffers) {
            ImageAnimator.saveToLibrary(videoURL: self.settings.outputURL)
            completion?()
        }

    }

//    // Replace this logic with your own.
//    func loadImages() -> [UIImage] {
//        var images = [UIImage]()
//        for index in 1...10 {
//            let filename = "\(index).jpg"
//            images.append(UIImage(named: filename)!)
//        }
//        return images
//    }

    // This is the callback function for VideoWriter.render()
    func appendPixelBuffers(writer: VideoWriter) -> Bool {

        let frameDuration = CMTimeMake(Int64(ImageAnimator.kTimescale / settings.fps), ImageAnimator.kTimescale)

        while !images.isEmpty {

            if writer.isReadyForData == false {
                // Inform writer we have more buffers to write.
                return false
            }

            let image = images.removeFirst()
            let presentationTime = CMTimeMultiply(frameDuration, Int32(frameNum))
            let success = videoWriter.addImage(image: image, withPresentationTime: presentationTime)
            if success == false {
                fatalError("addImage() failed")
            }

            frameNum += 1
        }

        // Inform writer all buffers have been written.
        return true
    }

}


class VideoWriter {

    let renderSettings: RenderSettings

    var videoWriter: AVAssetWriter!
    var videoWriterInput: AVAssetWriterInput!
    var pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor!

    var isReadyForData: Bool {
        return videoWriterInput?.isReadyForMoreMediaData ?? false
    }

    class func pixelBufferFromImage(image: UIImage, pixelBufferPool: CVPixelBufferPool, size: CGSize) -> CVPixelBuffer {

        var pixelBufferOut: CVPixelBuffer?

        let status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBufferOut)
        if status != kCVReturnSuccess {
            fatalError("CVPixelBufferPoolCreatePixelBuffer() failed")
        }

        let pixelBuffer = pixelBufferOut!

        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

        let data = CVPixelBufferGetBaseAddress(pixelBuffer)
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: data, width: Int(size.width), height: Int(size.height),
                                bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)

        context!.clear(CGRect(x:0,y: 0,width: size.width,height: size.height))

        let horizontalRatio = size.width / image.size.width
        let verticalRatio = size.height / image.size.height
        //aspectRatio = max(horizontalRatio, verticalRatio) // ScaleAspectFill
        let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit

        let newSize = CGSize(width: image.size.width * aspectRatio, height: image.size.height * aspectRatio)

        let x = newSize.width < size.width ? (size.width - newSize.width) / 2 : 0
        let y = newSize.height < size.height ? (size.height - newSize.height) / 2 : 0

        context?.draw(image.cgImage!, in: CGRect(x:x,y: y, width: newSize.width, height: newSize.height))
        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

        return pixelBuffer
    }

    init(renderSettings: RenderSettings) {
        self.renderSettings = renderSettings
    }

    func start() {

        let avOutputSettings: [String: Any] = [
            AVVideoCodecKey: renderSettings.avCodecKey,
            AVVideoWidthKey: NSNumber(value: Float(renderSettings.size.width)),
            AVVideoHeightKey: NSNumber(value: Float(renderSettings.size.height))
        ]

        func createPixelBufferAdaptor() {
            let sourcePixelBufferAttributesDictionary = [
                kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32ARGB),
                kCVPixelBufferWidthKey as String: NSNumber(value: Float(renderSettings.size.width)),
                kCVPixelBufferHeightKey as String: NSNumber(value: Float(renderSettings.size.height))
            ]
            pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput,
                                                                      sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
        }

        func createAssetWriter(outputURL: URL) -> AVAssetWriter {
            guard let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: AVFileTypeMPEG4) else {
                fatalError("AVAssetWriter() failed")
            }

            guard assetWriter.canApply(outputSettings: avOutputSettings, forMediaType: AVMediaTypeVideo) else {
                fatalError("canApplyOutputSettings() failed")
            }

            return assetWriter
        }

        videoWriter = createAssetWriter(outputURL: renderSettings.outputURL)
        videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: avOutputSettings)

        if videoWriter.canAdd(videoWriterInput) {
            videoWriter.add(videoWriterInput)
        }
        else {
            fatalError("canAddInput() returned false")
        }

        // The pixel buffer adaptor must be created before we start writing.
        createPixelBufferAdaptor()

        if videoWriter.startWriting() == false {
            fatalError("startWriting() failed")
        }

        videoWriter.startSession(atSourceTime: kCMTimeZero)

        precondition(pixelBufferAdaptor.pixelBufferPool != nil, "nil pixelBufferPool")
    }

    func render(appendPixelBuffers: ((VideoWriter)->Bool)?, completion: (()->Void)?) {

        precondition(videoWriter != nil, "Call start() to initialze the writer")

        let queue = DispatchQueue(label: "mediaInputQueue")
        videoWriterInput.requestMediaDataWhenReady(on: queue) {
            let isFinished = appendPixelBuffers?(self) ?? false
            if isFinished {
                self.videoWriterInput.markAsFinished()
                self.videoWriter.finishWriting() {
                    DispatchQueue.main.async {
                        completion?()
                    }
                }
            }
            else {
                // Fall through. The closure will be called again when the writer is ready.
            }
        }
    }

    func addImage(image: UIImage, withPresentationTime presentationTime: CMTime) -> Bool {

        precondition(pixelBufferAdaptor != nil, "Call start() to initialze the writer")

        let pixelBuffer = VideoWriter.pixelBufferFromImage(image: image, pixelBufferPool: pixelBufferAdaptor.pixelBufferPool!, size: renderSettings.size)
        return pixelBufferAdaptor.append(pixelBuffer, withPresentationTime: presentationTime)
    }

}

2
ব্যবহারের উদাহরণ কী হবে?
চিউই দ্য চোরকি

-7

আচ্ছা খাঁটি উদ্দেশ্য-সিতে এটি প্রয়োগ করা কিছুটা কঠিন .... আপনি যদি জেলব্রোকেন ডিভাইসের জন্য বিকাশ করছেন তবে আপনার অ্যাপের অভ্যন্তর থেকে কমান্ড-লাইন সরঞ্জাম ffmpeg ব্যবহার করা ভাল ধারণা। কমান্ড সহ ছবিগুলি থেকে সিনেমা তৈরি করা বেশ সহজ:

ffmpeg -r 10 -b 1800 -i %03d.jpg test1800.mp4

নোট করুন যে চিত্রগুলি ক্রম অনুসারে নামকরণ করতে হবে এবং একই ডিরেক্টরিতেও স্থাপন করতে হবে। আরও তথ্যের জন্য একবার দেখুন: http://electron.mit.edu/~gsteele/ffmpeg/


8
ffmpeg সুপার ধীর হবে। হার্ডওয়্যার ত্বরণযুক্ত এফফাউন্ডেশন ক্লাসগুলি ব্যবহার করা আরও ভাল।
ছন্দবদ্ধ ফিস্টম্যান

2
এটি করা কঠিন নয়, এটির জন্য কেবল ডকুমেন্টেশন এবং রাইটিং কোড পড়া দরকার। আপনার অ্যাপ্লিকেশনটির সম্ভাব্য ব্যবহারকারীদের ফোন জাল ব্রেক করতে এবং এফএফপিইজি ইনস্টল করার চেয়ে অ্যাপ্লিকেশনগুলির বিকাশের জন্য আরও অনেক বেশি উপযুক্ত উপায়।
ডেভ ডুরবিন

-7

সিনেমা হিসাবে চিত্র লিখতে AVAssetWriter ব্যবহার করুন। আমি ইতিমধ্যে এখানে উত্তর দিয়েছি: - https://stackoverflow.com/a/19166876/1582217


4
এটি উত্তর নয়। 1) এটি অন্য পোস্টের লিঙ্ক যেখানে আপনি সমস্যার উত্তর দিচ্ছেন না 2) যদি আপনার কাছে আসলে উত্তরটি দেওয়া উচিত তবে তা দেওয়া উচিত।
ক্রিসালিক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.