varias una seleccionar pistas pista para dividir deja cortar como cancion archivo audio avfoundation core-audio avasset avassetreader

una - Muestra de extracción precisa de fragmentos de audio con AVFoundation



como seleccionar para cortar en audacity (2)

Escribí otra respuesta en la que AVAssetReader incorrectamente que AVAssetReader / AVAssetReaderTrackOutput no buscaba la muestra con precisión, pero parece estar rota cuando la pista de audio está incrustada dentro de un archivo de película, por lo que ha encontrado un error. ¡Felicitaciones!

La pista de audio descargada con un pase a través de AVAssetExportSession , como se menciona en un comentario en la respuesta de @ hotpaw2, funciona bien, incluso cuando busca límites fuera del paquete (por casualidad buscaba límites de paquetes, el archivo vinculado tiene 1024 marcos por paquete) - buscando los límites del paquete, sus diferencias ya no son cero, pero son muy, muy pequeñas / no audibles).

No encontré una solución alternativa, así que reconsidere el volcado de la pista comprimida. ¿Es tan costoso? Si realmente no desea hacer eso, puede decodificar los paquetes crudos usted mismo al pasar nil outputSettings: a su AVAssetReaderOutput y ejecutar su salida a través de un AudioQueue o (preferiblemente?) Un AudioConverter para obtener LPCM.

NB en este último caso, tendrá que manejar el redondeo hasta los límites del paquete cuando lo busque.

Problema

Estoy buscando extraer rangos precisos de muestreo de audio LPCM a partir de pistas de audio dentro de archivos de video. Actualmente, estoy buscando lograr esto usando AVAssetReaderTrackOutput comparación con un AVAssetTrack entregado al leer un AVURLAsset .

A pesar de preparar y garantizar que el activo se inicialice con AVURLAssetPreferPreciseDurationAndTimingKey establecido en YES , buscar una posición exacta de la muestra dentro de un activo parece ser inexacto.

NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @(YES) }; _asset = [[AVURLAsset alloc] initWithURL:fileURL options:options];

Esto se manifiesta con, por ejemplo, flujos AAC codificados a velocidad de bits variable. Si bien sé que los flujos de audio VBR presentan una sobrecarga de rendimiento en la búsqueda precisa, estoy dispuesto a pagar esto siempre que se me entreguen muestras precisas.

Cuando uso, por ejemplo, Extended Audio File Services y las API ExtAudioFileRef , puedo lograr búsquedas con precisión de muestra y extracción de audio. Del mismo modo con AVAudioFile , ya que esto se basa en ExtAudioFileRef .

El problema, sin embargo, es que también me gustaría extraer el audio de los contenedores de medios que rechazan las API de audio solo, pero que son compatibles con AVFLAundation a través de AVURLAsset .

Método

Se define un intervalo de tiempo preciso de muestra para la extracción utilizando CMTime y CMTimeRange , y se establece en AVAssetReaderTrackOutput . Las muestras se extraen iterativamente.

-(NSData *)readFromFrame:(SInt64)startFrame requestedFrameCount:(UInt32)frameCount { NSUInteger expectedByteCount = frameCount * _bytesPerFrame; NSMutableData *data = [NSMutableData dataWithCapacity:expectedByteCount]; // // Configure Output // NSDictionary *settings = @{ AVFormatIDKey : @( kAudioFormatLinearPCM ), AVLinearPCMIsNonInterleaved : @( NO ), AVLinearPCMIsBigEndianKey : @( NO ), AVLinearPCMIsFloatKey : @( YES ), AVLinearPCMBitDepthKey : @( 32 ), AVNumberOfChannelsKey : @( 2 ) }; AVAssetReaderOutput *output = [[AVAssetReaderTrackOutput alloc] initWithTrack:_track outputSettings:settings]; CMTime startTime = CMTimeMake( startFrame, _sampleRate ); CMTime durationTime = CMTimeMake( frameCount, _sampleRate ); CMTimeRange range = CMTimeRangeMake( startTime, durationTime ); // // Configure Reader // NSError *error = nil; AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:_asset error:&error]; if( !reader ) { fprintf( stderr, "avf : failed to initialize reader/n" ); fprintf( stderr, "avf : %s/n%s/n", error.localizedDescription.UTF8String, error.localizedFailureReason.UTF8String ); exit( EXIT_FAILURE ); } [reader addOutput:output]; [reader setTimeRange:range]; BOOL startOK = [reader startReading]; NSAssert( startOK && reader.status == AVAssetReaderStatusReading, @"Ensure we''ve started reading." ); NSAssert( _asset.providesPreciseDurationAndTiming, @"We expect the asset to provide accurate timing." ); // // Start reading samples // CMSampleBufferRef sample = NULL; while(( sample = [output copyNextSampleBuffer] )) { CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp( sample ); if( data.length == 0 ) { // First read - we should be at the expected presentation time requested. int32_t comparisonResult = CMTimeCompare( presentationTime, startTime ); NSAssert( comparisonResult == 0, @"We expect sample accurate seeking" ); } CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer( sample ); if( !buffer ) { fprintf( stderr, "avf : failed to obtain buffer" ); exit( EXIT_FAILURE ); } size_t lengthAtOffset = 0; size_t totalLength = 0; char *bufferData = NULL; if( CMBlockBufferGetDataPointer( buffer, 0, &lengthAtOffset, &totalLength, &bufferData ) != kCMBlockBufferNoErr ) { fprintf( stderr, "avf : failed to get sample/n" ); exit( EXIT_FAILURE ); } if( bufferData && lengthAtOffset ) { [data appendBytes:bufferData length:lengthAtOffset]; } CFRelease( sample ); } NSAssert( reader.status == AVAssetReaderStatusCompleted, @"Completed reading" ); [output release]; [reader release]; return [NSData dataWithData:data]; }

Notas

El tiempo de presentación que me da CMSampleBufferGetPresentationTimeStamp parece coincidir con lo que buscaba, pero como parece inexacto, no tengo la oportunidad de corregir y alinear las muestras que recupero.

¿Alguna idea de cómo hacer esto?

Alternativamente, ¿hay alguna manera de adaptar AVAssetTrack para ser utilizado por AVAudioFile o ExtAudioFile ?

¿Es posible acceder a la pista de audio a través de AudioFileOpenWithCallbacks ?

¿Es posible acceder a la transmisión de audio desde un contenedor de video de una manera diferente en macOS?


Un procedimiento que funciona es usar AVAssetReader, para leer su archivo AV comprimido, junto con AVAssetWriter, para escribir un nuevo archivo LPCM sin procesar de las muestras de audio. Entonces uno puede rápidamente indexar a través de este nuevo archivo PCM (o matriz mapeada en memoria, si es necesario) para extraer rangos exactos de muestras exactas, sin incurrir en anomalías de tamaño de decodificación por paquete VBR o dependiendo de los algoritmos iOS CMTimeStamp fuera del control de uno.

Puede que este no sea el procedimiento más eficiente en cuanto a tiempo o memoria, pero funciona.