之前的视频解码仍然存在问题,那就是是在主线程中去完成解码的,会造成线程阻塞,这里将其改为多线程解码,使其主线程不被阻塞

前面介绍了音视频的主线程解码,那样会阻塞主线程,在前面学习了多线程以后,就可以对音频和视频分离开来在子线程里解析了,但这样存在音视频同步的问题了,这里贴出代码,只是提供一种思路,其运行存在大量问题,还需要慢慢解决。例如,退出发生异常,音视频不同步

#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <android/native_window.h>
#include <android/native_window_jni.h> #include "com_cj5785_ffmpegvideothread_VideoPlayer.h" #include "include/ffmpeg/libavformat/avformat.h"
#include "include/ffmpeg/libavcodec/avcodec.h"
#include "include/ffmpeg/libswscale/swscale.h"
#include "include/ffmpeg/libswresample/swresample.h"
#include "include/libyuv/libyuv.h" #define LOGI(FORMAT,...) __android_log_print(4,"cj5785",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(6,"cj5785",FORMAT,##__VA_ARGS__); #define MAX_STREAM 2
#define VIDEO_STREAM_TYPE 0
#define AUDIO_STREAM_TYPE 1
#define MAX_AUDIO_FRME_SIZE 48000 * 4 typedef struct _Player
{
JavaVM *javaVM;
AVFormatContext *input_format_ctx;
int video_stream_index;
int audio_stream_index;
AVCodecContext *input_codec_ctx[MAX_STREAM];
pthread_t decode_threads[MAX_STREAM];
ANativeWindow* nativeWindow;
SwrContext *swr_ctx;
enum AVSampleFormat in_sample_fmt;
enum AVSampleFormat out_sample_fmt;
int in_sample_rate;
int out_sample_rate;
int out_channel_nb;
jobject audio_track;
jmethodID audio_track_write_mid;
}Player; void init_input_format_ctx(Player *player, const char *input)
{
//注册组件
av_register_all(); AVFormatContext *format_ctx = avformat_alloc_context();
//打开视频文件
if(avformat_open_input(&format_ctx, input, NULL, NULL) != 0)
{
LOGE("%s", "打开文件失败!");
return;
} //获取视频相关信息
if(avformat_find_stream_info(format_ctx, NULL) < 0)
{
LOGE("%s", "获取视频信息失败!");
return;
} //判断输入流的类型
int i = 0;
for (i = 0; i < format_ctx->nb_streams; i++)
{
if(format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
player->video_stream_index = i;
}
else if(format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
player->audio_stream_index = i;
}
} player->input_format_ctx = format_ctx; } void init_codec_context(Player *player,int stream_idx){
AVFormatContext *format_ctx = player->input_format_ctx;
//获取解码器
AVCodecContext *codec_ctx = format_ctx->streams[stream_idx]->codec;
AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
if(codec == NULL){
LOGE("%s","无法解码");
return;
}
//打开解码器
if(avcodec_open2(codec_ctx, codec, NULL) < 0){
LOGE("%s","解码器无法打开");
return;
}
player->input_codec_ctx[stream_idx] = codec_ctx;
} void decode_video_prepare(JNIEnv *env,Player *player,jobject surface){
player->nativeWindow = ANativeWindow_fromSurface(env, surface);
} void decode_video(Player *player,AVPacket *packet)
{
//像素数据(解码数据)
AVFrame *yuv_frame = av_frame_alloc();
AVFrame *rgb_frame = av_frame_alloc();
//绘制时的缓冲区
ANativeWindow_Buffer outBuffer;
AVCodecContext *codec_ctx = player->input_codec_ctx[player->video_stream_index];
int got_frame;
//解码AVPacket->AVFrame
avcodec_decode_video2(codec_ctx, yuv_frame, &got_frame, packet);
//Zero if no frame could be decompressed
//非零,正在解码
if(got_frame){
//lock
//设置缓冲区的属性(宽、高、像素格式)
ANativeWindow_setBuffersGeometry(player->nativeWindow, codec_ctx->width, codec_ctx->height,WINDOW_FORMAT_RGBA_8888);
ANativeWindow_lock(player->nativeWindow,&outBuffer,NULL); //设置rgb_frame的属性(像素格式、宽高)和缓冲区
//rgb_frame缓冲区与outBuffer.bits是同一块内存
avpicture_fill((AVPicture *)rgb_frame, outBuffer.bits, AV_PIX_FMT_RGBA, codec_ctx->width, codec_ctx->height); //YUV->RGBA_8888
I420ToARGB(yuv_frame->data[0],yuv_frame->linesize[0],
yuv_frame->data[2],yuv_frame->linesize[2],
yuv_frame->data[1],yuv_frame->linesize[1],
rgb_frame->data[0], rgb_frame->linesize[0],
codec_ctx->width,codec_ctx->height); //unlock
ANativeWindow_unlockAndPost(player->nativeWindow); usleep(1000 * 16);
} av_frame_free(&yuv_frame);
av_frame_free(&rgb_frame);
} void decode_audio_prepare(Player *player){
SwrContext *swr_ctx = swr_alloc();
AVCodecContext *codec_ctx = player->input_codec_ctx[player->audio_stream_index];
enum AVSampleFormat in_sample_fmt = codec_ctx->sample_fmt;
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
int in_sample_rate = codec_ctx->sample_rate;
int out_sample_rate = in_sample_rate;
uint64_t in_ch_layout = codec_ctx->channel_layout;
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
swr_alloc_set_opts(swr_ctx,
out_ch_layout, out_sample_fmt, out_sample_rate,
in_ch_layout, in_sample_fmt, in_sample_rate,
0, NULL);
swr_init(swr_ctx);
int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout); player->in_sample_fmt = in_sample_fmt;
player->out_sample_fmt = out_sample_fmt;
player->in_sample_rate = in_sample_rate;
player->out_sample_rate = out_sample_rate;
player->out_channel_nb = out_channel_nb;
player->swr_ctx = swr_ctx;
} void jni_audio_prepare(JNIEnv *env, jobject jobj, Player *player)
{
jclass player_class = (*env)->GetObjectClass(env, jobj);
jmethodID create_audio_track_mid = (*env)->GetMethodID(env, player_class, "createAudioTrack", "(II)Landroid/media/AudioTrack;");
jobject audio_track = (*env)->CallObjectMethod(env, jobj, create_audio_track_mid, player->out_sample_rate, player->out_channel_nb);
jclass audio_track_class = (*env)->GetObjectClass(env, audio_track);
jmethodID audio_track_play_mid = (*env)->GetMethodID(env, audio_track_class, "play", "()V");
(*env)->CallVoidMethod(env, audio_track, audio_track_play_mid);
jmethodID audio_track_write_mid = (*env)->GetMethodID(env, audio_track_class, "write", "([BII)I"); player->audio_track = (*env)->NewGlobalRef(env, audio_track);
player->audio_track_write_mid = audio_track_write_mid;
} void decode_audio(Player *player, AVPacket *packet)
{
JNIEnv *env = NULL;
JavaVM *javaVM = player->javaVM;
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRME_SIZE);
AVCodecContext *codec_ctx = player->input_codec_ctx[player->audio_stream_index];
AVFrame *frame = av_frame_alloc();
int got_frame = 0;
avcodec_decode_audio4(codec_ctx, frame, &got_frame, packet);
if(got_frame)
{
swr_convert(player->swr_ctx, &out_buffer, MAX_AUDIO_FRME_SIZE,
(const uint8_t **)frame->data, frame->nb_samples);
int out_buffer_size = av_samples_get_buffer_size(NULL, player->out_channel_nb,
frame->nb_samples ,player->out_sample_fmt, 1);
jbyteArray audio_sample_array = (*env)->NewByteArray(env, out_buffer_size);
jbyte *sample_byte = (*env)->GetByteArrayElements(env, audio_sample_array, NULL);
memcpy(sample_byte, out_buffer, out_buffer_size);
(*env)->ReleaseByteArrayElements(env, audio_sample_array, sample_byte, 0);
(*env)->CallIntMethod(env, player->audio_track, player->audio_track_write_mid,
audio_sample_array, 0, out_buffer_size);
(*env)->DeleteLocalRef(env,audio_sample_array);
(*javaVM)->DetachCurrentThread(javaVM);
usleep(16 * 1000);
}
av_frame_free(&frame);
} void* decode_data(void* arg){
Player *player = (struct Player*)arg;
AVFormatContext *format_ctx = player->input_format_ctx;
//编码数据
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//6.一阵一阵读取压缩的视频数据AVPacket
int video_frame_count = 0;
while(av_read_frame(format_ctx,packet) >= 0){
if(packet->stream_index == player->video_stream_index)
{
//decode_video(player,packet);
}else if(packet->stream_index == player->audio_stream_index)
{
decode_audio(player,packet);
}
av_free_packet(packet);
}
} JNIEXPORT void JNICALL Java_com_cj5785_ffmpegvideothread_VideoPlayer_play
(JNIEnv *env, jobject jobj, jstring jstr_path, jobject obj_surface)
{
LOGE("%s", "开始"); const char *input_cstr = (*env)->GetStringUTFChars(env, jstr_path, NULL);
Player *player = (Player *)calloc(sizeof(Player), 1); (*env)->GetJavaVM(env,&(player->javaVM)); //初始化封装格式上下文
init_input_format_ctx(player,input_cstr);
int video_stream_index = player->video_stream_index;
int audio_stream_index = player->audio_stream_index;
//获取音视频解码器,并打开
init_codec_context(player,video_stream_index);
init_codec_context(player,audio_stream_index); decode_video_prepare(env, player, obj_surface);
decode_audio_prepare(player);
jni_audio_prepare(env,jobj,player); //创建子线程解码
pthread_create(&(player->decode_threads[video_stream_index]),NULL,decode_data,(void*)player);
pthread_create(&(player->decode_threads[audio_stream_index]),NULL,decode_data,(void*)player); /*ANativeWindow_release(nativeWindow);
av_frame_free(&yuv_frame);
avcodec_close(pCodeCtx);
avformat_free_context(pFormatCtx); (*env)->ReleaseStringUTFChars(env,input_jstr,input_cstr); free(player);*/ }

ffmpeg学习笔记-多线程音视频解码的更多相关文章

  1. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  2. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  3. ffmpeg学习笔记-初识ffmpeg

    ffmpeg用来对音视频进行处理,那么在使用ffmpeg前就需要ffmpeg有一个大概的了解,这里使用雷神的ppt素材进行整理,以便于复习 音视频基础知识 视频播放器的原理 播放视频的流程大致如下: ...

  4. ffmpeg学习笔记

           对于每一个刚開始学习的人,刚開始接触ffmpeg时,想必会有三个问题最为关心,即ffmpeg是什么?能干什么?怎么開始学习?本人前段时间開始接触ffmpeg,在刚開始学习过程中.这三个问 ...

  5. tensorflow学习笔记——多线程输入数据处理框架

    之前我们学习使用TensorFlow对图像数据进行预处理的方法.虽然使用这些图像数据预处理的方法可以减少无关因素对图像识别模型效果的影响,但这些复杂的预处理过程也会减慢整个训练过程.为了避免图像预处理 ...

  6. C++学习笔记——多线程(1)

    目前在做推理引擎开发相关的工作,这块内容的话,对工程能力的要求还是比较高的,不再像以前只是写一些Python脚本训训模型就可以了,而且深入了解C++之后,也能感受到Python较C++暴露出的缺点,另 ...

  7. ffmpeg学习笔记-音频解码

    在之前的文章已经初步对视频解码有个初步的认识了,接下来来看一看音频解码 音频解码步骤 音频解码与视频解码一样,有者固有的步骤,只要按照步骤来,就能顺利的解码音频 以上是ffmpeg的解码流程图,可以看 ...

  8. python学习笔记- 多线程(1)

    学习多线程首先先要理解线程和进程的关系. 进程 计算机的程序是储存在磁盘中的可执行的二进制文件,执行时把这些二进制文件加载到内存中,操作系统调用并交给处理器执行对应操作,进程是程序的一次执行过程,这是 ...

  9. java学习笔记 --- 多线程(多线程的创建方式)

    1.创建多线程方式1——继承Thread类. 步骤:  A:自定义类MyThread继承Thread类.  B:MyThread类里面重写run()? 为什么是run()方法呢? C:创建对象 D:启 ...

随机推荐

  1. python大战EXCEL--xlwings

    xlwings的特色 xlwings能够非常方便的读写Excel文件中的数据,并且能够进行单元格格式的修改 可以和matplotlib以及pandas无缝连接 可以调用Excel文件中VBA写好的程序 ...

  2. 洛谷P1273 有线电视网【树形dp】

    题目:https://www.luogu.org/problemnew/show/P1273 题意:一棵树,叶子节点是用户,每天边有一个权值表示花费,每一个用户有一个值表示他们会交的钱. 问在不亏本的 ...

  3. 如何预测 Pinterest 和 Instagram 的未来发展潜力?

    作者:陈琪链接:https://www.zhihu.com/question/20169268/answer/14229241来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...

  4. CF796D Police Stations BFS+染色

    题意:给定一棵树,树上有一些点是警察局,要求所有点到最近的警察局的距离不大于 $d$,求最多能删几条边 ? 题解: 考虑什么时候一条边可以被断开:这条边的两个端点被两个不同的警察局覆盖掉. 我们要设计 ...

  5. js 弹层下面的body禁止滚动

    弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互.但是,在蒙层元素中滑动的时候,滑到内容的尽头时,再继续滑动,蒙层底部的页面会开始滚动,显然这不是我们想 ...

  6. 2019 Multi-University Training Contest 10

    目录 Contest Info Solutions C - Valentine's Day D - Play Games with Rounddog E - Welcome Party G - Clo ...

  7. 7.27T2

    不可做题 sol:首先有个很显然的性质就是答案一定是在叶子上最优,然后画画图发现就是从最底层看,如果一条链就看做一个点,向上的第一颗非链的节点,它的儿子数-1就会对答案贡献,所有这样的累加起来就是答案 ...

  8. pojo、po、dto、dao、bo区别

    j2ee中,经常提到几种对象(object),理解他们的含义有助于我们更好的理解面向对象的设计思维.     POJO(plain old java object):普通的java对象,有别于特殊的j ...

  9. oracle insert into 多条数据

    mysql : insert into tablename (column1,column2) values ('aa','bb'), ('dd','cc'), ('ee','ff'); oracle ...

  10. 走进JavaWeb技术世界开篇:JavaWeb技术汇总

    微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频 ...