FFmpeg C++多线程解码音频数据

1. C++多线程解码音频数据

之前总结过一篇 FFMpeg 解码流程的博客FFMpeg 解码流程

接下来,对照着上面的流程,使用代码来实现 FFmpeg 的解码流程。在文末有源码下载。

1.1 解码流程

1.1.1 开启线程

  • 调用 prepared() 方法,开启线程。
  • 在 callbackDecode 中执行 decodeFFmpegThread 方法。
extern "C"
JNIEXPORT void JNICALL
Java_com_example_audioplayer_player_AudioPlayer__1prepare(JNIEnv *env, jobject instance,
                                                          jstring source_) {
    const char *source = env->GetStringUTFChars(source_, 0);

    if (ffmpeg == NULL) {
        if (callJava == NULL) {
            callJava = new CallJava(env, jvm, &instance);
        }
        //自己定义的一个类,用于解码音频数据
        ffmpeg = new FFmpeg(callJava, source);
        //1.调用准备方法
        ffmpeg->prepare();
    }
}

//2.准备方法
void FFmpeg::prepare() {
    pthread_create(&decodeThread, NULL, callbackDecode, this);
}
//构造方法
FFmpeg::FFmpeg(CallJava *callJava, const char *url) {
    this->callJava = callJava;
    this->url = url;
}
//3.线程执行体
void *callbackDecode(void *data) {
    FFmpeg *ffmpeg = (FFmpeg *) data;

    ffmpeg->decodeFFmpegThread();

    pthread_exit(&ffmpeg->decodeThread);
}

接下来,解码流程会在 decodeFFmpegThread 方法中执行。

1.1.2 准备阶段

下面是 decodeFFmpegThread 方法的内容:

  • 注册
//注册
av_register_all();
avformat_network_init();
  • 打开文件或网络流
avFormatContext = avformat_alloc_context();
if (avformat_open_input(&avFormatContext, url, NULL, NULL) != 0) {
    LOGE("avformat_open_input failed...");
    return;
}
  • 获取流信息
if (avformat_find_stream_info(avFormatContext, NULL) < 0) {
    LOGE("avformat_find_stream_info failed...");
    return;
}
  • 获取音频流

这里只解码音频,因此只需要找到 codec_typeAVMEDIA_TYPE_AUDIO 流信息即可。

for (int i = 0; i < avFormatContext->nb_streams; i++) {
    //找到对应的音频流信息
    if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
        if (audioInfo == NULL) {
            //创建 AudioInfo 保存音频相关信息
            audioInfo = new AudioInfo();
            audioInfo->streamIndex = i;
            audioInfo->avCodecParameters = avFormatContext->streams[i]->codecpar;
            break;
        }
    }
}
  • 根据 AVCodecID 获取解码器
const AVCodec *avCodec = avcodec_find_decoder(audioInfo->avCodecParameters->codec_id);
if (!avCodec) {
    LOGE("avcodec_find_decoder failed...");
    return;
}
  • 利用解码器创建解码器上下文
audioInfo->avCodecContext = avcodec_alloc_context3(avCodec);
if (!audioInfo->avCodecContext) {
    LOGE("avcodec_alloc_context3 failed...");
    return;
}
if (avcodec_parameters_to_context(audioInfo->avCodecContext, audioInfo->avCodecParameters) <
    0) {
    LOGE("avcodec_parameters_to_context failed...");
    return;
}
  • 打开解码器

至此,打开解码器之后,音频准备工作已经完成,接下来就可以解析每一个 AvPacket 数据了

if (avcodec_open2(audioInfo->avCodecContext, avCodec, 0) != 0) {
    LOGE("avcodec_open2 failed...");
    return;
}

1.1.3 解码 AvPacket 阶段

解码 AvPacket 阶段就是解码每一帧音频数据,AvPacket 存放了每一帧的音频数据。

AVPacket *avPacket = av_packet_alloc();
av_read_frame(avFormatContext, avPacket)

下面这个写一个 start() 函数,负责解码音频数据。

void FFmpeg::start() {
    //判断
    if (audioInfo == NULL) {
        LOGE("start failed audio info is null.")
        return;
    }

    int count = 0;
    //死循环判断
    while (1) {
        AVPacket *avPacket = av_packet_alloc();

        if (av_read_frame(avFormatContext, avPacket) == 0) {

            if (avPacket->stream_index == audioInfo->streamIndex) {
                count++;
                LOGD("当前解码第%d帧", count);
                av_packet_free(&avPacket);
                av_free(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
            }
        } else {
            LOGD("解码完成,总共解码%d帧", count);
            av_packet_free(&avPacket);
            av_free(avPacket);
            break;
        }
    }
}

1.2 源码

下载源码,运行Demo ,点击准备按钮,即可在控制台中看到解码的数据

FFmpeg C++多线程解码音频数据_第1张图片
示例
11-25 22:45:52.752 27636-27868/example.com.jniexample I/MainActivity: onPrepared
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第1帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第2帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第3帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第4帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第5帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第6帧
11-25 22:45:52.754 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第7帧
...
11-25 22:45:57.530 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第8403帧
11-25 22:45:57.530 27636-27870/example.com.jniexample D/liaoweijian: 当前解码第8404帧
11-25 22:45:57.530 27636-27870/example.com.jniexample D/liaoweijian: 解码完成,总共解码8404帧

记录于 2018年11月25日

你可能感兴趣的:(FFmpeg C++多线程解码音频数据)