您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
7. ffmpeg调用MediaCodec实现硬解码代码演示~1
发布时间:2021-06-01 18:42:28编辑:雪饮阅读()
上篇中實現了視頻軟解碼性能的衡量測試。一直以來都還沒有實現過硬解碼。
那麽硬解碼不僅僅要把我們前面獲取硬解碼器的代碼放開注釋,由於硬解碼還涉及到在c中調用java,所以需要提供一個java環境變量。而JNI_OnLoad函數,java加載時候自動調用。
另外需要導入libavcodec/jni.h頭文件的支持。因爲設置java環境變量所用的方法av_jni_set_java_vm位於此頭文件。
那麽一個具體的實例:cpp/native-lib.cpp實現如:
#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>
}
static double r2d(AVRational r){
return r.num==0 | r.den==0 ? 0:(double) r.num/(double)r.den;
}
/*
* 封裝一個當前時間戳返回的函數,以100小時以内返回
* ms:一般用於表示毫秒的單位
* */
long long GetNowMs(){
struct timeval tv;
gettimeofday(&tv,NULL);
/*
gettimeofday是计算机函数,使用C语言编写程序需要获得当前精确时间(1970年1月1日到现在的时间),或者为执行计时,可以使用gettimeofday()函数。
該函數返回的時間數據不是實時的,是根據cpu跳數來定的
tv_sec 代表多少秒,所以這裏tv_sec 代表多少秒從1970年1月1日到現在的秒數,因爲本函數最後要返回的是毫秒,這裏防止數據過大,所以這裏只返回100小時内的秒數
*/
int sec=tv.tv_sec%360000;
//tv.tv_usec:微秒,1毫秒等於100微秒
long long t=sec*1000+tv.tv_usec/100;
return t;
}
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
//JNI_OnLoad函數,java加載時候自動調用,這裏用做給c中調用java時的環境變量支持
av_jni_set_java_vm(vm,0);
//這裏返回一個java版本,這裏假定使用1.4的java版本
return JNI_VERSION_1_4;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndk_1and_141_MainActivity_stringFromJNI(JNIEnv* env,jobject) {
std::string hello = "Hello from C++";
hello+=avcodec_configuration();
//初始化解封裝
av_register_all();
//初始化網絡
avformat_network_init();
/*
* avcodec_register_all()只有调用了该函数,才能使用编解码器等。
* */
avcodec_register_all();
AVFormatContext *ic=NULL;
//要取一些信息,mp4格式取的比較多
char path[]="/sdcard/1080.mp4";
int re=avformat_open_input(&ic,path,0,0);
if(re!=0){
LOGW("avformat_open_input failed!:%s",av_err2str(re));
return env->NewStringUTF(hello.c_str());
}
/*
這裏需要漲點教訓,原來這裏是LOGW("avformat_open_input %s success!");
也就是多帶了一個%s,很明顯這裏語法在c中是有問題的,可是編譯過程時候沒有報錯。
日志貓中報錯信息讓我一度懷疑自己的arm64位動態鏈接庫編譯錯了呢。。。
*/
LOGW("avformat_open_input success!");
/*
duration方法:
mp4格式可以直接獲取到時長
返回類型是int64_t:
int64_t 是标准C ++类型,用于完全64位的有符号整数。 int64 不是标准类型。
第一个C ++标准没有固定宽度类型。在将 int64_t 添加到标准C ++之前,不同的编译器都实现了64位类型,但它们使用了自己的名称(例如 long long < / code>, __ int64 等。)
那麽因爲long long < / code>,所以這裏int64_t的輸出就是l ld了
*/
//對於flv來説,要先探測獲取下流信息,若不先探測,則獲取的場地可能就是如:-9223372036854775808一樣的負數
re=avformat_find_stream_info(ic,0);
if(re!=0){
LOGW("avformat_find_stream_info failed!");
}
//這裏格式化int64_t的時候千萬記住不是%lld而是%l ld
LOGW("duration=%l ld nb_streams=%d",ic->duration,ic->nb_streams);
int fps = 0;
int videoStream = 0;
int audioStream = 1;
/*
遍历获取AVStream音视频流信息并打印参数
*/
for(int i = 0; i < ic->nb_streams; i++)
{
AVStream *as = ic->streams[i];
if(as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
LOGW("视频数据");
videoStream = i;
//avg_frame_rate:幀率,一個分數,就是fps的分數表示法,分數表示可以有效保護精度損失
fps = r2d(as->avg_frame_rate);
/*
codecpar->width,codecpar->height:视频的宽高,只有视频有
codecpar->codec_id: 獲取解碼器的id號
codecpar->format:格式。对于视频来说指的就是像素格式(YUV420,YUV422...),对于音频来说,指的就是音频的采样格式。
*/
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;
/*
codecpar->sample_rate:樣本率
codecpar->channels:声道数
codecpar->format:格式。对于视频来说指的就是像素格式(YUV420,YUV422...),对于音频来说,指的就是音频的采样格式。
*/
LOGW("sample_rate=%d channels=%d sample_format=%d",
as->codecpar->sample_rate,
as->codecpar->channels,
as->codecpar->format
);
}
}
/*
獲取音頻流信息
第一個參數:AVStream的實例
第二個參數type:音頻或視頻流類型
第三個參數wanted_stream_nb是指定索引,這裏沒有必要,所以就為-1
第四個參數related_stream,同一節目組的相關流信息,這裏也沒有節目組,所以也就設置為-1
第五個參數decoder_ret 這個參數的作用我們要單獨做
第五個參數flags 目前還沒有用
*/
audioStream=av_find_best_stream(ic,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
LOGW("av_find_best_stream audioStream=%d",audioStream);
//软解码器-視頻解碼器
//avcodec_find_decoder:获取解码器
AVCodec *codec=avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
//硬解码,avcodec_find_decoder_by_name通过名字打开解码器,比如這裏h264_mediacodec就是用Android里面自带的解码模块
codec=avcodec_find_decoder_by_name("h264_mediacodec");
if(!codec){
LOGW("avcodec_find failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化,avcodec_alloc_context3方法用于获取编解码器上下文信息
AVCodecContext *vc=avcodec_alloc_context3(codec);
//avcodec_parameters_to_context()方法用于将流的参数(stream->codecpar)复制到解码器中,否则某些流可能无法正常解码。
avcodec_parameters_to_context(vc,ic->streams[videoStream]->codecpar);
//编解码时的线程数量,由用户设置,与CPU核心数有关,最佳效果一般设置为CPU核心数*2.
vc->thread_count=8;
//打开解码器
/*
* avcodec_open2()各个参数的含义:
avctx:需要初始化的AVCodecContext。
codec:输入的AVCodec
options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置
这里用不到codec和options,所以就填写0吧
* */
re=avcodec_open2(vc,0,0);
if(re!=0){
LOGW("avcodec_open2 video failed!");
return env->NewStringUTF(hello.c_str());
}
//软解码器-音頻解碼器
//avcodec_find_decoder:获取解码器
AVCodec *acodec=avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
//硬解码(先看软解码,硬解码另外再做)
//codec=avcodec_find_decoder_by_name("h264_mediacodec");
if(!acodec){
LOGW("avcodec_find_audio failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化,avcodec_alloc_context3方法用于获取编解码器上下文信息
AVCodecContext *ac=avcodec_alloc_context3(acodec);
//avcodec_parameters_to_context()方法用于将流的参数(stream->codecpar)复制到解码器中,否则某些流可能无法正常解码。
avcodec_parameters_to_context(ac,ic->streams[audioStream]->codecpar);
//编解码时的线程数量,由用户设置,与CPU核心数有关,最佳效果一般设置为CPU核心数*2.
ac->thread_count=1;
//打开解码器
/*
* avcodec_open2()各个参数的含义:
avctx:需要初始化的AVCodecContext。
codec:输入的AVCodec
options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置
这里用不到codec和options,所以就填写0吧
* */
re=avcodec_open2(ac,0,0);
if(re!=0){
LOGW("avcodec_open2 audio failed!");
return env->NewStringUTF(hello.c_str());
}
/*
讀取幀數據
av_packet_alloc():分配一个结构体大小的内存
av_read_frame():获取视频的一帧,不存在半帧说法。但可以获取音频的若干帧。
av_seek_frame():
函數原型:
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,int flags);
参数说明:
s:操作上下文;
stream_index:基本流索引,表示当前的seek是针对哪个基本流,比如视频或者音频等等。
timestamp:要seek的时间点,以time_base或者AV_TIME_BASE为单位。單位毫秒。
Flags:seek标志,可以设置为按字节,在按时间seek时取该点之前还是之后的关键帧,以及不按关键帧seek等
*/
AVPacket *pkt=av_packet_alloc();
/*
av_frame_alloc:分配AVFrame并将其字段设置为默认值。主要该函数只分配AVFrame的空间,它的data字段的指定的buffer需要其它函数分配。
*/
AVFrame *frame = av_frame_alloc();
long long start=GetNowMs();
int frameCount=0;
for(;;){
//假定每3秒統計下
if(GetNowMs()-start>=3000){
LOGW("now decode fps is %d",frameCount/3);
}
int re=av_read_frame(ic,pkt);
if(re!=0){
LOGW("讀取到結尾処!");
/*
這裏跳轉到第20秒処
AVSEEK_FLAG_BACKWARD表示所跳的時間點如果沒有幀则向前找第一个:若你设置seek时间为1秒,但是只有0秒和2秒上才有I帧,则时间从0秒开始。
AVSEEK_FLAG_FRAME:若你设置seek时间为1秒,但是只有0秒和2秒上才有I帧,则时间从2秒开始。是基于帧数量快进.
AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME:既往後找,又找關鍵幀
*/
int pos=20*r2d(ic->streams[videoStream]->time_base);
av_seek_frame(ic,videoStream,pos,AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);
continue;
}
// LOGW("stream =%d size=%d pts=%l ld flag=%d",pkt->stream_index,pkt->size,pkt->pts,pkt->flags);
/*
av_packet_unref():释放空间,减少引用计数:
对AVPacket缓冲区的引用计数-1.(内部调用 atomic_fetch_add_explicit(…, -1, …)),如果引用为个数为1,将释放data缓冲区;
将其余信息包字段重置为它们的默认值。
例如在编码时,循环处理压缩数据,每循环一次都会分配AVPacket->data来存储压缩后的数据(如 avcodec_receive_packet,内部使用 av_new_packet ),
处理完压缩数据之后,并且在进入下一次循环之前,记得使用 av_packet_unref 来释放已经分配的AVPacket->data缓冲区。
*/
//av_packet_unref(pkt);
//判斷流進行不同的解碼器上下文初始化
AVCodecContext *cc = vc;
if(pkt->stream_index == audioStream){
cc=ac;
}
//发送到线程中解码,发送数据到ffmepg,放到解码队列中
re = avcodec_send_packet(cc,pkt);
//清理
int p = pkt->pts;
//avpacket 解码和解封装是2个线程,如果解封装后,调用此函数后,会将avpacket的引用计数加1 或者复制一份(没有计数引用)。因此在调用了后,释放掉 avpacket。
av_packet_unref(pkt);
if(re != 0)
{
LOGW("avcodec_send_packet failed!");
continue;
}
//不斷的從當前解碼器上下文中解碼數據到解碼后用於存放AVPacket解碼后數據的數據結構AVFrame
for(;;)
{
re = avcodec_receive_frame(cc,frame);
if(re !=0)
{
//LOGW("avcodec_receive_frame failed!");
break;
}
//%lld:即long long=64bit。
LOGW("avcodec_receive_frame %lld",frame->pts);
//如果是視頻幀就統計一次
if(cc==vc){
frameCount++;
}
}
}
avformat_close_input(&ic);
return env->NewStringUTF(hello.c_str());
}
#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>
}
static double r2d(AVRational r){
return r.num==0 | r.den==0 ? 0:(double) r.num/(double)r.den;
}
/*
* 封裝一個當前時間戳返回的函數,以100小時以内返回
* ms:一般用於表示毫秒的單位
* */
long long GetNowMs(){
struct timeval tv;
gettimeofday(&tv,NULL);
/*
gettimeofday是计算机函数,使用C语言编写程序需要获得当前精确时间(1970年1月1日到现在的时间),或者为执行计时,可以使用gettimeofday()函数。
該函數返回的時間數據不是實時的,是根據cpu跳數來定的
tv_sec 代表多少秒,所以這裏tv_sec 代表多少秒從1970年1月1日到現在的秒數,因爲本函數最後要返回的是毫秒,這裏防止數據過大,所以這裏只返回100小時内的秒數
*/
int sec=tv.tv_sec%360000;
//tv.tv_usec:微秒,1毫秒等於100微秒
long long t=sec*1000+tv.tv_usec/100;
return t;
}
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
//JNI_OnLoad函數,java加載時候自動調用,這裏用做給c中調用java時的環境變量支持
av_jni_set_java_vm(vm,0);
//這裏返回一個java版本,這裏假定使用1.4的java版本
return JNI_VERSION_1_4;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndk_1and_141_MainActivity_stringFromJNI(JNIEnv* env,jobject) {
std::string hello = "Hello from C++";
hello+=avcodec_configuration();
//初始化解封裝
av_register_all();
//初始化網絡
avformat_network_init();
/*
* avcodec_register_all()只有调用了该函数,才能使用编解码器等。
* */
avcodec_register_all();
AVFormatContext *ic=NULL;
//要取一些信息,mp4格式取的比較多
char path[]="/sdcard/1080.mp4";
int re=avformat_open_input(&ic,path,0,0);
if(re!=0){
LOGW("avformat_open_input failed!:%s",av_err2str(re));
return env->NewStringUTF(hello.c_str());
}
/*
這裏需要漲點教訓,原來這裏是LOGW("avformat_open_input %s success!");
也就是多帶了一個%s,很明顯這裏語法在c中是有問題的,可是編譯過程時候沒有報錯。
日志貓中報錯信息讓我一度懷疑自己的arm64位動態鏈接庫編譯錯了呢。。。
*/
LOGW("avformat_open_input success!");
/*
duration方法:
mp4格式可以直接獲取到時長
返回類型是int64_t:
int64_t 是标准C ++类型,用于完全64位的有符号整数。 int64 不是标准类型。
第一个C ++标准没有固定宽度类型。在将 int64_t 添加到标准C ++之前,不同的编译器都实现了64位类型,但它们使用了自己的名称(例如 long long < / code>, __ int64 等。)
那麽因爲long long < / code>,所以這裏int64_t的輸出就是l ld了
*/
//對於flv來説,要先探測獲取下流信息,若不先探測,則獲取的場地可能就是如:-9223372036854775808一樣的負數
re=avformat_find_stream_info(ic,0);
if(re!=0){
LOGW("avformat_find_stream_info failed!");
}
//這裏格式化int64_t的時候千萬記住不是%lld而是%l ld
LOGW("duration=%l ld nb_streams=%d",ic->duration,ic->nb_streams);
int fps = 0;
int videoStream = 0;
int audioStream = 1;
/*
遍历获取AVStream音视频流信息并打印参数
*/
for(int i = 0; i < ic->nb_streams; i++)
{
AVStream *as = ic->streams[i];
if(as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
LOGW("视频数据");
videoStream = i;
//avg_frame_rate:幀率,一個分數,就是fps的分數表示法,分數表示可以有效保護精度損失
fps = r2d(as->avg_frame_rate);
/*
codecpar->width,codecpar->height:视频的宽高,只有视频有
codecpar->codec_id: 獲取解碼器的id號
codecpar->format:格式。对于视频来说指的就是像素格式(YUV420,YUV422...),对于音频来说,指的就是音频的采样格式。
*/
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;
/*
codecpar->sample_rate:樣本率
codecpar->channels:声道数
codecpar->format:格式。对于视频来说指的就是像素格式(YUV420,YUV422...),对于音频来说,指的就是音频的采样格式。
*/
LOGW("sample_rate=%d channels=%d sample_format=%d",
as->codecpar->sample_rate,
as->codecpar->channels,
as->codecpar->format
);
}
}
/*
獲取音頻流信息
第一個參數:AVStream的實例
第二個參數type:音頻或視頻流類型
第三個參數wanted_stream_nb是指定索引,這裏沒有必要,所以就為-1
第四個參數related_stream,同一節目組的相關流信息,這裏也沒有節目組,所以也就設置為-1
第五個參數decoder_ret 這個參數的作用我們要單獨做
第五個參數flags 目前還沒有用
*/
audioStream=av_find_best_stream(ic,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
LOGW("av_find_best_stream audioStream=%d",audioStream);
//软解码器-視頻解碼器
//avcodec_find_decoder:获取解码器
AVCodec *codec=avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
//硬解码,avcodec_find_decoder_by_name通过名字打开解码器,比如這裏h264_mediacodec就是用Android里面自带的解码模块
codec=avcodec_find_decoder_by_name("h264_mediacodec");
if(!codec){
LOGW("avcodec_find failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化,avcodec_alloc_context3方法用于获取编解码器上下文信息
AVCodecContext *vc=avcodec_alloc_context3(codec);
//avcodec_parameters_to_context()方法用于将流的参数(stream->codecpar)复制到解码器中,否则某些流可能无法正常解码。
avcodec_parameters_to_context(vc,ic->streams[videoStream]->codecpar);
//编解码时的线程数量,由用户设置,与CPU核心数有关,最佳效果一般设置为CPU核心数*2.
vc->thread_count=8;
//打开解码器
/*
* avcodec_open2()各个参数的含义:
avctx:需要初始化的AVCodecContext。
codec:输入的AVCodec
options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置
这里用不到codec和options,所以就填写0吧
* */
re=avcodec_open2(vc,0,0);
if(re!=0){
LOGW("avcodec_open2 video failed!");
return env->NewStringUTF(hello.c_str());
}
//软解码器-音頻解碼器
//avcodec_find_decoder:获取解码器
AVCodec *acodec=avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
//硬解码(先看软解码,硬解码另外再做)
//codec=avcodec_find_decoder_by_name("h264_mediacodec");
if(!acodec){
LOGW("avcodec_find_audio failed!");
return env->NewStringUTF(hello.c_str());
}
//解码器初始化,avcodec_alloc_context3方法用于获取编解码器上下文信息
AVCodecContext *ac=avcodec_alloc_context3(acodec);
//avcodec_parameters_to_context()方法用于将流的参数(stream->codecpar)复制到解码器中,否则某些流可能无法正常解码。
avcodec_parameters_to_context(ac,ic->streams[audioStream]->codecpar);
//编解码时的线程数量,由用户设置,与CPU核心数有关,最佳效果一般设置为CPU核心数*2.
ac->thread_count=1;
//打开解码器
/*
* avcodec_open2()各个参数的含义:
avctx:需要初始化的AVCodecContext。
codec:输入的AVCodec
options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置
这里用不到codec和options,所以就填写0吧
* */
re=avcodec_open2(ac,0,0);
if(re!=0){
LOGW("avcodec_open2 audio failed!");
return env->NewStringUTF(hello.c_str());
}
/*
讀取幀數據
av_packet_alloc():分配一个结构体大小的内存
av_read_frame():获取视频的一帧,不存在半帧说法。但可以获取音频的若干帧。
av_seek_frame():
函數原型:
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,int flags);
参数说明:
s:操作上下文;
stream_index:基本流索引,表示当前的seek是针对哪个基本流,比如视频或者音频等等。
timestamp:要seek的时间点,以time_base或者AV_TIME_BASE为单位。單位毫秒。
Flags:seek标志,可以设置为按字节,在按时间seek时取该点之前还是之后的关键帧,以及不按关键帧seek等
*/
AVPacket *pkt=av_packet_alloc();
/*
av_frame_alloc:分配AVFrame并将其字段设置为默认值。主要该函数只分配AVFrame的空间,它的data字段的指定的buffer需要其它函数分配。
*/
AVFrame *frame = av_frame_alloc();
long long start=GetNowMs();
int frameCount=0;
for(;;){
//假定每3秒統計下
if(GetNowMs()-start>=3000){
LOGW("now decode fps is %d",frameCount/3);
}
int re=av_read_frame(ic,pkt);
if(re!=0){
LOGW("讀取到結尾処!");
/*
這裏跳轉到第20秒処
AVSEEK_FLAG_BACKWARD表示所跳的時間點如果沒有幀则向前找第一个:若你设置seek时间为1秒,但是只有0秒和2秒上才有I帧,则时间从0秒开始。
AVSEEK_FLAG_FRAME:若你设置seek时间为1秒,但是只有0秒和2秒上才有I帧,则时间从2秒开始。是基于帧数量快进.
AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME:既往後找,又找關鍵幀
*/
int pos=20*r2d(ic->streams[videoStream]->time_base);
av_seek_frame(ic,videoStream,pos,AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);
continue;
}
// LOGW("stream =%d size=%d pts=%l ld flag=%d",pkt->stream_index,pkt->size,pkt->pts,pkt->flags);
/*
av_packet_unref():释放空间,减少引用计数:
对AVPacket缓冲区的引用计数-1.(内部调用 atomic_fetch_add_explicit(…, -1, …)),如果引用为个数为1,将释放data缓冲区;
将其余信息包字段重置为它们的默认值。
例如在编码时,循环处理压缩数据,每循环一次都会分配AVPacket->data来存储压缩后的数据(如 avcodec_receive_packet,内部使用 av_new_packet ),
处理完压缩数据之后,并且在进入下一次循环之前,记得使用 av_packet_unref 来释放已经分配的AVPacket->data缓冲区。
*/
//av_packet_unref(pkt);
//判斷流進行不同的解碼器上下文初始化
AVCodecContext *cc = vc;
if(pkt->stream_index == audioStream){
cc=ac;
}
//发送到线程中解码,发送数据到ffmepg,放到解码队列中
re = avcodec_send_packet(cc,pkt);
//清理
int p = pkt->pts;
//avpacket 解码和解封装是2个线程,如果解封装后,调用此函数后,会将avpacket的引用计数加1 或者复制一份(没有计数引用)。因此在调用了后,释放掉 avpacket。
av_packet_unref(pkt);
if(re != 0)
{
LOGW("avcodec_send_packet failed!");
continue;
}
//不斷的從當前解碼器上下文中解碼數據到解碼后用於存放AVPacket解碼后數據的數據結構AVFrame
for(;;)
{
re = avcodec_receive_frame(cc,frame);
if(re !=0)
{
//LOGW("avcodec_receive_frame failed!");
break;
}
//%lld:即long long=64bit。
LOGW("avcodec_receive_frame %lld",frame->pts);
//如果是視頻幀就統計一次
if(cc==vc){
frameCount++;
}
}
}
avformat_close_input(&ic);
return env->NewStringUTF(hello.c_str());
}
那麽最後編譯並運行在如魅族16T上可以看到硬解碼的速率。雖然夏曹俊老師說一般是穩定在120計算是有誤差的,畢竟時間的即時性是每3秒為統計周期并且gettimeofday函數返回的時間數據不是實時的,是根據cpu跳數來定的。不過這個只是我的理解。但是這裏輸出的結果明顯大於120速率太多了。我的理解是這裏并非是固定120的,應該是根據具體硬件來確定的,畢竟是硬解的。不過夏曹俊老師也說了“一般”。
另外硬解碼的方式則綫程數就是無意義的,沒有用的。例如這裏vc->thread_count就是沒有用處的一個參數設置了。
关键字词:MediaCodec,硬解碼
相关文章
-
无相关信息