ffmpeg封装和解封装介绍-(4)解封装出数据并渲染

文件树:

封装好的函数在前面文章里有

主函数:


#include <iostream>
#include <thread>
#include "xdecode.h"
#include "xvideoview.h"
using namespace std;
extern "C" { //指定函数是c语言函数,函数名不包含重载标注
//引用ffmpeg头文件
#include <libavformat/avformat.h>
}
//预处理指令导入库
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
//void PrintErr(int err)
//{
//    char buf[1024] = { 0 };
//    av_strerror(err, buf, sizeof(buf) - 1);
//    cerr << buf << endl;
//}
#define CERR(err) if(err!=0){ PrintErr(err);getchar();return -1;}

int main(int argc, char* argv[])
{
    //打开媒体文件
    const char* url = "v1080.mp4";

    //解封装输入上下文
    AVFormatContext* ic = nullptr;
    auto re = avformat_open_input(&ic, url,
        NULL,       //封装器格式 null 自动探测 根据后缀名或者文件头
        NULL        //参数设置,rtsp需要设置
    );
    CERR(re);

    //获取媒体信息 无头部格式
    re = avformat_find_stream_info(ic, NULL);
    CERR(re);

    //打印封装信息
    av_dump_format(ic, 0, url,
        0 //0表示上下文是输入 1 输出
    );
    AVStream* as = nullptr; //音频流
    AVStream* vs = nullptr; //视频流
    for (int i = 0; i < ic->nb_streams; i++)
    {
        //音频
        if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            as = ic->streams[i];
            cout << "=====音频=====" << endl;
            cout << "sample_rate:" << as->codecpar->sample_rate << endl;
        }
        else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            vs = ic->streams[i];
            cout << "=========视频=========" << endl;
            cout << "width:" << vs->codecpar->width << endl;
            cout << "height:" << vs->codecpar->height << endl;
        }
    }


    auto video_codec_id = vs->codecpar->codec_id;   //视频编码器ID
    auto video_par = vs->codecpar;                  //视频编码参数
    //
    /// 视频解码初始化
    XDecode decode;
    AVCodecContext* decode_c = XCodec::Create(video_codec_id, false);
    //解封装的视频编码参数,传递给解码上下文
    avcodec_parameters_to_context(decode_c, video_par);

    //设置到解码器中,线程安全,设置完后decode_c 不在外部中使用
    decode.set_c(decode_c);
    if (!decode.Open())
    {
        cout << "decode Open failed!" << endl;
        return -1;
    }

    //创建解码输出的空间
    auto frame = decode.CreateFrame();
    //


    //
    /// 渲染初始化
    auto view = XVideoView::Create();
    view->Init(video_par->width, video_par->height, (XVideoView::Format)video_par->format);
    //


    AVPacket pkt;
    for (;;)
    {
        re = av_read_frame(ic, &pkt);
        CERR(re);
        if (vs && pkt.stream_index == vs->index)
        {
            cout << "视频:";
            //解码视频
            if (decode.Send(&pkt))
            {
                while (decode.Recv(frame))
                {
                    cout << frame->pts << " " << flush;
                    view->DrawFrame(frame); //渲染视频
                }
            }

        }
        else if (as && pkt.stream_index == as->index)
        {
            //cout << "音频:";
        }
        //cout << pkt.pts << " : " << pkt.dts << " :" << pkt.size << endl;
        av_packet_unref(&pkt);
    }

    av_frame_free(&frame);
    avformat_close_input(&ic);
    return 0;
}

运行结果:

 

好的,针对您的问题,我来简单回答一下。 首先,要明确的是,FFmpeg 是一个开源的跨平台音视频处理库,支持包括码、编码、封装封装等功能。而封装(Demuxing)是指将音视频封装格式中的音视频流来,以便后续对音视频流进行码、编码等操作。 在 Android 基于 FFmpeg 开发简易播放器中,我们可以使用 FFmpeg 提供的 API 来进行封装。具体步骤如下: 1. 打开输入文件 使用 FFmpeg 的 avformat_open_input() 函数打开要封装的音视频文件,该函数返回一个 AVFormatContext 结构体指针,该指针包含了输入文件的相关信息。 2. 寻找音视频流 使用 FFmpeg 的 avformat_find_stream_info() 函数读取输入文件的文件头信息,并寻找其中包含的音视频流。该函数会将每个音视频流的信息保存在 AVStream 结构体中。 3. 选择音视频流 根据需要播放的音视频流类型,在所有寻找到的音视频流中选择对应的流。可以通过判断 AVStream 结构体中的 codecpar->codec_type 来确定该流是音频流还是视频流。 4. 获取码器 使用 FFmpeg 的 avcodec_find_decoder() 函数获取对应的码器,并使用 avcodec_open2() 函数打开码器。 5. 循环读取数据包 使用 FFmpeg 的 av_read_frame() 函数循环读取音视频数据包(AVPacket 结构体),并将数据包送到码器进行码。 6. 关闭码器和输入文件 在播放完成后,需要使用 avcodec_free_context() 函数释放码器占用的资源,并使用 avformat_close_input() 函数关闭输入文件。 以上就是基于 FFmpeg 进行封装的大致步骤。当然,在实际开发中还有很多细节需要注意,比如错误处理、内存管理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值