zoukankan      html  css  js  c++  java
  • FFmpeg(9)-解码器解码代码演示(FFmpeg调用MediaCodec实现硬解码、多线程解码、及音视频解码性能测试)

    一.AVFrame

    用来存放解码后的数据。

    【相关函数】

    AVFrame *frame = av_frame_alloc();                       // 空间分配,分配一个空间并初始化。

    void av_frame_free(AVFrame **frame);                   // 空间释放。两种释放方式,一种是将引用计数-1,

    int av_frame_ref(AVFrame *dst, const AVFrame *src); // 引用计数增加1。比如要在多线程访问的时候复制到另外一边,就可以利用引用计数的变化。

    AVFrame *av_frame_clone(const AVFrame *src);      // 复制。也是重新创建一个空间,然后引用计数加+1。与AVPacket的区别在于:AVFrame的复制开销更大。1920*1080p的视频,一帧可能就有几MB,一秒钟可能就有几百MB,所以做一帧画面的内存复制可能都耗费到毫秒级别,不像AVPacket可能只有微秒级别,会影响帧率。所以在它的空间复制上一定要慎重,所以我们一般用引用计数的方式来做。

    void av_frame_unref(AVFrame *frame);                   // 直接把对象的引用计数-1.

    【结构体包含的内容】

    uint8_t *data[AV_NUM_DATA,POINTERS]                // 存放的数据。

    int linesize[AV_NUM_DATA,POINTERS]                    // 对于视频就是一行数据的大小。对于音频就是一个通道数据的大小。

    int width, int height, int nb_samples                         // 视频部分, 音频相关(单通道的样本数量)

    int64_ t pts                                                             // 实际这一帧的pts。

    int64_t pkt_dts                                                       // 对应包当中的dts。

    int sample_rate;                                                      // 样本率

    uint64_t channel_layout;                                         // 通道类型

    int channels;                                                          // 通道数量

    int format;                                                              // 像素格式。区分音频和视频。视频的话就是AVPixelFormat,音频的话就是AVSampleFormat

    二.解码器解码代码演示

    // 初始化解封装
        av_register_all();
        // 注册解码器
        avcodec_register_all();
    
        // 初始化网络
        avformat_network_init();
        // 打开文件
        AVFormatContext *ic = NULL;
        char path[] = "sdcard/1080.mp4";
        // char path[] = "/sdcard/qingfeng.flv";
        int ret = avformat_open_input(&ic, path, 0, 0);
        if (ret != 0) {
            LOGE("avformat_open_input() called failed: %s", av_err2str(ret));
            return env->NewStringUTF(hello.c_str());
        }
        LOGI("avformat_open_input(): File open success.");
        LOGI("File duration is: %lld, nb_stream is: %d", ic->duration, ic->nb_streams);
        if (avformat_find_stream_info(ic, 0) >=0 ) {
            LOGI("File duration is: %lld, nb_stream is: %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) {
                LOGI("视频数据");
                videoStream = i;
                fps = (int)r2d(as->avg_frame_rate);
                LOGI("fps = %d, width = %d, height = %d, codecid = %d, format = %d",
                     fps,
                     as->codecpar->width,
                     as->codecpar->height,
                     as->codecpar->codec_id,
                     as->codecpar->format);
            } else if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                LOGI("音频数据");
                audioStream = i;
                LOGI("sample_rate = %d, channels = %d, sample_format = %d",
                     as->codecpar->sample_rate,
                     as->codecpar->channels,
                     as->codecpar->format
                );
            }
        }
    
        // 也可以利用av_find_best_stream()函数来查找音视频流索引
        // audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
        // LOGI("av_find_best_stream, audio index is: %d", audioStream);
    
    
        // 查找视频解码器
        AVCodec *vCodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id); // 软解
        // vCodec = avcodec_find_decoder_by_name("h264_mediacodec"); // 硬解
        if (!vCodec) {
            LOGE("avcodec_find_decoder() failed. can not found video decoder.");
            return env->NewStringUTF(hello.c_str());
        }
        // 配置解码器上下文
        AVCodecContext *vc = avcodec_alloc_context3(vCodec);
        // 将AVStream里面的参数复制到上下文当中
        avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);
        vc->thread_count = 8;
        // 打开解码器
        ret = avcodec_open2(vc, vCodec, 0);
        if (ret != 0) {
            LOGE("avcodec_open2() failed. can not open video decoder, line is: %d", __LINE__);
            return env->NewStringUTF(hello.c_str());
        }
    
        // 查找音频解码器
        AVCodec *aCodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id); // 软解
        // aCodec= avcodec_find_decoder_by_name("h264_mediacodec"); // 硬解
        if (!aCodec) {
            LOGE("avcodec_find_decoder() failed. can not found audio decoder.");
            return env->NewStringUTF(hello.c_str());
        }
        // 配置解码器上下文
        AVCodecContext *ac = avcodec_alloc_context3(aCodec);
        // 将AVStream里面的参数复制到上下文当中
        avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);
        ac->thread_count = 8;
        // 打开解码器
        ret = avcodec_open2(ac, aCodec, 0);
        if (ret != 0) {
            LOGE("avcodec_open2() failed. can not open audio decoder");
            return env->NewStringUTF(hello.c_str());
        }
    
        // 读取帧数据
        AVPacket *packet = av_packet_alloc();
        AVFrame *frame = av_frame_alloc();
        int64_t start = getNowMs();
        int frameCount = 0;
    
        for (;;) {
    
            if (getNowMs() - start >= 3000) {
                LOGI("now decoder fps is: %d", frameCount / 3);
                start = getNowMs();
                frameCount = 0;
            }
            int ret = av_read_frame(ic, packet);
            if (ret != 0) {
                LOGE("读取到结尾处");
                int pos = 20 * r2d(ic->streams[videoStream]->time_base);
                // 改变播放进度
                av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
                continue;
            }
    
    //        LOGI("Read a Packet. streamIndex=%d, size=%d, pts=%lld, flag=%d",
    //             packet->stream_index,
    //             packet->size,
    //             packet->pts,
    //             packet->flags
    //        );
    
            AVCodecContext *cc = vc;
            if (packet->stream_index == audioStream) cc = ac;
            // 发送到线程中去解码(将packet写入到解码队列当中去)
            ret = avcodec_send_packet(cc, packet);
            // 清理
            int p = packet->pts;
            av_packet_unref(packet);
            if (ret != 0) {
                // LOGE("avcodec_send_packet failed.");
                continue;
            }
    
            for(;;) {
                // 从已解码成功的数据当中取出一个frame, 要注意send一次,receive不一定是一次
                ret = avcodec_receive_frame(cc, frame);
                if (ret != 0) {
                    break;
                }
                if (cc == vc) {
                    frameCount++;
                }
                // LOGI("Receive a frame.........");
            }
        }
    
        // 关闭上下文
        avformat_close_input(&ic);
        return env->NewStringUTF(hello.c_str());

    另外:硬件解码器需要进行注册,即需要把Java虚拟机的环境传递给FFmpeg,因此还需要添加下列代码,否则解码器无法打开。

    extern "C"
    JNIEXPORT
    jint JNI_OnLoad(JavaVM *vm,void *res)
    {
        av_jni_set_java_vm(vm,0);
        return JNI_VERSION_1_4;
    }

     av_jni_set_java_vm(vm,0); 包含在 <libavcodec/jni.h> 头文件当中,不要忘记包含该头文件。

  • 相关阅读:
    METHODS OF AND APPARATUS FOR USING TEXTURES IN GRAPHICS PROCESSING SYSTEMS
    Display controller
    Graphics processing architecture employing a unified shader
    Graphics-Processing Architecture Based on Approximate Rendering
    Architectures for concurrent graphics processing operations
    Procedural graphics architectures and techniques
    DYNAMIC CONTEXT SWITCHING BETWEEN ARCHITECTURALLY DISTINCT GRAPHICS PROCESSORS
    Thermal zone monitoring in an electronic device
    System and method for dynamically adjusting to CPU performance changes
    Framework for Graphics Animation and Compositing Operations
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/9810379.html
Copyright © 2011-2022 走看看