一.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. centos7 在docker swarm中运行Jenkins,利用gitlab的webhook触发自动部署脚本

    1.宿主机中创建目录 mkdir -p /jenkins_home 2.编辑compose文件,文件名jenkins.yml version: '3.4' services: jenkins-upgr ...

  2. 转:场景管理--BSP

    对于一个3D引擎来说,最核心的部分应该算是场景组织(scene graph)了,如果这部分你都没有设计好, 那么就别指望开发一个成熟的3D引擎了.为了开发3d引擎,所以我首先就研究这方面的内容,对一个 ...

  3. 图标资源: http://www.easyicon.net/ 可以按关键词搜索

      搜索图标    

  4. 没有找到AdbWinApi.dll

    今天调试adb命令时遇到一些问题 1. 没有找到AdbWinApi.dll 2. adb server is out of date.  killing... ADB server didn't AC ...

  5. How to use, monitor, and disable transparent hugepages in Red Hat Enterprise Linux 6

    Resolution Note: Transparent Huge Pages are not available on the 32-bit version of RHEL 6. Transpare ...

  6. 测试cnblogs的代码折叠展开功能和zoom.js实现图片放大缩小冲突的问题

    #!/usr/bin/env python # -*- coding:utf- -*- def test(): print('from the test'

  7. 查看linux设备文件系统类型的方法

    网络上找来找去没有找到简单的,最后翻了鸟哥的书就找到了,鸟哥的书还是真的有用心写的. /proc/filesystems 当前被内核支持的文件系统类型列表文件 /etc/filesystems 系统已 ...

  8. linux达人养成计划学习笔记(一)——命令基本格式及文件处理命令

    一.shell终端初始 快捷键Ctrl + Alt + T,可以快速打开终端 bluemoutain 当前用户名 bluemoutain-CN155 本机名 ~ 当前目录(家目录,root用户为/ro ...

  9. 【Visual Studio】Visual C# 中XML注释的使用(含注释在开发时显示换行)

    为函数方法注释说明要用到 xml 语句 <summary> 段落说明 </summary> .<para> 新段示例说明 </para>.<par ...

  10. 【MySQL】MySQL之MySQL常用的函数方法

    MySQL常用函数 本篇主要总结了一些在使用MySQL数据库中常用的函数,本篇大部分都是以实例作为讲解,如果有什么建议或者意见欢迎前来打扰. limit Select * from table ord ...