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

 

转载于:https://www.cnblogs.com/ZeGod/p/9995240.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 FFmpeg 进行音视频编解码时,需要注册相应的编解码器。可以通过 avcodec_register_all() 函数来注册 FFmpeg 中所有已经实现的编解码器。 具体步骤如下: 1. 引入头文件 ```c #include <libavcodec/avcodec.h> ``` 2. 注册编解码器 ```c avcodec_register_all(); ``` 3. 查找编解码器 在注册编解码器后,可以通过调用 avcodec_find_encoder() 或 avcodec_find_decoder() 函数来查找特定的编解码器。 例如,查找 H.264 编码器: ```c AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!codec) { // 没有找到 H.264 编码器 return -1; } ``` 4. 打开编解码器 在找到所需的编解码器后,可以通过调用 avcodec_open2() 函数来打开编解码器。该函数会分配一个 AVCodecContext 结构体,用于存储编解码相关的信息。 例如,打开 H.264 编码器: ```c AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!codec) { // 没有找到 H.264 编码器 return -1; } AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { // 分配 AVCodecContext 失败 return -1; } // 设置编码参数 codec_ctx->width = width; codec_ctx->height = height; codec_ctx->time_base = (AVRational){1, fps}; codec_ctx->framerate = (AVRational){fps, 1}; // 打开编码器 if (avcodec_open2(codec_ctx, codec, NULL) < 0) { // 打开编码器失败 avcodec_free_context(&codec_ctx); return -1; } ``` 5. 使用编解码器 打开编码器后,就可以使用它进行音视频编解码了。对于编码器,可以通过调用 avcodec_send_frame() 函数发送待编码的帧,然后调用 avcodec_receive_packet() 函数获取编码后的数据包。 例如,使用 H.264 编码器进行视频编码: ```c AVPacket pkt = {0}; // 发送帧 if (avcodec_send_frame(codec_ctx, frame) < 0) { // 发送帧失败 return -1; } // 获取编码后的数据包 while (avcodec_receive_packet(codec_ctx, &pkt) == 0) { // 处理数据包 ... // 释放数据包内存 av_packet_unref(&pkt); } ``` 对于解码器,可以通过调用 avcodec_send_packet() 函数发送待解码的数据包,然后调用 avcodec_receive_frame() 函数获取解码后的帧。 例如,使用 H.264 解码器进行视频解码: ```c AVFrame *frame = av_frame_alloc(); // 发送数据包 if (avcodec_send_packet(codec_ctx, &pkt) < 0) { // 发送数据包失败 return -1; } // 获取解码后的帧 while (avcodec_receive_frame(codec_ctx, frame) == 0) { // 处理帧 ... } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值