android :ffmpeg+anativewindow做视频播放

这篇博客详细介绍了如何在Android平台上利用ffmpeg库和anativewindow进行视频播放。首先,通过avformat_alloc_context()创建AVFormatContext,接着avformat_open_input打开输入流,avformat_find_stream_info获取流信息。文中还提到了AVStream、AVCodecContext、AVCodec等关键结构体的作用。使用avcodec_open2初始化解码器,av_frame_alloc分配AVFrame,AVPacket存储压缩编码数据,而AVFrame则用于存放解码后的数据。通过av_read_frame读取数据,avcodec_decode_video2解码,sws_scale进行像素格式转换。最后,利用anativewindow展示视频帧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇博文按照 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这个对象

		// 由于windowstride和帧的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)的结构体
                                                   AVIOContextURLProtocolURLContext主要存储视音频使用的协议的类型以及状态。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 *opaqueURLContext结构体
        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++) {
        //AVFormatContextunsigned int nb_streams:视音频流的个数
        //AVFormatContext*AVStream **streams:视音频流
        //其中AVStream是存储每一个视频/音频流信息的结构体。
        /*      AVStream
         *       int index
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值