ios - AVFoundation - Reverse an AVAsset and output video file -
i've seen question asked few times, none of them seem have working answers.
the requirement reverse , output video file (not play in reverse) keeping same compression, format, , frame rate source video.
ideally, solution able in memory or buffer , avoid generating frames image files (for ex: using avassetimagegenerator) , recompiling (resource intensive, unreliable timing results, changes in frame/image quality original, etc.).
--
my contribution: still not working, best i've tried far:
- read in sample frames array of
cmsamplebufferref[]usingavassetreader. - write in reverse order using
avassetwriter. - problem: seems timing each frame saved in
cmsamplebufferrefappending them backwards not work. - next, tried swapping timing information of each frame reverse/mirror frame.
- problem: causes unknown error
avassetwriter. next step: i'm going
avassetwriterinputpixelbufferadaptor- (avasset *)assetbyreversingasset:(avasset *)asset { nsurl *tmpfileurl = [nsurl urlwithstring:@"/tmp/test.mp4"]; nserror *error; // initialize avassetreader read 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 samples 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 writer save 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 sample frames in reverse order for(nsinteger = samples.count-1; >= 0; i--) { cmsamplebufferref sample = (__bridge cmsamplebufferref)samples[i]; // since timing information built cmsamplebufferref // need make copy of new timing info. copy // timing data mirror frame @ samples[samples.count - -1] cmitemcount numsampletimingentries; cmsamplebuffergetsampletiminginfoarray((__bridge cmsamplebufferref)samples[samples.count - -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]; }
worked on over last few days , able working.
source code here: http://www.andyhin.com/post/5/reverse-video-avfoundation
uses avassetreader read out samples/frames, extracts image/pixel buffer, , appends presentation time of mirror frame.
Comments
Post a Comment