Цикл AVMutableCompositionTrack

У меня есть две звуковые дорожки, которые я комбинирую друг с другом следующим образом:

AVMutableComposition *composition = [[AVMutableComposition alloc] init];

AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack setPreferredVolume:1.0];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:originalContentURL options:nil];
AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack atTime:kCMTimeZero error:nil];

AVMutableCompositionTrack *compositionAudioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack1 setPreferredVolume:0.01];
NSString *soundOne1  =[[NSBundle mainBundle]pathForResource:@"jingle1" ofType:@"m4a"];
NSURL *url1 = [NSURL fileURLWithPath:soundOne1];
AVAsset *avAsset1 = [AVURLAsset URLAssetWithURL:url1 options:nil];
AVAssetTrack *clipAudioTrack1 = [[avAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack1 atTime:kCMTimeZero error:nil];

Как видите, первые AVAsset — это базовая длина второй дорожки, а это означает, что если у меня будет длинная вторая дорожка, она будет обрезана, как я и хочу.

Однако мне нужно иметь возможность зацикливать вторую дорожку, чтобы, если первая слишком длинная, вторая продолжалась и продолжалась. Позже я должен сохранить полученный трек на диск, что также является важным фактором.

Проведя исследование, я обнаружил, что в iOS нет удобного способа зациклить дорожку. Одним из выходов было бы вставлять вторую дорожку в AVMutableComposition несколько раз снова и снова, но мне это кажется довольно странным. Любые идеи, основанные на теме, будут действительно полезны.


person Sergey Grischyov    schedule 26.11.2013    source источник


Ответы (3)


Я думаю, что это должно работать:

CMTime videoDuration = avAsset.duration;
if(CMTimeCompare(videoDuration, audioAsset.duration) == -1){
    [compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack1 atTime:kCMTimeZero error:nil];
}else if(CMTimeCompare(videoDuration, audioAsset.duration) == 1){
     CMTime currentTime = kCMTimeZero;
     while(YES){
           CMTime audioDuration = audioAsset.duration;
           CMTime totalDuration = CMTimeAdd(currentTime,audioDuration);
           if(CMTimeCompare(totalDuration, videoDuration)==1){
              audioDuration = CMTimeSubtract(totalDuration,videoDuration);

           }
           [compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioDuration) ofTrack:clipAudioTrack1 atTime:currentTime error:nil];
           currentTime = CMTimeAdd(currentTime, audioDuration);
           if(CMTimeCompare(currentTime, videoDuration) == 1 || CMTimeCompare(currentTime, videoDuration) == 0){
               break;
           }
     }
}
person Gaurav Singh    schedule 26.11.2013
comment
Работал как шарм, отлично! - person Sergey Grischyov; 26.11.2013
comment
Хороший ответ @GauravSingh - person Lalit Kumar Maurya; 26.11.2013
comment
@GauravSingh Я попробовал код выше. Но получите ошибку от AVAssetExportSession Видео не может быть создано с кодом ошибки: -11841. Любая идея, почему это потерпит неудачу? Если продолжительность видео короче, чем mp3, это нормально. - person resting; 05.03.2014
comment
@resting, пожалуйста, проверьте свой код и попробуйте записать продолжительность композиции Audiotrack в каждой итерации цикла, чтобы увидеть, все ли идет хорошо или нет. - person Gaurav Singh; 05.03.2014
comment
Это хорошие коды и большое спасибо автору. Но в некоторых ситуациях, например: длина видео больше, чем продолжительность звука, и звук будет воспроизводиться в цикле во время воспроизведения видео. Я думаю, что этот код: audioDuration = CMTimeSubtract(totalDuration, timeDuringMerge); следует заменить на audioDuration = CMTimeSubtract(timeDuringMerge, currentTime); - person Gallery; 15.04.2016

В ответе Гаурав вы должны изменить эту строку:
audioDuration = CMTimeSubtract(totalDuration,videoDuration);

to:
audioDuration = CMTimeSubtract(videoDuration,currentTime);

В противном случае аудиодорожка будет длиннее видео (оно будет просто черным)

person MindTrip    schedule 14.04.2014

Быстро с repeat whiel Loop.

исправить проблему с длиной видео

let audioLength = CMTime(value: 100, timescale: 1)
let videoLength = CMTime(value: 220, timescale: 1)

func getAudioRepeatInfo(audioLength:CMTime, videoLength:CMTime) -> [(at:CMTime, duration:CMTime)] {
    var at = [(at:CMTime, duration:CMTime)](), start = CMTime.zero
    repeat {
        let info = (at:start, duration: min(CMTimeSubtract(videoLength, start), audioLength) )
        at.append(info)
        start = CMTimeAdd(start, info.duration)
    } while start < videoLength
    return at
}

var ranges =  getAudioRepeatInfo(audioLength: audioLength, videoLength: videoLength)

Выход на площадку:

[(at: __C.CMTime(value: 0, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0), duration: __C.CMTime(value: 100, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)),
 (at: __C.CMTime(value: 100, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0), duration: __C.CMTime(value: 100, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0)),
 (at: __C.CMTime(value: 200, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0), duration: __C.CMTime(value: 20, timescale: 1, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0))]

Использовать:

for info in repeatInfo {
    try compositionAudioTrack1?.insertTimeRange(.init(start: .zero, duration: info.duration), of: assetTrack, at: info.at)
}
person SPatel    schedule 24.07.2020