zoukankan      html  css  js  c++  java
  • FFmpeg(三) 编解码相关函数理解

    一、编解码基本流程

    主要流程:

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

      软解码、硬解码

      进行编解码


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

      ①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 <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 <libavcodec/jni.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;
    }
    
    //当前时间戳 clock
    long 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"
    JNIEXPORT
    jint JNI_OnLoad(JavaVM *vm,void *res)
    {
        av_jni_set_java_vm(vm,0);
        return JNI_VERSION_1_4;
    }
    
    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();
    
        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 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
    天助自助者
  • 相关阅读:
    洛谷P2089 烤鸡
    HDU-1000 A+B Problem
    《新标准C++程序设计》4.7-4.9(C++学习笔记17)
    《新标准C++程序设计》4.6(C++学习笔记16)
    面向对象程序设计寒假作业3
    《新标准C++程序设计》4.5(C++学习笔记15)
    《新标准C++程序设计》4.4(C++学习笔记14)
    《新标准C++程序设计》4.2-4.3(C++学习笔记13)
    洛谷题解P1047 校门外的树
    [lr] 矫正白平衡
  • 原文地址:https://www.cnblogs.com/ZeGod/p/9995240.html
Copyright © 2011-2022 走看看