博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FFmpeg(三) 编解码相关函数理解
阅读量:6716 次
发布时间:2019-06-25

本文共 7131 字,大约阅读时间需要 23 分钟。

一、编解码基本流程

主要流程:

  打开视频解码器(音频一样)

  软解码、硬解码

  进行编解码


 

下面先来看打开视频解码器  

  ①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 != 0 )  失败 return    //开始接收数据,把解码成功的frame取出来    int re = avcodec_receive_frame( AVCodecContext *acctx ,AVFrame *frame);          if(re !=0)  失败 return

 


 

FFmpeg调用MediaCodec实现硬解码 

  ① 需要C++调用java ,在C++代码中实现一个函数,Java在执行的时候会自己调用这个函数,不用去调用。

  jnit  JNI_OnLoad(JavaVM *vm, void *res){        av_jni_set_java_vm(vm , 0);          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 
#include
#include
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,"testff",__VA_ARGS__)extern "C"{#include
#include
#include
}#include
using namespace std;static double r2d(AVRational r){ return r.num==0||r.den == 0 ? 0 :(double)r.num/(double)r.den;}//当前时间戳 clocklong long GetNowMs(){ struct timeval tv; gettimeofday(&tv,NULL); int sec = tv.tv_sec%360000; long long t = sec*1000+tv.tv_usec/1000; return t;}extern "C"JNIEXPORTjint JNI_OnLoad(JavaVM *vm,void *res){ av_jni_set_java_vm(vm,0); return JNI_VERSION_1_4;}extern "C"JNIEXPORT jstringJNICALLJava_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,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!",path); //获取流信息 re = avformat_find_stream_info(ic,0); if(re != 0) { LOGW("avformat_find_stream_info failed!"); } LOGW("duration = %lld nb_streams = %d",ic->duration,ic->nb_streams); int fps = 0; int videoStream = 0; int audioStream = 1; 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; 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,-1,-1,NULL,0); 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 = 8; //打开解码器 re = avcodec_open2(vc,0,0); //vc->time_base = ic->streams[videoStream]->time_base; LOGW("vc timebase = %d/ %d",vc->time_base.num,vc->time_base.den); if(re != 0) { 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 = 8; //打开解码器 re = avcodec_open2(ac,0,0); if(re != 0) { 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 = 0; for(;;) { //超过三秒 if(GetNowMs() - start >= 3000) { LOGW("now decode fps is %d",frameCount/3); start = GetNowMs(); frameCount = 0; } int re = av_read_frame(ic,pkt); if(re != 0) { LOGW("读取到结尾处!"); int pos = 20 * 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 != 0) { LOGW("avcodec_send_packet failed!"); continue; } for(;;) { re = avcodec_receive_frame(cc,frame); if(re !=0) { //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 JNICALLJava_aplay_testffmpeg_MainActivity_Open(JNIEnv *env, jobject instance, jstring url_, jobject handle) { const char *url = env->GetStringUTFChars(url_, 0); // 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;}
View Code

 

转载于:https://www.cnblogs.com/ZeGod/p/9995240.html

你可能感兴趣的文章
tomcat配置文件详解
查看>>
iOS NSURLSession DownloadTask(下载任务)
查看>>
vue解决字段类型为数字导致单选不正确的问题
查看>>
Prometheus 2.0正式推出 性能提升带来质的飞跃
查看>>
WPF实现抽屉效果
查看>>
http2-浏览器支持的情况
查看>>
去除百度置顶的广告,优化百度搜索
查看>>
设计模式(六)适配器模式
查看>>
GTK+重拾--04 菜单栏使用
查看>>
设计模式(十七) 迭代器模式
查看>>
线性回归 极大似然 参差满足高斯分布
查看>>
持续集成之测试自动化
查看>>
多字符串拼接
查看>>
后台登录——实验吧
查看>>
表格存储如何在控制台使用多元索引(SearchIndex)功能
查看>>
Java并发编程艺术----读书笔记(一)
查看>>
第1章—Spring之旅—简化Spring的java开发
查看>>
Spring Web MVC框架(九) XML和JSON视图与内容协商
查看>>
百度地图深度使用
查看>>
c++11新特性
查看>>