前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux

FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux

作者头像
gongluck
发布于 2019-07-08 08:48:44
发布于 2019-07-08 08:48:44
62600
代码可运行
举报
文章被收录于专栏:C++C++
运行总次数:0
代码可运行

Github

https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff

CDemux.h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*******************************************************************
*  Copyright(c) 2019
*  All rights reserved.
*
*  文件名称:    CDemux.h
*  简要描述:    解封装
*
*  作者:  gongluck
*  说明:
*
*******************************************************************/

#ifndef __CDEMUX_H__
#define __CDEMUX_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>

#ifdef __cplusplus
}
#endif

#include <string>
#include <mutex>
#include <thread>

class CDemux
{
public:
    virtual ~CDemux();
    // 状态
    enum STATUS { STOP, DEMUXING };
    // 状态通知回调声明
    typedef void (*DemuxStatusCallback)(STATUS status, const std::string& err, void* param);
    // 解封装帧回调声明
    typedef void (*DemuxPacketCallback)(const AVPacket* packet, int64_t timestamp, void* param);

    // 设置输入
    bool set_input(const std::string& input, std::string& err);
    // 获取输入
    const std::string& get_input(std::string& err);

    // 设置解封装帧回调 
    bool set_demux_callback(DemuxPacketCallback cb, void* param, std::string& err);
    // 设置解封装状态变化回调
    bool set_demux_status_callback(DemuxStatusCallback cb, void* param, std::string& err);

    // 打开输入
    bool openinput(std::string& err);
    // 开始解封装
    bool begindemux(std::string& err);
    // 停止解封装
    bool stopdemux(std::string& err);

    // 获取流索引
    int get_steam_index(AVMediaType type, std::string& err);
    // 获取流参数
    const AVCodecParameters* get_steam_par(int index, std::string& err);

    // 跳转到指定秒
    bool seek(int64_t timestamp, int index, int flags, std::string& err);

    // 启用设备采集
    bool device_register_all(std::string& err);
    // 设置输入格式
    bool set_input_format(const std::string& fmt, std::string& err);
    // 设置附加参数
    bool set_dic_opt(const std::string& key, const std::string& value, std::string& err);
    // 清理设置
    bool free_opt(std::string& err);

    // 设置bsf名称,影响回调的packet数据能否直接播放
    bool set_bsf_name(const std::string& bsf, std::string& err);

private:
    // 解封装线程
    bool demuxthread();

private:
    STATUS status_ = STOP;
    std::recursive_mutex mutex_;

    std::string input_;
    std::thread demuxth_;

    DemuxStatusCallback demuxstatuscb_ = nullptr;
    void* demuxstatuscbparam_ = nullptr;
    DemuxPacketCallback demuxpacketcb_ = nullptr;
    void* demuxpacketcbparam_ = nullptr;

    //ffmpeg
    AVFormatContext* fmtctx_ = nullptr;
    AVInputFormat* fmt_ = nullptr;
    AVDictionary* dic_ = nullptr;
    std::string bsfname_;
};

#endif//__CDEMUX_H__

CDemux.cpp

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*******************************************************************
*  Copyright(c) 2019
*  All rights reserved.
*
*  文件名称:    CDemux.cpp
*  简要描述:    解封装
*
*  作者:  gongluck
*  说明:
*
*******************************************************************/

#include "common.h"
#include "CDemux.h"

CDemux::~CDemux()
{
    std::string err;
    stopdemux(err);
    free_opt(err);
}

bool CDemux::set_input(const std::string& input, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    if (input.empty())
    {
        err = "input is empty.";
        return false;
    }
    else
    {
        input_ = input;
        return true;
    }
}

const std::string& CDemux::get_input(std::string& err)
{
    LOCK();
    err = "opt succeed.";
    return input_;
}

bool CDemux::set_demux_callback(DemuxPacketCallback cb, void* param, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    demuxpacketcb_ = cb;
    demuxpacketcbparam_ = param;

    return true;
}

bool CDemux::set_demux_status_callback(DemuxStatusCallback cb, void* param, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    demuxstatuscb_ = cb;
    demuxstatuscbparam_ = param;

    return true;
}

bool CDemux::openinput(std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";
    int ret = 0;

    avformat_close_input(&fmtctx_);
    fmtctx_ = avformat_alloc_context();
    if (fmtctx_ == nullptr)
    {
        err = "avformat_alloc_context() return nullptr.";
        return false;
    }
    ret = avformat_open_input(&fmtctx_, input_.c_str(), fmt_, &dic_);
    CHECKFFRET(ret);

    ret = avformat_find_stream_info(fmtctx_, nullptr);
    CHECKFFRET(ret);

    av_dump_format(fmtctx_, 0, input_.c_str(), 0);

    return true;
}

bool CDemux::begindemux(std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    status_ = DEMUXING;
    std::thread th(&CDemux::demuxthread, this);
    demuxth_.swap(th);

    return true;
}

bool CDemux::stopdemux(std::string& err)
{
    LOCK();
    err = "opt succeed.";

    status_ = STOP;
    if (demuxth_.joinable())
    {
        demuxth_.join();
    }
    avformat_close_input(&fmtctx_);

    return true;
}

bool CDemux::demuxthread()
{
    int ret;
    std::string err;

    AVPacket* packet = av_packet_alloc();
    const AVBitStreamFilter* bsf = nullptr;
    AVBSFContext* bsfctx = nullptr;
    AVCodecParameters* codecpar = nullptr;
    int vindex = -1;
    do
    {
        if (fmtctx_ == nullptr)
        {
            err = "fmtctx is nullptr.";
            break;
        }
        else if (packet == nullptr)
        {
            err = "av_packet_alloc() return nullptr.";
            break;
        }
        // 初始化packet
        av_init_packet(packet);

        // bsf
        if (!bsfname_.empty())
        {
            bsf = av_bsf_get_by_name(bsfname_.c_str());
            if (bsf == nullptr)
            {
                err = "av_bsf_get_by_name() return nullptr.";
                break;
            }
            ret = av_bsf_alloc(bsf, &bsfctx);
            if (ret < 0)
            {
                err = av_err2str(ret);
                break;
            }
            for (int i = 0; i < fmtctx_->nb_streams; ++i)
            {
                if (fmtctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
                {
                    codecpar = fmtctx_->streams[i]->codecpar;
                    vindex = i;
                    break;
                }
            }
            if (codecpar == nullptr)
            {
                err = "can not find codecpar.";
                break;
            }
            ret = avcodec_parameters_copy(bsfctx->par_in, codecpar);
            if (ret < 0)
            {
                err = av_err2str(ret);
                break;
            }
            ret = av_bsf_init(bsfctx);
            if (ret < 0)
            {
                err = av_err2str(ret);
                break;
            }
        }

        // 循环读数据解码数据
        while (true)
        {
            if (status_ != DEMUXING)
            {
                break;
            }

            // 读数据
            ret = av_read_frame(fmtctx_, packet);
            if (ret < 0)
            {
                err = av_err2str(ret);
                break; //这里认为视频读取完了
            }
            else if (demuxpacketcb_ != nullptr)
            {
                if (packet->stream_index == vindex && bsfctx != nullptr)
                {
                    ret = av_bsf_send_packet(bsfctx, packet);
                    if (ret < 0)
                    {
                        err = av_err2str(ret);
                        break;
                    }
                    while (ret >= 0)
                    {
                        ret = av_bsf_receive_packet(bsfctx, packet);
                        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        {
                            // 不完整或者EOF
                            break;
                        }
                        else if (ret < 0)
                        {
                            // 其他错误
                            err = av_err2str(ret);
                            if (demuxstatuscb_ != nullptr)
                            {
                                demuxstatuscb_(DEMUXING, err, demuxstatuscbparam_);
                            }
                            break;
                        }
                        else
                        {
                            demuxpacketcb_(packet, av_rescale_q_rnd(packet->pts, fmtctx_->streams[packet->stream_index]->time_base, { 1, 1 }, static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)), demuxpacketcbparam_);
                        }
                    }
                }
                else
                {
                    demuxpacketcb_(packet, av_rescale_q_rnd(packet->pts, fmtctx_->streams[packet->stream_index]->time_base, { 1, 1 }, static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)), demuxpacketcbparam_);
                }
            }

            // 不再引用指向的缓冲区
            av_packet_unref(packet);
        }
        break;
    } while (true);

    // 清理bsf
    av_bsf_free(&bsfctx);
    // 清理packet
    av_packet_free(&packet);

    status_ = STOP;
    if (demuxstatuscb_ != nullptr)
    {
        demuxstatuscb_(STOP, err, demuxstatuscbparam_);
    }

    return true;
}

int CDemux::get_steam_index(AVMediaType type, std::string& err)
{
    TRYLOCK();
    err = "opt succeed.";

    int ret = av_find_best_stream(fmtctx_, type, -1, -1, nullptr, 0);
    UNLOCK();
    if (ret < 0)
    {
        err = av_err2str(ret);
        return -1;
    }
    else
    {
        return ret;
    }
} 

const AVCodecParameters* CDemux::get_steam_par(int index, std::string& err)
{
    TRYLOCK();
    const AVCodecParameters* par = nullptr;
    err = "opt succeed.";

    if (index < 0 || static_cast<unsigned int>(index) >= fmtctx_->nb_streams)
    {
        err = "stream index err.";
    }
    else
    {
        par = fmtctx_->streams[index]->codecpar;
    }
    UNLOCK();

    return par;
}

bool CDemux::seek(int64_t timestamp, int index, int flags, std::string& err)
{
    TRYLOCK();
    err = "opt succeed.";

    int ret = av_seek_frame(fmtctx_, index, av_rescale_q_rnd(timestamp, { 1, 1 }, fmtctx_->streams[index]->time_base, static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)), flags);
    UNLOCK();
    if (ret < 0)
    {
        err = av_err2str(ret);
        return false;
    }
    else
    {
        return true;
    }
}

bool CDemux::device_register_all(std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    avdevice_register_all();

    return true;
}

bool CDemux::set_input_format(const std::string& fmt, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    if (fmt.empty())
    {
        err = "fmt is empty.";
        return false;
    }
    else
    {
        fmt_ = av_find_input_format(fmt.c_str());
        if (fmt_ == nullptr)
        {
            err = "can not find fmt " + fmt;
            return false;
        }
    }

    return true;
}

bool CDemux::set_dic_opt(const std::string& key, const std::string& value, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    if (key.empty() || value.empty())
    {
        err = "input is empty.";
        return false;
    }

    CHECKFFRET(av_dict_set(&dic_, key.c_str(), value.c_str(), 0));

    return true;
}

bool CDemux::free_opt(std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    av_dict_free(&dic_);
    fmt_ = nullptr;

    return true;
}

bool CDemux::set_bsf_name(const std::string& bsf, std::string& err)
{
    LOCK();
    CHECKSTOP(err);
    err = "opt succeed.";

    bsfname_ = bsf;

    return true;
}

测试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 解封装
void test_demux()
{
    bool ret = false;
    std::string err;
    CDemux demux;
    
    ret = demux.set_input("in.flv", err);
    //ret = demux.set_input("in.h264", err);
    //ret = demux.set_input("in.aac", err);
    TESTCHECKRET(ret);

    ret = demux.set_demux_callback(DemuxPacketCB, &demux, err);
    TESTCHECKRET(ret);

    ret = demux.set_demux_status_callback(DemuxStatusCB, &demux, err);
    TESTCHECKRET(ret);

    ret = demux.set_bsf_name("h264_mp4toannexb", err);
    TESTCHECKRET(ret);

    ret = demux.openinput(err);
    TESTCHECKRET(ret);

    g_vindex = demux.get_steam_index(AVMEDIA_TYPE_VIDEO, err);
    std::cout << err << std::endl;
    g_aindex = demux.get_steam_index(AVMEDIA_TYPE_AUDIO, err);
    std::cout << err << std::endl;

    ret = demux.begindemux(err);
    TESTCHECKRET(ret);

    std::cout << "input to stop demuxing." << std::endl;
    std::cin.get();

    ret = demux.stopdemux(err);
    TESTCHECKRET(ret);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-07-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
gongluck
2019/06/15
1.1K0
FFmpeg4.0笔记:封装ffmpeg的视频帧转换功能类CSws
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
gongluck
2019/06/15
6200
FFmpeg4.0笔记:封装ffmpeg的音频重采样功能类CSwr
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
gongluck
2019/06/15
9130
ffmpeg实战实现音视频解封装!
大家好,很长一段时间没有继续更新ffmpeg的相关技术文章了,最近更多的时间和精力主要集中在给自己不断灌入新的知识,所以接下来只要有时间就会疯狂输出所学习到的技术干货!
用户6280468
2022/03/21
1.1K0
ffmpeg实战实现音视频解封装!
FFmpeg4.0笔记:rtsp2rtmp
Github https://github.com/gongluck/FFmpeg4.0-study.git #include <iostream> using namespace std; extern "C" { #include "libavformat/avformat.h" #include "libavutil/time.h" } #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avutil.lib") #pragma co
gongluck
2018/06/22
1.5K0
FFmpeg解封装实验!
大家好,我是txp,好久没有写技术原创文章了,久等了,记得之前写的播放器学习文章后面也没有继续更了;主要原因这段时间一个是由于工作的原因,二来主要是最近利用空闲时间看完了今年的第四本非技术书籍,目前又买了几本新的非技术书籍学习!在书上看到学到的知识,目前我都在总结输出!一些学习经验和工作当中的经验,我会慢慢录制视频出来,视频比文字更加生动(ps:因为之前录制的视频,不知道如何添加字幕,所以现在知道了,做就要认真做,就要把一件事情做好!)
用户6280468
2022/03/21
6400
FFmpeg解封装实验!
FFmpeg 实现视频 封装 与 解封装
本例子实现的是将视频数据和音频数据,按照一定的格式封装为特定的容器,比如FLV、MKV、MP4、AVI等等。
音视频开发进阶
2020/07/24
2.7K0
FFmpeg 实现视频 封装 与 解封装
FFmpeg4.0笔记:采集桌面
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
gongluck
2019/07/09
5940
FFmpeg 4.x 从入门到精通(一)—— QT 中如何用 FFmpeg 实现软件解码
因为在2021年给自己定了目标和计划,学习ffmpeg,所以这篇文章是实现计划的第一步。
全栈程序员站长
2022/09/13
1.3K0
FFmpeg封装格式处理4-转封装例程
源码修改自 FFmpeg 4.1 自带的例程 remuxing.c。代码非常简短:
叶余
2019/04/02
1.1K0
FFmpeg封装格式处理4-转封装例程
SDL2:封装媒体显示播放Csdl2
https://github.com/gongluck/SDL2-study/tree/master/Csdl2
gongluck
2019/06/22
5810
AVFormatContext封装层:理论与实战
AVFormatContext 是一个贯穿始终的数据结构,很多函数都用到它作为参数,是输入输出相关信息的一个容器,本文讲解 AVFormatContext 的封装层,主要包括两大数据结构:AVInputFormat,AVOutputFormat。
Gnep@97
2023/12/06
5200
AVFormatContext封装层:理论与实战
FFmpeg4.0+SDL2.0笔记01:Making Screencaps
背景:在系统性学习FFmpeg时,发现官方推荐教程还是15年的,不少接口已经弃用,大版本也升了一级,所以在这里记录下FFmpeg4.0+SDL2.0的学习过程。
非一
2021/04/06
6380
音视频八股文(11)-- ffmpeg avio 内存输入和内存输出。内存输出有完整代码,网上很少有的。
avio提供了一系列API,可以将数据从内存读取到缓冲区中,也可以将缓冲区中的数据写入到内存中。其实现依赖于IOContext结构体,该结构体定义了当前输入/输出事件的状态、数据、回调函数等信息,并支持通过自定义回调函数实现不同的输入/输出方式。
福大大架构师每日一题
2023/05/09
1.9K0
音视频八股文(11)-- ffmpeg avio 内存输入和内存输出。内存输出有完整代码,网上很少有的。
FFmpeg封装格式处理2-解复用例程
本例实现,将输入文件中的视频流和音频流分离出来,保存为单独的文件,所保存的文件是不含封装格式的裸流文件。
叶余
2019/04/02
1.1K0
FFmpeg封装格式处理2-解复用例程
FFMPEG音频视频开发: 开发本地视频播放器(单线程解码)
ffmpeg与x264源码开源,可以在任意平台上编译。自行下载源码编译得到库文件即可。
DS小龙哥
2022/01/12
1.4K0
FFMPEG音频视频开发: 开发本地视频播放器(单线程解码)
FFmpeg简单转码程序--视频剪辑
学习了雷神的文章,慕斯人分享精神,感其英年而逝,不胜唏嘘。他有分享一个转码程序《最简单的基于FFMPEG的转码程序》其中使用了filter(参考了ffmpeg.c中的流程),他曾说想再编写一个不需要filter的版本,可惜未有机会。恰好工作中有相关ffmpeg处理内容,故狗尾续貂,撰写本文。
jacker
2018/09/01
8.3K0
FFmpeg简单转码程序--视频剪辑
基于FFmpeg进行RTMP推流(一)简介
这里的bin、include、lib就是我们刚才在FFmpeg下载的相关文件。 src是我们的项目源码目录。 新建Win32控制台应用程序、选择位置、项目名称。注意:去掉“为结局方案创建目录”的勾选
用户2929716
2018/08/23
14.1K0
基于FFmpeg进行RTMP推流(一)简介
FFmpeg4.0笔记:本地媒体文件解码、帧格式转换、重采样、编码、封装等例子
https://github.com/gongluck/FFmpeg4.0-study/blob/master/official%20example/my_example.cpp
gongluck
2019/05/22
2.5K0
QT应用编程: 基于FFMPEG设计的流媒体播放器(播放rtmp视频流)
使用的FFMPEG库版本下载地址:https://download.csdn.net/download/xiaolong1126626497/12304729
DS小龙哥
2022/01/07
4.2K2
QT应用编程: 基于FFMPEG设计的流媒体播放器(播放rtmp视频流)
推荐阅读
相关推荐
FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验