簡體   English   中英

使用ffmpeg和AVAudioPlayer播放音頻

[英]Playing audio using ffmpeg and AVAudioPlayer

我正在嘗試使用ffmpeg讀取音頻文件(iOS不支持),然后使用AVAudioPlayer播放它。 我花了一段時間才能在iOS項目中構建ffmpeg,但是最終我還是使用kewlbear / FFmpeg-iOS-build-script

經過大量的網絡搜索,包括stackoverflow,這是我現在的片段。 我發現的最好的例子之一就是這里

我相信這是所有相關的代碼。 我添加了評論,以使您知道我在做什么以及我需要在哪里進行一些聰明的事情。

#import "FFmpegWrapper.h"
#import <AVFoundation/AVFoundation.h>

AVFormatContext *formatContext = NULL;
AVStream *audioStream = NULL;

av_register_all();
avformat_network_init();
avcodec_register_all();

// this is a file locacted on my NAS
int opened = avformat_open_input(&formatContext, @"http://192.168.1.70:50002/m/NDLNA/43729.flac", NULL, NULL);

// can't open file
if(opened == 1) {
    avformat_close_input(&formatContext);
}

int streamInfoValue = avformat_find_stream_info(formatContext, NULL);

// can't open stream
if (streamInfoValue < 0)
{
    avformat_close_input(&formatContext);
}

// number of streams available
int inputStreamCount = formatContext->nb_streams;

for(unsigned int i = 0; i<inputStreamCount; i++)
{
    // I'm only interested in the audio stream
    if(formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
    {
        // found audio stream
        audioStream = formatContext->streams[i];
    }
}

if(audioStream == NULL) {
    // no audio stream        
}

AVFrame* frame = av_frame_alloc();

AVCodecContext* codecContext = audioStream->codec;

codecContext->codec = avcodec_find_decoder(codecContext->codec_id);
if (codecContext->codec == NULL)
{
    av_free(frame);
    avformat_close_input(&formatContext);
    // no proper codec found
}
else if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
{
    av_free(frame);
    avformat_close_input(&formatContext);
    // could not open the context with the decoder
}

// this is displaying: This stream has 2 channels and a sample rate of 44100Hz
// which makes sense
NSLog(@"This stream has %d channels and a sample rate of %dHz", codecContext->channels, codecContext->sample_rate);

AVPacket packet;
av_init_packet(&packet);

// this is where I try to store in the sound data
NSMutableData *soundData = [[NSMutableData alloc] init];

while (av_read_frame(formatContext, &packet) == 0)
{
    if (packet.stream_index == audioStream->index)
    {
        // Try to decode the packet into a frame
        int frameFinished = 0;
        avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet);

        // Some frames rely on multiple packets, so we have to make sure the frame is finished before
        // we can use it
        if (frameFinished)
        {
            // this is where I think something clever needs to be done
            // I need to store some bytes, but I can't figure out what exactly and what length?
            // should the length be multiplied by the of the number of channels?
            NSData *frameData = [[NSData alloc] initWithBytes:packet.buf->data length:packet.buf->size];
            [soundData appendData: frameData];
        }
    }

    // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory
    av_free_packet(&packet);
}

// first try to write it to a file, see if that works
// this is indeed writing bytes, but it is unplayable
[soundData writeToFile:@"output.wav" atomically:YES];

NSError *error;

// this is my final goal, playing it with the AVAudioPlayer, but this is giving unclear errors
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:soundData error:&error];

if(player == nil) {
    NSLog(error.description); // Domain=NSOSStatusErrorDomain Code=1954115647 "(null)"
} else {
    [player prepareToPlay];
    [player play]; 
}

// Some codecs will cause frames to be buffered up in the decoding process. If the CODEC_CAP_DELAY flag
// is set, there can be buffered up frames that need to be flushed, so we'll do that
if (codecContext->codec->capabilities & CODEC_CAP_DELAY)
{
    av_init_packet(&packet);
    // Decode all the remaining frames in the buffer, until the end is reached
    int frameFinished = 0;
    while (avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet) >= 0 && frameFinished)
    {
    }
}

av_free(frame);
avcodec_close(codecContext);

avformat_close_input(&formatContext);

尚未真正找到解決此特定問題的解決方案,但最終改用ap4y / OrigamiEngine 我想使用FFmpeg的主要原因是要在iOS和tvOS上播放不受支持的音頻文件(FLAC / OGG),而OrigamiEngine可以正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM