[Cmake-Android音视频]ffmpeg3.4软硬解码和多线程解码

1.解码流程图2.函数介绍avcodec_register_all()注册解码器格式,比如h264,mjpeg。avcodec_find_decoder(...)通过解码器ID查找相应的解码器。如果没有找到,应该是在编译ffmpeg的时候没有打开相应的解码器。此时,需要在ffmpeg的configure文件中打开,并重新编译出库文件。avcodec_find_decoder_by_name(......

[Cmake-Android音视频]ffmpeg3.4软硬解码和多线程解码
1.解码流程图

�2.函数介绍

avcodec_register_all()

注册解码器格式,比如h264,mjpeg。

avcodec_find_decoder(...)

通过解码器ID查找相应的解码器。如果没有找到,应该是在编译ffmpeg的时候没有打开相应的解码器。此时,需要在ffmpeg的configure文件中打开,并重新编译出库文件。

avcodec_find_decoder_by_name(...)

通过名字来查找解码器。

//硬解码 avcodec_find_decoder_by_name("h264_mediacodec");

avcodec_open2(...)

打开解码器

//可以通过options来设置多线程解码//所有可设置的参数在/libavcodec/options_table.h//int thread_count解码线程数//time_base 时间基数int avcodec_open2(AVCodecContext *avctx, const AVCodec*codec, AVDictionary **options)

avcodec_send_packet(...)

发送到线程中解码,packet会被复制一份,我们可以直接清理packet。

该函数会有缓存,发送到文件结尾后,可以通过发送NULL,将缓存数据取出。

avcodec_receive_frame(...)

从解码成功的数据中取出一个 frame, 解码的时候前几帧的可能获取失败,因为avcodec_send_packet(...)会有缓存, 导致播放的时候视频最后的几帧没有播放。

解决方案: 读到文件结尾处的时候,avcodec_send_packet()中的avpkt传NULL,然后在avcodec_receive_frame()一直读,,直到取不出帧为止。

接收已经解码好的数据。接收的数据和发送的数据并不一定是一一对应的。

3.关键结构体介绍

AVCodecContext

描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息。

//空间申请AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) //释放void avcodec_free_context(AVCodecContext **avctx); //把avstream中的参数复制到codec中avcodec_parameters_to_context(codec, p); //关键参数enum AVMediaType codec_type:编解码器的类型(视频,音频...)struct AVCodec  *codec:采用的解码器AVCodec(H.264,MPEG2...)int bit_rate:平均比特率uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)int width, height:如果是视频的话,代表宽和高int sample_rate:采样率(音频)int channels:声道数(音频)enum AVSampleFormat sample_fmt:采样格式

AVFrame

用于存放解码后的数据,对视频来说是YUV,RGB,对音频来说是PCM

//分配内存空间AVFrame *frame = av_frame_alloc() //释放内存空间void av_frame_free(AVFrame **frame) //关键参数//解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)uint8_t *data[AV_NUM_DATA_POINTERS];//视频一行的数据大小 音频一个通道数据大小 存放的目的主要是为了按字节对齐int linesize[AV_NUM_DATA_POINTERS];//视频宽高int width, height;//音频单通道样本数 待定int nb_samples;//收到的当前帧的ptsint64_t pts;//packet中的dtsint64_t pkt_dts;//音频采样率  通道类型通道数int sample_rate;uint64_t channel_layout;int channels;//视频---图片格式  音频---采样格式int format; //AVPixelFormat AVSampleFormat
�4.关键代码

多线程neon软解码关键代码� neon是在编译fffmpeg的时候配置的

//初始化解码器avcodec_register_all();//软解码器AVCodec *codec = avcodec_find_decoder(vStream->codecpar->codec_id);if (!codec){ LOGI("avcodec_find_decoder failed"); return env->NewStringUTF(hello.c_str());}//初始化解码器上下文AVCodecContext *cc = avcodec_alloc_context3(codec);avcodec_parameters_to_context(cc, vStream->codecpar);cc->thread_count = 8; //解码线程数//打开解码器 re = avcodec_open2(cc, 0, 0); if (re != 0) {  LOGI("avcodec_open2 failed! %s", av_err2str(re));  return env->NewStringUTF(hello.c_str()); }LOGI("avcodec_open2 success");//读取帧数据AVPacket *pkt = av_packet_alloc();AVFrame *frame = av_frame_alloc();for (; ; ){ int re = av_read_frame(ic, pkt); if (re != 0) {  LOGI("读到结尾处了");  //传递NULL  保证缓存数据也能被取出  avcodec_send_packet(cc, NULL);  //int pos = 20 * r2d(vStream->time_base);  //av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);  continue; } //只测试视频 if (pkt->stream_index != videoStream) {  continue; } //发送到线程中解码 re = avcodec_send_packet(cc, pkt); //清理 av_packet_unref(pkt); if (re != 0) {  LOGI("avcodec_send_packet FAILED");  continue; } //保证能接受到所有的数据 for (;;) {  re = avcodec_receive_frame(cc, frame);  if (re != 0)  {// LOGI("avcodec_receive_frame FAILED");break;  }  LOGI("avcodec_receive_frame %lld, nb_samples = %d", frame->pts, frame->nb_samples); }}

调用MedieCodec硬解码关键代码

//添加头文件#include <libavcodec/jni.h>//java加载的时候自动调用extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *res){ //java虚拟机环境传递给ffmpeg av_jni_set_java_vm(vm, 0); return JNI_VERSION_1_4;}//硬解码AVCodec *codec = avcodec_find_decoder_by_name("h264_mediacodec");

源文地址:https://www.guoxiongfei.cn/csdn/4886.html