AVFoundation - Video didn't play in AVMutablecomposition with AVplayer - ios

I am working on playing a online video file with AVplayer using AVMutablecomposition. The Video plays very fine when I play it on browser but I just cant get it play on my iPad.
Here's the code:
self.composition = [AVMutableComposition composition];
NSURL *urlVideo = [NSURL URLWithString:#"{video location}"];
AVURLAsset *videoAsset = [[AVURLAsset alloc]initWithURL:urlVideo options:nil];
AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *error = NULL;
AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];
CMTimeRange x = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]);
[compositionVideoTrack insertTimeRange:x ofTrack:videoTrack atTime:kCMTimeZero error:nil];
And the code to set the AVPlayer:
AVPlayerItem *playerItem = [[AVPlayerItem alloc]initWithAsset:composition];
self.player = [[AVPlayer alloc]initWithPlayerItem:playerItem];
AVPlayerLayer *layerVideo = [AVPlayerLayer playerLayerWithPlayer:self.player];
layerVideo.frame = self.view.layer.bounds;
layerVideo.backgroundColor = [UIColor orangeColor].CGColor;
layerVideo.position = CGPointMake(1024/2, 768/2-10);
[self.view.layer addSublayer:layerVideo];
[self.player play];
If I use "AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:videoAsset];" instead of "AVPlayerItem *playerItem = [[AVPlayerItem alloc]initWithAsset:composition];" then I can see the video playing.
Anyone know how to play the video using AVplayer with AVMutablecomposition ?

Your code lacks a couple error checkings but it works as is.
Unless you're in an iPad this line will send the player off-screen:
layerVideo.position = CGPointMake(1024/2, 768/2-10);
If that's the case, try with the right position.

Related

Audio video merge with main video sound

I can merge a video with another audio nicely. But I also need main video sound in final output video. That means I want to set main video sound with low volume. How can I do this?
-(void)mergeAndSave
{
//Create AVMutableComposition Object which will hold our multiple AVMutableCompositionTrack or we can say it will hold our video and audio files.
AVMutableComposition* mixComposition = [AVMutableComposition composition];
//Now first load your audio file using AVURLAsset. Make sure you give the correct path of your videos.
NSURL *audio_url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Asteroid_Sound" ofType:#"mp3"]];
AVURLAsset *audioAsset = [[AVURLAsset alloc]initWithURL:audio_url options:nil];
CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
//Now we are creating the first AVMutableCompositionTrack containing our audio and add it to our AVMutableComposition object.
AVMutableCompositionTrack *b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
//Now we will load video file.
NSURL *video_url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Asteroid_Video" ofType:#"m4v"]];
AVURLAsset *videoAsset = [[AVURLAsset alloc]initWithURL:video_url options:nil];
CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,audioAsset.duration);
//Now we are creating the second AVMutableCompositionTrack containing our video and add it to our AVMutableComposition object.
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:nil];
//decide the path where you want to store the final video created with audio and video merge.
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *outputFilePath = [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"FinalVideo.mov"]];
NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath])
[[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
//Now create an AVAssetExportSession object that will save your final video at specified path.
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
_assetExport.outputFileType = #"com.apple.quicktime-movie";
_assetExport.outputURL = outputFileUrl;
[_assetExport exportAsynchronouslyWithCompletionHandler:
^(void ) {
dispatch_async(dispatch_get_main_queue(), ^{
[self exportDidFinish:_assetExport];
});
}
];
}
This is my code how I merge a video with audio.
You can have multiple Audio tracks in a composition, you can create another track that would have the main video's audio:
AVMutableCompositionTrack *mainVideoAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[mainVideoAudioTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
In order to reduce the volume you would need to create use AVAudioMix:
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
//Instruction for main video audio track
AVMutableAudioMixInputParameters *mainAudioMixParams = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:mainVideoAudioTrack];
[mainAudioMixParams setVolume:0.25 atTime:kCMTimeZero];
//Instruction for background audio track
AVMutableAudioMixInputParameters *b_audioMixParams = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:b_compositionAudioTrack];
[b_audioMixParams setVolume:1 atTime:kCMTimeZero];
audioMix.inputParameters = #[b_audioMixParams, mainAudioMixParams];
Then when you are exporting you need to add the audio mix to the export session as so:
_assetExport.audioMix = audioMix;

How to achieve a playback rate of 4.0 with AVPlayer in iOS?

player.rate = 4.0 doesn't work for me.
Is it a good way to achieve rate of 4.0 with AVPlayer, or there is a better approach?
Use [AVComposition scaleTimeRange:toDuration:] to produce a fast-motion AVAsset.
float rate = 4.0;
AVAsset *asset = [AVAsset assetWithURL:someURL];
AVMutableComposition *composition = [AVMutableComposition composition];
NSError *error = nil;
[composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofAsset:asset
atTime:kCMTimeZero error:&error];
[composition scaleTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
toDuration:CMTimeMultiplyByFloat64(asset.duration, 1 / rate)];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];

Weird behaviour of AVPlayer

I am merging multiple videos and playing it in AVPlayer. Video starts fine, but after sometime video freezes and only audio plays. I have a UISlider which helps to go forward or backward in video using seekToTime:.
So the weird part is that after video freezes, if I use the slider to go forward or backward, video starts playing perfectly fine. I have tried my app in multiple devices.
So basically I have to use seekToTime: method to give the video a nudge every time it freezes.
My code for merging video and playing back is following:
AVAsset *asset0 = [self currentAsset:0];
AVAsset *asset1 = [self currentAsset:1];
AVAsset *asset2 = [self currentAsset:2];
AVAsset *asset3 = [self currentAsset:3];
AVAsset *asset4 = [self currentAsset:4];
NSArray *assets = #[asset0, asset1, asset2, asset3, asset4];
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
NSMutableArray *instructions = [NSMutableArray new];
CGSize size = CGSizeZero;
CMTime time = kCMTimeZero;
for (AVAsset *asset in assets)
{
AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
AVAssetTrack *audioAssetTrack = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject;
NSError *error;
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, assetTrack.timeRange.duration)
ofTrack:assetTrack
atTime:time
error:&error];
if (error) {
NSLog(#"Error - %#", error.debugDescription);
}
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, assetTrack.timeRange.duration)
ofTrack:audioAssetTrack
atTime:time
error:&error];
if (error) {
NSLog(#"Error - %#", error.debugDescription);
}
AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionInstruction.timeRange = CMTimeRangeMake(time, assetTrack.timeRange.duration);
videoCompositionInstruction.layerInstructions = #[[AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack]];
[instructions addObject:videoCompositionInstruction];
time = CMTimeAdd(time, assetTrack.timeRange.duration);
if (CGSizeEqualToSize(size, CGSizeZero)) {
size = assetTrack.naturalSize;;
}
}
AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = instructions;
mutableVideoComposition.frameDuration = CMTimeMake(1, 30);
mutableVideoComposition.renderSize = size;
pi = [AVPlayerItem playerItemWithAsset:mutableComposition];
pi.videoComposition = mutableVideoComposition;
player = [AVPlayer playerWithPlayerItem:[[CameraEngine engine] pi]];
player.volume = 0.75;
playerLayer = [AVPlayerLayer playerLayerWithPlayer: player];
playerLayer.frame = self.bounds;
[self.layer addSublayer: playerLayer];
[playerLayer setNeedsDisplay];
[player play];
UPDATE : Found out this link which describes similar problem. But his solution is not well understood.
Why do you call [AVPlayer playerWithPlayerItem:[[CameraEngine engine] pi]] instead of [AVPlayer playerWithPlayerItem:pi]? Your construct your composition and assign it to pi while it is not used anywhere. Try to use [AVPlayer playerWithPlayerItem:pi] instead.
If this will not help try to change your videoComposition generation. Try to do the following:
AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, mutableComposition.timeRange.duration);
videoCompositionInstruction.layerInstructions = #[[AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:[mutableComposition tracksWithMediaType:AVMediaTypeVideo].firstObject]];
AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = #[videoCompositionInstruction];
mutableVideoComposition.frameDuration = CMTimeMake(1, 30);
mutableVideoComposition.renderSize = size;
after your for loop. Hope this helps!

Save video with 1.5 playback speed in iOS sdk

I have one recorded video and i want to save it as a new video with 1.5 playback speed(fast-forward) in ios sdk . Can anyone please suggest how can i achieve this functionality?
Thanks
Yashesh
AVURLAsset* videoAsset = nil; //self.inputAsset;
//create mutable composition
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *videoInsertError = nil;
BOOL videoInsertResult = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero
error:&videoInsertError];
if (!videoInsertResult || nil != videoInsertError) {
//handle error
return;
}
//slow down whole video by 2.0
double videoScaleFactor = 2.0;
CMTime videoDuration = videoAsset.duration;
[compositionVideoTrack scaleTimeRange:CMTimeRangeMake(kCMTimeZero, videoDuration)
toDuration:CMTimeMake(videoDuration.value*videoScaleFactor, videoDuration.timescale)];
//export
AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetLowQuality];
Check this
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath = [bundle pathForResource:titleOfButton ofType:#"mov"];
NSURL *movieURL = [ NSURL fileURLWithPath:moviePath];
MPMoviePlayerController *themovie = [[MPMoviePlayerController alloc]initWithContentURL: movieURL];
[themovie play];
[themovie setCurrentPlaybackRate:2.f];

Shorten/edit video recorded as AVCaptureMovieFileOutput with fixed time

In my app I am recording video using the device's camera and AVCaptureSession to save. Long story short, I need to be able to cut out the first x seconds of this recorded clip. I do not want the user to be presented with an 'edit video'-view, and it is not a "fixed" amount of time per say, but at the end of the day, I am left here with a CMTime with a value of exactly how much I have to cut out from the beginning of the clip. I have been looking at AVAssetWriter etc, but no luck. For playback, I guess [player seekToTime:time]; will do, but I need the actual video to be time duration shorter, cut out from the beginning. What method, or where can I get documentation for this?
Have you tried AVMutableComposition? It will have processing time though. Something like:
// get your asset
AVAsset *asset = [AVAsset assetWithURL:yourURL];
// get asset tracks
AVAssetTrack *assetTrackVideo = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *assetTrackAudio = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
// create composition
AVMutableComposition *composition = [AVMutableComposition composition];
AVMutableCompositionTrack *trackVideo = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *trackAudio = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// YOUR_DURATION is something like CMTimeSubstruct(asset.duration, YOURTIME_START);
[trackVideo insertTimeRange:CMTimeRangeMake(YOURTIME_START, YOUR_DURATION)
ofTrack:assetTrackVideo
atTime:kCMTimeZero
error:nil];
[trackAudio insertTimeRange:CMTimeRangeMake(YOURTIME_START, YOUR_DURATION)
ofTrack:assetTrackAudio
atTime:kCMTimeZero
error:nil];
// do the orientation change if needed
NSString* filename = [NSString stringWithFormat:#"videoFileName-%d.mov",arc4random() % 1000];
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
NSURL *exporterURL = [NSURL fileURLWithPath:path];
// Create exporter
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = exporterURL;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
// NSLog(#"Finished Output composition with error '%#' reason '%#'", exporter.error.localizedDescription,exporter.error.localizedFailureReason);
}];

Resources