如何通过 AVAssetReader 播放声音

Posted

技术标签:

【中文标题】如何通过 AVAssetReader 播放声音【英文标题】:how to play sound by AVAssetReader 【发布时间】:2012-01-16 10:15:05 【问题描述】:

我正在使用 AVAssetReader 从视频文件中获取各个帧。我想知道如何播放 Mp4 文件中的音频。

方法[player play]的返回值为false,所以没有声音可以播放,但是为什么

谢谢。

创建 AVAssetReader

  NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"zhang" ofType:@"mp4"]];
    AVURLAsset *avasset = [[AVURLAsset alloc] initWithURL:url options:nil];
    AVAssetTrack *track1 = [[avasset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];

NSMutableDictionary *dic2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,  [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                             [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
                             [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                             [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved, nil]; 

output1 = [[AVAssetReaderTrackOutput alloc] initWithTrack:track1 outputSettings:dic2];

AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:avasset error:nil];

[reader addOutput:output1];
[reader startReading];

输出代码如下:

  CMSampleBufferRef sample = [output1 copyNextSampleBuffer];
    CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sample);

    size_t length = CMBlockBufferGetDataLength(blockBufferRef);
    UInt8 buffer[length];
    CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, buffer);

    NSData * data = [[NSData alloc] initWithBytes:buffer length:length];
    NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *filePath = [NSString stringWithFormat:@"%@/test.mp3", docDirPath];
    [data writeToFile:filePath atomically:YES];
    [data release];

    NSError *error;
    NSURL *fileURL = [NSURL fileURLWithPath:filePath];
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
    player.numberOfLoops = 0;
    [player play];

【问题讨论】:

您不会将视频帧提供给 OpenGL 吧?如果是这样的话,我正在尝试做一些非常相似的事情,并且想知道你是如何做到的。 【参考方案1】:

一种选择是避免使用 AVAudioPlayer,而是使用您已解码的 PCM 数据来填充 AudioQueue 用于输出。

理想的方法是从源 mp4 文件创建的 AVAsset 的音轨创建 AVComposition。该 AVComposition(作为 AVAsset 的子类)可以在 AVPlayer 中使用,然后它将只为您播放音轨。

您不能简单地将 PCM 数据块写入扩展名为“.mp3”的文件 - 这是一种编码格式。 AVAudioPlayer 将在文件中查找某些编码数据,而您写入的文件将不兼容,这就是您收到返回值 false 的原因。如果您决定将音频写入文件,请使用具有适当设置的AVAssetWriter。这可以写为核心音频文件以获得最佳性能。此步骤还可以将音频编码为另一种格式(例如 mp3 或 AAC),但这会带来沉重的性能成本,并且很可能不适合实时播放。

【讨论】:

谢谢。我将尝试使用 AVComposition @ichoyb 你能分享最终解决方案吗?我已经堆叠了 smilar 的东西。谢谢【参考方案2】:

资产不能立即播放。需要使用 key-value 观察来观察状态的值。请参考 Apple 的 AVCam 示例。

【讨论】:

以上是关于如何通过 AVAssetReader 播放声音的主要内容,如果未能解决你的问题,请参考以下文章

AVAssetReader+AVAssetReaderTrackOutput播放视频

启动 AVAssetReader 停止对远程 I/O AudioUnit 的回调

如何通过暂停前一个实例再次播放相同的声音?

如何通过 HTML 按钮播放声音

Swift,如何通过按下按钮一次又一次地播放声音

如何播放安卓通知声音