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 ,点击准备按钮,即可在控制台中看到解码的数据

示例
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日

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容