本篇博文按照 http://blog.youkuaiyun.com/glouds/article/details/50937266 的方法来操作,主要通过记录对各个函数的解释来学习ffmpeg。
关于ffmpeg的编译以及具体java层的代码可以按照上面的博客中去操作,这里只贴出底层的代码:
关于ffmpeg的知识下面只要介绍了:
AVFormatContext 结构体:avformat_alloc_context()来获取,主要存储视音频封装格式中包含的信息,比如流的个数,流本身,比特率等等
avformat_open_input含义:打开输入流,将结构体和文件联系起来了,等于把文件的数据open打开后给了AVFormatContext 这里面
AVIOContext结构体:FFMPEG管理输入输出数据的结构体,缓存大小,指针位置
avformat_find_stream_info含义:可以读取一部分视频信息来获取一些相关信息,用来判断这个AVFormatContext 里面有没有流信息,这个 AVFormatContext 在上面的打开输入流被赋予了数据
AVStream结构体:存储了一个视频/音频的流信息的结构体,比如编解码格式,帧率,时基等等
AVCodecContext结构体:编解码格式的的结构体。比如编解码器的类型(eg:H264),宽高,采样率,声道等等
AVCodec结构体:存储编码器信息的结构体:比如编码器的名称,支持的帧率、像素格式、采样率等等
avcodec_open2含义:用于初始化一个视音频解码器的AVCodecContext
av_frame_alloc含义:等于申请一个内存,或者说 申请一个AVFrame结构体对象,只有空壳子,没有具体的数据
AVPacket结构体:存储压缩编码数据相关信息的结构体
AVFrame结构体:存数据,这是视音频具体的数据,视频(yuv或者rgb),音频(pcm)这是解码后的数据 和AVPacket(解码前的数据)对应,
av_image_file_arrays含义:基于镇定的图像参数和提供的数组设置数据指针和行数
sws_getContext含义:分配并返回一个SwsContext。 您需要使用sws_scale()执行缩放/转换操作。
av_read_frame含义:从流中读取一部分数据到packet里面去
avcondec_decode_video2含义:从avpacket中读取出一个一帧的avframe出来;
sws_scale含义:像素格式的转换 yuv----->rgba
这里还有nativeWindow这个对象
// 由于window的stride和帧的stride不同,因此需要逐行复制 int h; for (h = 0; h < videoHeight; h++) { //循环的次数是videoHeight 视频帧画面的高度 // dst , source ,length memcpy(dst + h * dstStride, src + h * srcStride, srcStride); //这里发现这个 等于是把数据一行行铺到屏幕上去 //memcpy(dst + h * dstStride, src + h * srcStride, srcStride-1000); //如果-1000 去操作 发现横屏的时候屏幕右边1000距离的数据是黑的 没有的 //uint8_t *dst = (uint8_t *) windowBuffer.bits相当于屏幕的所有像素点的一个集合的起点的 //一行行去赋值数据 //dst + h * dstStride这个可以看做是对屏幕像素点在内存空间的指针src + h * srcStride这个可以看做是赋值后个pRGBAFrame在内存中的指针本来 屏幕像素都是黑,一行行去赋值就出现了我们的一帧画面了//LOGD("while if (frameFinished) for (h = 0; h < videoHeight; h++)"); }
具体代码如下:
JNIEXPORT jint JNICALL Java_com_example_jareld_wfdcamera_1server_VideoPlayer_play(JNIEnv *env, jclass type, jobject surface) { // surface 就是 从java层穿过来surface对象 // sd卡中的视频文件地址,可自行修改或者通过jni传入 char *file_name = "/storage/emulated/0/test1.h264"; //注册所有的编解码器,复用/解复用器等等组件。其中调用了avcodec_register_all()注册所有编解码器相关的组件。 av_register_all(); AVFormatContext *pFormatCtx = avformat_alloc_context(); //创建AVFormatContext结构体。 AVFormatContext主要存储视音频封装格式中包含的信息 /*AVFormatContext结构体 几个重要的参数 * *AVIOContext *pb:输入数据的缓存 *unsigned int nb_streams:视音频流的个数 *AVStream **streams:视音频流 *char filename[1024]:文件名 *int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000) *int bit_rate:比特率(单位bps,转换为kbps需要除以1000) *AVDictionary *metadata:元数据 */ if (avformat_open_input(&pFormatCtx, file_name, NULL, NULL) != 0) { /*avformat_open_input含义:打开媒体的函数中完成下面几个功能 输入输出结构体AVIOContext的初始化:有关解协议(http,rtsp,rtmp,mms)的结构体 AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”) 输入数据的协议(例如RTMP,或者file)的识别(通过一套评分机制):1判断文件名的后缀 2读取文件头的数据进行比对; 使用获得最高分的文件协议对应的URLProtocol,通过函数指针的方式,与FFMPEG连接(非专业用词); 剩下的就是调用该URLProtocol的函数进行open,read等操作了 */ //AVIOContext结构体 是FFMPEG管理输入输出数据的结构体 // unsigned char *buffer:缓存开始位置 // int buffer_size:缓存大小(默认32768) 在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。 // unsigned char *buf_ptr:当前指针读取到的位置 // unsigned char *buf_end:缓存结束的位置 // void *opaque:URLContext结构体 int err_code = avformat_open_input(&pFormatCtx, file_name, NULL, NULL); char buf[1024]; av_strerror(err_code, buf, 1024); LOGD("Couldn't open file %s: %d(%s)", file_name, err_code, buf); LOGD("Couldn't open file:%s\n", file_name); return -1; // Couldn't open file } if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { // Retrieve(取得) stream information //该函数可以读取一部分视音频数据并且获得一些相关的信息。 LOGD("Couldn't find stream information."); return -1; } // Find the first video stream int videoStream = -1, i; for (i = 0; i < pFormatCtx->nb_streams; i++) { //AVFormatContext:unsigned int nb_streams:视音频流的个数 //AVFormatContext:*AVStream **streams:视音频流 //其中AVStream是存储每一个视频/音频流信息的结构体。 /* AVStream * int index