Объединение и преобразование файлов MP3 с помощью QTKit

Я пытаюсь объединить два файла .mp3 в один файл .wav с помощью QTKit. Вроде работает, но последние секунды второго файла обрезаются. Любые идеи?

- (IBAction)combineSelectedFilesAndOutputAsWAV {
    QTMovie *movie = [QTMovie movieWithFile:fileOne error:NULL];
    [movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];  
    QTMovie *segmentTwo = [QTMovie movieWithFile:fileTwo error:NULL];
    QTTimeRange range = { .time = QTZeroTime, .duration = [segmentTwo duration] };
    [segmentTwo setSelection:range];
    [movie appendSelectionFromMovie:segmentTwo];
    while([[movie attributeForKey:QTMovieLoadStateAttribute] longValue] != 100000L) {
        //wait until QTMovieLoadStateComplete
    }
    NSDictionary *exportAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], QTMovieExport,
                                     [NSNumber numberWithLong:kQTFileTypeWave], QTMovieExportType, nil];
    NSString *outputFile = [NSString stringWithFormat:@"%@.wav", outputFilename];
    NSString *filepath = [destinationDirectory stringByAppendingPathComponent:outputFile];
    if (![movie writeToFile:filepath withAttributes:exportAttributes]) {
       //ERROR
    } 
}

(Игнорируйте цикл while, ожидающий QTMovieLoadStateComplete. В будущем я переключусь на использование уведомлений. Но пока это не имеет значения...)


person Stephen Poletto    schedule 13.08.2010    source источник


Ответы (3)


1) Как правило, нет никакого способа быстро получить точную продолжительность произвольного файла MP3. Единственный надежный способ - декодировать файл целиком. Это верно даже для файлов CBR (некоторые кодировщики MP3 не обеспечивают точный битрейт). Таким образом, [длительность сегмента] может не подходить для случаев, когда требуется точная продолжительность. Для ваших целей вы можете попробовать использовать что-то вроде [длительность сегмента]+дельта или даже MaxDuration вместо [длительность сегмента].

2) В некоторых случаях (по крайней мере, если форматы объединяемых файлов совпадают) вы можете просто объединить файлы как двоичные данные (без использования QTKit). Более дорого: написать заголовок RIFF-WAV для целевого файла; звуковой поток appeng из первого файла (то есть исходного файла, исключая теги заголовка и хвоста, если они есть); добавить звуковой поток из второго файла; обновить заголовок RIFF-WAV.

person VitalyVal    schedule 14.09.2010
comment
Есть ли простой способ декодировать файл с помощью QTKit? Если нет, я могу просто использовать дельту, чтобы убедиться, что длина достаточна для добавления всего файла. - person Stephen Poletto; 15.09.2010
comment
Я не уверен в QTKit, но это можно сделать с помощью Core Audio (я только начинаю изучать Cocoa). Но по возможности следует избегать декодирования (хотя бы для экономии времени обработки). Я не вижу необходимости декодировать для ваших конкретных целей. То есть: просто используйте огромную MaxDuration, или используйте предложение N 2). - person VitalyVal; 15.09.2010

Похоже, настоящая проблема заключалась в том, что я делал запросы к объекту QTMovie сразу после его создания. Прежде чем запрашивать продолжительность объекта QTMovie, вы должны убедиться, что он полностью загружен. Я добавил следующее:

- (void)pasteSegmentToConcatenatedFile:(QTMovie *)segment {
    NSLog(@"LoadStateChanged for movie : %@", segment);
    if ([[segment attributeForKey:QTMovieLoadStateAttribute] longValue] >= kMovieLoadStateComplete) {
        NSLog(@"The movie is fully loaded.");
        QTTimeRange range = { .time = QTZeroTime, .duration = [segment duration] };
        [segment setSelection:range];
        [concatenatedFile appendSelectionFromMovie:segment];
        [self convertAndOutputConcatenatedMovie];
    }
}

Этот метод будет вызываться всякий раз, когда изменяется LoadState объекта QTMovie:

QTMovie *segmentTwo = [QTMovie movieWithFile:fileTwo error:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pasteSegmentToConcatenatedFile:) name:QTMovieLoadStateDidChangeNotification object:segmentTwo];
person Stephen Poletto    schedule 21.09.2010

Вы также можете просто установить синхронный загрузчик фильмов/mp3 вместо асинхронного, используя QTMovieOpenAsyncOKAttribute следующим образом:

NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:NO] , QTMovieOpenAsyncOKAttribute,
    @"path to file", QTMovieFileNameAttribute,
    [NSNumber numberWithBool:YES], QTMovieEditableAttribute,
     nil];
QTMovie *myMovie = [QTMovie movieWithAttributes:movieAttributes error:nil];

таким образом, продолжительность должна быть правильной, когда вы проверяете ее так:

[[myMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue].timeValue;
person Thamster    schedule 04.11.2010