FFmpeg(三) 编解码相关函数理解
一、编解码基本流程
主要流程:
打开视频解码器(音频一样)
软解码、硬解码
进行编解码
下面先来看打开视频解码器
①avcodec_register_all()//初始化解码
②先找到解码器、
找解码器(软解码):AVCodec *codec = avcodec_find_decoder(stream.codecparcodec_id); 从AVStream中根据codec_id取出解码器
找解码器(硬解码):AVCodec *codec = avcodec_find_decoder_byname("h264_mediacodec "); 从通过名字获取解码器
③解码器上下文
AVCodecContext *cc = avcodec_alloc_context3(codec ); //参数为上面找到的解码器
④把AVStream中的参数复制到我们的AVCodecContext当中
avcodec_parameters_to_context(cc,stream );//stream为音频或者视频的流信息,cc为解码器的上下文
设置线程为1
codec->thread_count = 1
⑤打开解码器
int re = avcodec_open2(cc,0,0);//cc为解码器上下文,返回值 re != 0 则失败
软解码(发送,然后接受数据)
①AVFrame
空间分配 AVFrame *frame =av_frame_alloc() ; //分配空间并初始化
空间释放 void av_frame_free(AVFrame **frame) ;
void av_frame_unref(AVFrame *frame);
int av_frame_ref(AVFrame *dst , const AVFrame *src); // 释放对象本身空间
复制 AVframe *av_frame_clone(const AVFrame *src); //
②早期版本提供两个av_codec_video()和av_codec_audio()
新版本是这两个:avcodec_send_packet() 和avcodec_receive_frame()
解码已经放到后台,以前是单线程在内部等待解码。
现在是多线程:两个步骤 :a、发到发到解码队列;b、接收(接收的时候要接受多次)
//发送到线程解码
int re = avcodec_send_packet(AVCodecContext *acctx , const AVPacket *avpkt);
//清理
av_packet_unref(pkt);
//把packet写到解码队列
if(re != ) 失败 return
//开始接收数据,把解码成功的frame取出来
int re = avcodec_receive_frame( AVCodecContext *acctx ,AVFrame *frame);
if(re !=) 失败 return
FFmpeg调用MediaCodec实现硬解码
① 需要C++调用java ,在C++代码中实现一个函数,Java在执行的时候会自己调用这个函数,不用去调用。
jnit JNI_OnLoad(JavaVM *vm, void *res){
av_jni_set_java_vm(vm , );
return JNI_VERSION_1_4;
}
②找解码器(硬解码):AVCodec *codec = avcodec_find_decoder_byname("h264_mediacodec "); 从通过名字获取解码器
③然后开始解码和软解码一样
二、相关函数解析
①avcodec_register_all()//初始化解码
②avcodec_find_decoder(stream.codecparcodec_id);从AVStream中根据codec_id取出解码器
③avcodec_find_decoder_byname("h264_mediacodec "); 从通过名字获取解码器
④avcodec_alloc_context3(codec ); //,得到解码器上下文、参数为上面找到的解码器
⑤avcodec_parameters_to_context(cc,stream );// 把AVStream中的参数复制到我们的AVCodecContext当中, stream为音频或者视频的流信息,cc为解码器的上下文
⑥int re = avcodec_open2(cc,0,0);//cc为解码器上下文,返回值 re != 0 则失败
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,"testff",__VA_ARGS__) extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavcodec/jni.h>
}
#include<iostream>
using namespace std; static double r2d(AVRational r)
{
return r.num==||r.den == ? :(double)r.num/(double)r.den;
} //当前时间戳 clock
long long GetNowMs()
{
struct timeval tv;
gettimeofday(&tv,NULL);
int sec = tv.tv_sec%;
long long t = sec*+tv.tv_usec/;
return t;
}
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
av_jni_set_java_vm(vm,);
return JNI_VERSION_1_4;
} extern "C"
JNIEXPORT jstring
JNICALL
Java_aplay_testffmpeg_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++ ";
hello += avcodec_configuration();
//初始化解封装
av_register_all();
//初始化网络
avformat_network_init(); avcodec_register_all(); //打开文件
AVFormatContext *ic = NULL;
char path[] = "/sdcard/1080.mp4";
//char path[] = "/sdcard/video.flv";
int re = avformat_open_input(&ic,path,,);
if(re != )
{
LOGW("avformat_open_input failed!:%s",av_err2str(re));
return env->NewStringUTF(hello.c_str());
}
LOGW("avformat_open_input %s success!",path);
//获取流信息
re = avformat_find_stream_info(ic,);
if(re != )
{
LOGW("avformat_find_stream_info failed!");
}
LOGW("duration = %lld nb_streams = %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)
{
LOGW("视频数据");
videoStream = i;
fps = r2d(as->avg_frame_rate); LOGW("fps = %d,width=%d height=%d codeid=%d pixformat=%d",fps,
as->codecpar->width,
as->codecpar->height,
as->codecpar->codec_id,
as->codecpar->format
);
}
else if(as->codecpar->codec_type ==AVMEDIA_TYPE_AUDIO )
{
LOGW("音频数据");
audioStream = i;
LOGW("sample_rate=%d channels=%d sample_format=%d",
as->codecpar->sample_rate,
as->codecpar->channels,
as->codecpar->format
);
}
}
//ic->streams[videoStream];
//获取音频流信息
audioStream = av_find_best_stream(ic,AVMEDIA_TYPE_AUDIO,-,-,NULL,);
LOGW("av_find_best_stream audioStream = %d",audioStream);
//////////////////////////////////////////////////////////
//打开视频解码器
//软解码器
AVCodec *codec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
//硬解码
codec = avcodec_find_decoder_by_name("h264_mediacodec");
if(!codec)
{
LOGW("avcodec_find failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化
AVCodecContext *vc = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(vc,ic->streams[videoStream]->codecpar); vc->thread_count = ;
//打开解码器
re = avcodec_open2(vc,,);
//vc->time_base = ic->streams[videoStream]->time_base;
LOGW("vc timebase = %d/ %d",vc->time_base.num,vc->time_base.den);
if(re != )
{
LOGW("avcodec_open2 video failed!");
return env->NewStringUTF(hello.c_str());
} //////////////////////////////////////////////////////////
//打开音频解码器
//软解码器
AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
//硬解码
//codec = avcodec_find_decoder_by_name("h264_mediacodec");
if(!acodec)
{
LOGW("avcodec_find failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化
AVCodecContext *ac = avcodec_alloc_context3(acodec);
avcodec_parameters_to_context(ac,ic->streams[audioStream]->codecpar);
ac->thread_count = ;
//打开解码器
re = avcodec_open2(ac,,);
if(re != )
{
LOGW("avcodec_open2 audio failed!");
return env->NewStringUTF(hello.c_str());
}
//读取帧数据
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
long long start = GetNowMs();
int frameCount = ;
for(;;)
{
//超过三秒
if(GetNowMs() - start >= )
{
LOGW("now decode fps is %d",frameCount/);
start = GetNowMs();
frameCount = ;
} int re = av_read_frame(ic,pkt);
if(re != )
{ LOGW("读取到结尾处!");
int pos = * r2d(ic->streams[videoStream]->time_base);
av_seek_frame(ic,videoStream,pos,AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME );
continue;
}
//只测试视频
/*if(pkt->stream_index !=videoStream)
{
continue;
}*/
//LOGW("stream = %d size =%d pts=%lld flag=%d",
// pkt->stream_index,pkt->size,pkt->pts,pkt->flags
//); AVCodecContext *cc = vc;
if(pkt->stream_index == audioStream)
cc=ac; //发送到线程中解码
re = avcodec_send_packet(cc,pkt);
//清理
int p = pkt->pts;
av_packet_unref(pkt); if(re != )
{
LOGW("avcodec_send_packet failed!");
continue;
}
for(;;)
{
re = avcodec_receive_frame(cc,frame);
if(re !=)
{
//LOGW("avcodec_receive_frame failed!");
break;
}
//LOGW("avcodec_receive_frame %lld",frame->pts);
//如果是视频帧
if(cc == vc)
{
frameCount++;
}
}
//////////////////////
}
//关闭上下文
avformat_close_input(&ic);
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_aplay_testffmpeg_MainActivity_Open(JNIEnv *env, jobject instance, jstring url_,
jobject handle) {
const char *url = env->GetStringUTFChars(url_, ); // TODO
FILE *fp = fopen(url,"rb");
if(!fp)
{
LOGW("File %s open failed!",url);
}
else
{
LOGW("File %s open succes!",url);
fclose(fp);
}
env->ReleaseStringUTFChars(url_, url);
return true;
}
FFmpeg(三) 编解码相关函数理解的更多相关文章
- ffmpeg H264 编解码配置
ffmpeg H264编解码前面有文章介绍下,本文主要介绍一些参数配置. 编码: int InitEncoderCodec( int iWidth, int iHeight) { AVCodec * ...
- ffmpeg:编解码过程,基本用法
1 术语: 什么是影片?其实就是一组(很多张)图片,时间间隔很小的连续展示出来,人们就觉得画面中的人物在动,这就是影片.那电影的实质就是N多张图片的集合.那 每张图片和帧又有什么关系呢?事实上,如果 ...
- 视频编解码的理论和实践2:Ffmpeg视频编解码
近几年,视频编解码技术在理论及应用方面都取得了重大的进展,越来越多的人想要了解编解码技术.因此,网易云信研发工程师为大家进行了归纳梳理,从理论及实践两个方面简单介绍视频编解码技术. 相关阅读推荐 &l ...
- FFmpeg(二) 解封装相关函数理解
一.解封装基本流程 ①av_register_All()////初始化解封装,注册解析和封装的格式. ②avformat_netword_init()//初始化网络,解析rtsp协议 ③avforma ...
- [转载] 问题解决:FFmpeg视频编解码库,无法解析的外部信号
在编译FFmpeg相关项目时,可能会出现: error LNK2019: 无法解析的外部符号 "int __cdecl avpicture_fill(struct AVPicture *,u ...
- 问题解决:FFmpeg视频编解码库,无法解析的外部信号
在编译FFmpeg相关项目时.可能会出现: error LNK2019: 无法解析的外部符号 "int __cdecl avpicture_fill(struct AVPicture *,u ...
- FFmpeg(四) 像素转换相关函数理解
一.基本流程 1.sws_getCachedContext();//得到像素转换的上下文 2.sws_scale()://进行转换 二.函数说明 1.SwsContext *vctx = NULL; ...
- FFmpeg音视频编解码实践总结
PS:由于目前开发RTSP服务器传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需 ...
- 【GPU编解码】GPU硬解码---DXVA
前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...
随机推荐
- 牛客OI测试赛 C 序列 思维
链接:https://www.nowcoder.com/acm/contest/181/C来源:牛客网 题目描述 小a有n个数,他想把他们划分为连续的权值相等的k段,但他不知道这是否可行. 每个数都必 ...
- HDU-1532 网络流裸题
HDU-1532 题意简单的来说就是从1点到n点,最大的流量是多少. 代码: #include<bits/stdc++.h> using namespace std; #define Fo ...
- codeforces 762 D. Maximum path(dp)
题目链接:http://codeforces.com/problemset/problem/762/D 题意:给出一个3*n的矩阵然后问从左上角到右下角最大权值是多少,而且每一个点可以走上下左右,但是 ...
- CF994B Knights of a Polygonal Table 第一道 贪心 set/multiset的用法
Knights of a Polygonal Table time limit per test 1 second memory limit per test 256 megabytes input ...
- ie表单提交提示下载文件
使用jquery的ajaxform提交form表单 如果在html中多了 enctype ="multipart/form-data" 属性值 提交时就会在ie8中提示下载 ...
- 每天学会一点点(JAVA基础)
1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? 虚拟机是一个可以执行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机执行的字节码文件. Java被设计成允 ...
- Docker竟然还能这么玩?商业级4G代理搭建实战!
时间过得真快,距离这个系列的上一篇文章<商业级4G代理搭建指南[准备篇]>发布的时间已经过了两个星期了,上个星期由于各种琐事缠身,周二开始就没空写文章了,所以就咕咕咕了. 那么在准备篇中, ...
- SQL查询出距当前时间最近的一条或多条记录。
select * from bas_dredge,(SELECT C_ENTERPRISEID,MAX(D_UTIME) D_LTIME FROM BAS_DREDGE GROUP BY C_ENTE ...
- Protostuff序列化问题
最近在开发中遇到一个Protostuff序列化问题,在这记录一下问题的根源:分析一下Protostuff序列化和反序列化原理:以及怎么样避免改bug. 1. 问题描述 有一个push业务用到了mq,m ...
- Redis集群增加节点和删除节点
本文主要是承接上一篇文章Redis集群的离线安装成功以后,我们如何进行给集群增加新的主从节点(集群扩容)以及如何从集群中删除节点(集群缩容),也就是集群的伸缩,集群伸缩的原理是控制虚拟槽和数据在节点之 ...