• FFmpeg(二) 解封装相关函数理解


    一、解封装基本流程

      ①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;
    }
    View Code




    天助自助者
  • 相关阅读:
    两数之和
    数组,链表,跳表
    第二讲:增长全景图
    三数之和
    第一讲:增长的本质
    移动零
    八十忆双亲+师友杂记
    java:从命令行接收多个数字,求和之后输出结果
    编程的精义读后感
    java语言基础第三讲作业
  • 原文地址:https://www.cnblogs.com/ZeGod/p/9995234.html
Copyright © 2020-2023  润新知