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


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

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信