一、解封装基本流程
①av_register_All()////初始化解封装,注册解析和封装的格式。 ②avformat_netword_init()//初始化网络,解析rtsp协议 ③avformat_open_init()//打开 ④avformat_find_stream_info()//探测 ⑤av_find_best_stream() //获取音视频的索引 ⑥av_read_Frame()//读一帧数据,音频可能好几帧、视频是I帧 ⑦av_seek_frame()//跳转
二、函数介绍
①av_register_all();
初始化解封装,注册解封装格式
在最开始编译FFmpeg的时候,我们做了一个configure的配置,其中开启或者关闭了很多选项。configure的配置会生成两个文件:config.mk和config.h。
config.mk:就是makefile文件需要包含进去的子模块,会作用在编译阶段,帮助开发者编译出正确的库。
config.h:作用在运行阶段,主要是确定需要注册那些容器及编解码格式到FFmpeg框架中。
调用 av_register_all 就可以注册config.h里面开发的编解码器,然后会注册所有的Muxer和Demuxer(封装格式),最后注册所有的Protocol(协议)。
②avformat_network_init();
初始化网络,rtsp流
③avformat_open_input(&ic,path,0,0);
打开文件并解析视频流、音频流、流的参数,
参数:AVFormactContext **ps;
const char *URL;
AVInputFormat *fmt;
AVDictionary *options;
④avformat_find_stream_info()
探测
⑤av_find_best_stream()
获取对应的视频流或者视频流 还有一种方式:遍历返回值的一个成员(Stream数组),根据标志位判断是音频还是视频。
⑥av_read_frame();
从AVPacket中,读取帧信息,av_read_packet函数读出的是包,其可能是半帧或者多帧,不保证帧的完整性。
av_read_frame对av_read_packet进行了封装,使其读出的数据总是完整的帧,av_read_frame函数调用了read_fram_internal。读取码流中若干个音频帧或者I帧视频。例如,在解码视频的时候,每解码一个视频,需要先调用av_read_frame获得一帧视频的压缩数据,然后才能对该数据进行解码
返回值有三种结果:
①如果packet_buffer存在数据,根据PTS返回AVPacket。
②如果packet_buffer不存在数据,调用av_read_frame_internal函数。
③(待补充)
代码展示:
/*****************************FILE INFOMATION*********************************** ** av_register_all(); //初始化解封装,注册解封装格式 在最开始编译FFmpeg的时候,我们做了一个configure的配置,其中开启或者关闭了很多选项。configure的配置会生成两个文件:config.mk和config.h。 config.mk:就是makefile文件需要包含进去的子模块,会作用在编译阶段,帮助开发者编译出正确的库。 config.h:作用在运行阶段,主要是确定需要注册那些容器及编解码格式到FFmpeg框架中。 调用 av_register_all 就可以注册config.h里面开发的编解码器,然后会注册所有的Muxer和Demuxer(封装格式),最后注册所有的Protocol(协议)。 avformat_network_init(); //初始化网络,rtsp流 avformat_open_input(&ic,path,0,0); //打开文件并解析视频流、音频流、流的参数, 参数:AVFormactContext **ps; const char *URL; AVInputFormat *fmt; AVDictionary *options; avformat_find_stream_info() //探测 av_find_best_stream() //获取对应的视频流或者视频流 还有一种方式:遍历返回值的一个成员(Stream数组),根据标志位判断是音频还是视频。 av_read_frame(); 从AVPacket中,读取帧信息,av_read_packet函数读出的是包,其可能是半帧或者多帧,不保证帧的完整性。 av_read_frame对av_read_packet进行了封装,使其读出的数据总是完整的帧,av_read_frame函数调用了read_fram_internal。 读取码流中若干个音频帧或者I帧视频。例如,在解码视频的时候,每解码一个视频,需要先调用av_read_frame获得一帧视频的压缩数据,然后才能对该数据进行解码 返回值有三种结果: ①如果packet_buffer存在数据,根据PTS返回AVPacket。 ②如果packet_buffer不存在数据,调用av_read_frame_internal函数。 ③不够清晰?(android视频开发书) ** *******************************************************************************/ #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<iostream> using namespace std; static double r2d(AVRational r) { return r.num==0||r.den == 0 ? 0 :(double)r.num/(double)r.den; } 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(); //打开文件 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); //读取帧数据 AVPacket *pkt = av_packet_alloc(); for(;;) { 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; } LOGW("stream = %d size =%d pts=%lld flag=%d", pkt->stream_index,pkt->size,pkt->pts,pkt->flags ); ////////////////////// av_packet_unref(pkt); } //关闭上下文 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_, 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; }