Per quanto ne so, il AVMutableVideoCompositionLayerInstruction
non può essere semplicemente "aggiunto" o "aggiunta", come il tuo modo di codice.
Dal codice, credo che si desidera mantenere le informazioni sulle istruzioni video quando si uniscono le risorse video, ma le istruzioni non possono essere "copiate" direttamente.
Se si desidera fare ciò, consultare i documenti per AVVideoCompositionLayerInstruction
, ad es.
getTransformRampForTime:startTransform:endTransform:timeRange:
setTransformRampFromStartTransform:toEndTransform:timeRange:
setTransform:atTime:
getOpacityRampForTime:startOpacity:endOpacity:timeRange:
setOpacityRampFromStartOpacity:toEndOpacity:timeRange:
setOpacity:atTime:
getCropRectangleRampForTime:startCropRectangle:endCropRectangle:timeRange:
setCropRectangleRampFromStartCropRectangle:toEndCropRectangle:timeRange:
setCropRectangle:atTime:
Si dovrebbe usare getFoo...
metodi su traccia sorgente, quindi calc la insertTime
o timeRange
per la traccia finale, quindi setFoo...
, quindi aggiungere ai layerInstructions del videoComposition finale.
SI, un po 'complicato ... Inoltre, il più importante, non è possibile ottenere tutti gli effetti video che si applicano alla risorsa sorgente.
Quindi qual è il tuo scopo? E a cosa è supportata la risorsa sorgente?
Se si desidera unire alcuni file mp4/mov, basta eseguire il loop delle tracce e aggiungerle a AVMutableCompositionTrack
, non videoComposition
. E ho testato il tuo codice, funziona.
Se si desidera unire AVAssets che con le istruzioni video, vedere la spiegazione sopra e docs. E la mia pratica migliore è, prima di unire, salvare quegli AVAssets in un file usando AVAssetExportSession
, quindi basta unire file video.
p.s. Forse ci sono alcuni problemi con i tuoi file di test o risorse di origine.
codice dal mio progetto come Vine:
- (BOOL)generateComposition
{
[self cleanComposition];
NSUInteger segmentsCount = self.segmentsCount;
if (0 == segmentsCount) {
return NO;
}
AVMutableComposition *composition = [AVMutableComposition composition];
AVMutableVideoComposition *videoComposition = nil;
AVMutableVideoCompositionInstruction *videoCompositionInstruction = nil;
AVMutableVideoCompositionLayerInstruction *videoCompositionLayerInstruction = nil;
AVMutableAudioMix *audioMix = nil;
AVMutableCompositionTrack *videoTrack = nil;
AVMutableCompositionTrack *audioTrack = nil;
AVMutableCompositionTrack *musicTrack = nil;
CMTime currentTime = kCMTimeZero;
for (MVRecorderSegment *segment in self.segments) {
AVURLAsset *asset = segment.asset;
NSArray *videoAssetTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
NSArray *audioAssetTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
CMTime maxBounds = kCMTimeInvalid;
CMTime videoTime = currentTime;
for (AVAssetTrack *videoAssetTrack in videoAssetTracks) {
if (!videoTrack) {
videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
videoTrack.preferredTransform = CGAffineTransformIdentity;
videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
}
/* Fix orientation */
CGAffineTransform transform = videoAssetTrack.preferredTransform;
if (AVCaptureDevicePositionFront == segment.cameraPosition) {
transform = CGAffineTransformMakeTranslation(self.config.videoSize, 0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
} else if (AVCaptureDevicePositionBack == segment.cameraPosition) {
}
[videoCompositionLayerInstruction setTransform:transform atTime:videoTime];
/* Append track */
videoTime = [MVHelper appendAssetTrack:videoAssetTrack toCompositionTrack:videoTrack atTime:videoTime withBounds:maxBounds];
maxBounds = videoTime;
}
if (self.sessionConfiguration.originalVoiceOn) {
CMTime audioTime = currentTime;
for (AVAssetTrack *audioAssetTrack in audioAssetTracks) {
if (!audioTrack) {
audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
}
audioTime = [MVHelper appendAssetTrack:audioAssetTrack toCompositionTrack:audioTrack atTime:audioTime withBounds:maxBounds];
}
}
currentTime = composition.duration;
}
if (videoCompositionInstruction && videoCompositionLayerInstruction) {
videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration);
videoCompositionInstruction.layerInstructions = @[videoCompositionLayerInstruction];
videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.renderSize = CGSizeMake(self.config.videoSize, self.config.videoSize);
videoComposition.frameDuration = CMTimeMake(1, self.config.videoFrameRate);
videoComposition.instructions = @[videoCompositionInstruction];
}
// 添加背景音乐 musicTrack
NSURL *musicFileURL = self.sessionConfiguration.musicFileURL;
if (musicFileURL && musicFileURL.isFileExists) {
AVAsset *musicAsset = [AVAsset assetWithURL:musicFileURL];
AVAssetTrack *musicAssetTrack = [musicAsset tracksWithMediaType:AVMediaTypeAudio].firstObject;
if (musicAssetTrack) {
musicTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
if (CMTIME_COMPARE_INLINE(musicAsset.duration, >=, composition.duration)) {
// 如果背景音乐时长大于视频总时长, 则直接添加
[musicTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, composition.duration) ofTrack:musicAssetTrack atTime:kCMTimeZero error:NULL];
} else {
// 否则, 循环背景音乐
CMTime musicTime = kCMTimeZero;
CMTime bounds = composition.duration;
while (true) {
musicTime = [MVHelper appendAssetTrack:musicAssetTrack toCompositionTrack:musicTrack atTime:musicTime withBounds:bounds];
if (CMTIME_COMPARE_INLINE(musicTime, >=, composition.duration)) {
break;
}
}
}
}
}
// 处理音频
if (musicTrack) {
AVMutableAudioMixInputParameters *audioMixParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:musicTrack];
/* 背景音乐添加淡入淡出 */
AVAsset *musicAsset = musicTrack.asset;
CMTime crossfadeDuration = CMTimeMake(15, 10); // 前后都是1.5秒
CMTime halfDuration = CMTimeMultiplyByFloat64(musicAsset.duration, 0.5);
crossfadeDuration = CMTimeMinimum(crossfadeDuration, halfDuration);
CMTimeRange crossfadeRangeBegin = CMTimeRangeMake(kCMTimeZero, crossfadeDuration);
CMTimeRange crossfadeRangeEnd = CMTimeRangeMake(CMTimeSubtract(musicAsset.duration, crossfadeDuration), crossfadeDuration);
[audioMixParameters setVolumeRampFromStartVolume:0.0 toEndVolume:self.sessionConfiguration.musicVolume timeRange:crossfadeRangeBegin];
[audioMixParameters setVolumeRampFromStartVolume:self.sessionConfiguration.musicVolume toEndVolume:0.0 timeRange:crossfadeRangeEnd];
audioMix = [AVMutableAudioMix audioMix];
[audioMix setInputParameters:@[audioMixParameters]];
}
_composition = composition;
_videoComposition = videoComposition;
_audioMix = audioMix;
return YES;
}
- (AVPlayerItem *)playerItem
{
AVPlayerItem *playerItem = nil;
if (self.composition) {
playerItem = [AVPlayerItem playerItemWithAsset:self.composition];
if (!self.videoComposition.animationTool) {
playerItem.videoComposition = self.videoComposition;
}
playerItem.audioMix = self.audioMix;
}
return playerItem;
}
///=============================================
/// MVHelper
///=============================================
+ (CMTime)appendAssetTrack:(AVAssetTrack *)track toCompositionTrack:(AVMutableCompositionTrack *)compositionTrack atTime:(CMTime)atTime withBounds:(CMTime)bounds
{
CMTimeRange timeRange = track.timeRange;
atTime = CMTimeAdd(atTime, timeRange.start);
if (!track || !compositionTrack) {
return atTime;
}
if (CMTIME_IS_VALID(bounds)) {
CMTime currentBounds = CMTimeAdd(atTime, timeRange.duration);
if (CMTIME_COMPARE_INLINE(currentBounds, >, bounds)) {
timeRange = CMTimeRangeMake(timeRange.start, CMTimeSubtract(timeRange.duration, CMTimeSubtract(currentBounds, bounds)));
}
}
if (CMTIME_COMPARE_INLINE(timeRange.duration, >, kCMTimeZero)) {
NSError *error = nil;
[compositionTrack insertTimeRange:timeRange ofTrack:track atTime:atTime error:&error];
if (error) {
MVLog(@"Failed to append %@ track: %@", compositionTrack.mediaType, error);
}
return CMTimeAdd(atTime, timeRange.duration);
}
return atTime;
}
Il vostro 'layerInstructions' è non corrette, possono disporre guardare commentando l'ultima riga:' playerItem.videoComposition = mutableVideoComposition; ' –
vuoi dire non è corretta? Cosa non è corretto nelle istruzioni? Dopo aver commentato quella linea, ottengo una cornice nera tra tutti i video. – blancos