2015-05-11 6 views
23

मैंने यह प्रश्न कुछ बार पूछा है, लेकिन उनमें से कोई भी कोई कामकाजी उत्तर नहीं लगता है।AVFoundation - एक AVAsset को रिवर्स करें और आउटपुट वीडियो फ़ाइल

आवश्यकता वीडियो स्रोत के रूप में एक ही संपीड़न, प्रारूप और फ्रेम दर को रखने के लिए एक वीडियो फ़ाइल को रिवर्स और आउटपुट करने के लिए (केवल इसे विपरीत में नहीं चलाएं) की आवश्यकता है।

आदर्श रूप से, समाधान यह सब स्मृति या बफर में करने में सक्षम होगा और छवि फ़ाइलों में फ्रेम उत्पन्न करने से बच जाएगा (पूर्व में: AVAssetImageGenerator का उपयोग करके) और फिर इसे पुन: संकलित करें (संसाधन गहन, अविश्वसनीय समय के परिणाम, फ्रेम/मूल से छवि गुणवत्ता, आदि)।

-

मेरे योगदान: यह अभी भी काम नहीं कर रहा है, लेकिन सबसे अच्छा मैं अब तक की कोशिश की है: CMSampleBufferRef[] की एक सरणी AVAssetReader का उपयोग करते हुए नमूना फ्रेम में

  • पढ़ें।
  • इसे AVAssetWriter का उपयोग कर रिवर्स ऑर्डर में वापस लिखें।
  • समस्या: प्रत्येक फ्रेम के लिए समय की तरह लगता है CMSampleBufferRef में सहेजा गया है, इसलिए उन्हें पीछे की ओर भी काम नहीं करेगा।
  • अगला, मैंने रिवर्स/दर्पण फ्रेम के साथ प्रत्येक फ्रेम की समय की जानकारी को स्वैप करने का प्रयास किया।
  • समस्या: यह AVAssetWriter के साथ एक अज्ञात त्रुटि का कारण बनता है।
  • अगला कदम: मैं AVAssetWriterInputPixelBufferAdaptor

    - (AVAsset *)assetByReversingAsset:(AVAsset *)asset { 
        NSURL *tmpFileURL = [NSURL URLWithString:@"/tmp/test.mp4"];  
        NSError *error; 
    
        // initialize the AVAssetReader that will read the input asset track 
        AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; 
        AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] lastObject]; 
    
        AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:nil]; 
        [reader addOutput:readerOutput]; 
        [reader startReading]; 
    
        // Read in the samples into an array 
        NSMutableArray *samples = [[NSMutableArray alloc] init]; 
    
        while(1) { 
         CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer]; 
    
         if (sample == NULL) { 
          break; 
         } 
    
         [samples addObject:(__bridge id)sample]; 
         CFRelease(sample); 
        } 
    
        // initialize the the writer that will save to our temporary file. 
        CMFormatDescriptionRef formatDescription = CFBridgingRetain([videoTrack.formatDescriptions lastObject]); 
        AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil sourceFormatHint:formatDescription]; 
        CFRelease(formatDescription); 
    
        AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:tmpFileURL 
                     fileType:AVFileTypeMPEG4 
                     error:&error]; 
        [writerInput setExpectsMediaDataInRealTime:NO]; 
        [writer addInput:writerInput]; 
        [writer startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp((__bridge CMSampleBufferRef)samples[0])]; 
        [writer startWriting]; 
    
    
        // Traverse the sample frames in reverse order 
        for(NSInteger i = samples.count-1; i >= 0; i--) { 
         CMSampleBufferRef sample = (__bridge CMSampleBufferRef)samples[i]; 
    
         // Since the timing information is built into the CMSampleBufferRef 
         // We will need to make a copy of it with new timing info. Will copy 
         // the timing data from the mirror frame at samples[samples.count - i -1] 
    
         CMItemCount numSampleTimingEntries; 
         CMSampleBufferGetSampleTimingInfoArray((__bridge CMSampleBufferRef)samples[samples.count - i -1], 0, nil, &numSampleTimingEntries); 
         CMSampleTimingInfo *timingInfo = malloc(sizeof(CMSampleTimingInfo) * numSampleTimingEntries); 
         CMSampleBufferGetSampleTimingInfoArray((__bridge CMSampleBufferRef)sample, numSampleTimingEntries, timingInfo, &numSampleTimingEntries); 
    
         CMSampleBufferRef sampleWithCorrectTiming; 
         CMSampleBufferCreateCopyWithNewTiming(
                   kCFAllocatorDefault, 
                   sample, 
                   numSampleTimingEntries, 
                   timingInfo, 
                   &sampleWithCorrectTiming); 
    
         if (writerInput.readyForMoreMediaData) { 
          [writerInput appendSampleBuffer:sampleWithCorrectTiming]; 
         } 
    
         CFRelease(sampleWithCorrectTiming); 
         free(timingInfo); 
        } 
    
        [writer finishWriting]; 
    
        return [AVAsset assetWithURL:tmpFileURL]; 
    } 
    
+2

मुझे नहीं लगता कि यह वीडियो संपीड़न के तरीके के कारण संभव है ... मेरी समझ से आप केवल एक कीफ्रेम से आगे बढ़ सकते हैं, लेकिन पीछे नहीं .. मुख्य फ्रेम के बीच सभी फ्रेमों की गणना किए बिना – Bastian

+0

@Bastian क्या आप इसका मतलब बता सकते हैं कि आपका क्या मतलब है? मेरे पास एक सरणी में संग्रहीत प्रत्येक फ्रेम के लिए कच्चा नमूना डेटा (CMSampleBufferRef) है। –

+0

इसे पढ़ने के लिए बस एक एफवाईआई। मैंने इसे समझ लिया और अगले कुछ दिनों में जवाब पोस्ट कर रहा होगा। –

उत्तर

13

पिछले कुछ दिनों में इस पर काम किया गौर करने के लिए जा रहा हूँ और यह काम कर पाने में सक्षम था।

स्रोत यहाँ कोड: http://www.andyhin.com/post/5/reverse-video-avfoundation

, का उपयोग करता है AVAssetReader नमूने/फ्रेम बाहर पढ़ने के लिए छवि/पिक्सेल बफर निकालता है, और उसके बाद दर्पण फ्रेम की प्रस्तुति समय के साथ यह जोड़ देता है।

+1

github पर उल्लसित परिणाम के लिए प्लसोन ... – Shai

+0

इस कोड में समस्या है। यदि आप एक लैंडस्केप या पोर्ट्रेट वीडियो पास करते हैं तो इसका आउटपुट ओरिएंटेशन सब गड़बड़ हो जाता है। नियमित आईफोन हाई रेज वीडियो से भी परेशान न हों, यह नियंत्रण केवल स्मृति को अनंतता और दुर्घटनाओं तक चलाता है। –

+1

@SamB कोड अवधारणा का सबूत है। यह वास्तव में केवल एक फ़ाइल प्रकार और अभिविन्यास का समर्थन करता है (स्रोत कोड पर एक नज़र डालें)। यदि आप अन्य सेटिंग्स और प्रारूपों का समर्थन करना चाहते हैं तो आपको इसे संशोधित करने की आवश्यकता होगी। साथ ही, यदि आप बड़ी फ़ाइलों को संसाधित कर रहे हैं तो आप शायद इसे स्मृति में संग्रहीत नहीं करना चाहते हैं - बस सामान्य सेब समाधान का उपयोग करें और इसे डिस्क पर लिखें। –

संबंधित मुद्दे