FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux
Github
https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff
CDemux.h
/*******************************************************************
*  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
/*******************************************************************
*  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;
}
测试
// 解封装
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);
}
FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux的更多相关文章
- FFmpeg4.0笔记:封装ffmpeg的音频重采样功能类CSwr
		Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CSwr.h /************************* ... 
- FFmpeg(二) 解封装相关函数理解
		一.解封装基本流程 ①av_register_All()////初始化解封装,注册解析和封装的格式. ②avformat_netword_init()//初始化网络,解析rtsp协议 ③avforma ... 
- FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode
		Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CDecode.h /********************** ... 
- FFmpeg4.0笔记:封装ffmpeg的视频帧转换功能类CSws
		Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CSws.h /************************* ... 
- FFmpeg4.0笔记:本地媒体文件解码、帧格式转换、重采样、编码、封装、转封装、avio、硬解码等例子
		Github https://github.com/gongluck/FFmpeg4.0-study/blob/master/official%20example/my_example.cpp #in ... 
- FFmpeg4.0笔记:rtsp2rtmp
		Github https://github.com/gongluck/FFmpeg4.0-study.git #include <iostream> using namespace std ... 
- FFmpeg4.0笔记:file2rtmp
		Github: https://github.com/gongluck/FFmpeg4.0-study.git #include <iostream> using namespace st ... 
- FFmpeg4.0笔记:VS2019编译FFmpeg4.0源码
		0.下载TDM.msys和yasm 1.安装TDM-GCC-64 2.安装msys到TDM-GCC的安装目录中 3.将call "C:\Program Files (x86)\Microso ... 
- FFmpeg4.0笔记:采集系统声音
		Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff // 采集系统声音 void test_systemsound() ... 
随机推荐
- CISCO实验记录四:备份路由器的IOS
			1.配置好TFTP服务器(假设ip为192.168.2.1) 2.查看当前IOS名称 #show version 输出中有一段:System image file is "bootflash ... 
- Nginx-HTTP之ngx_http_top_body_filter
			1. ngx_http_top_body_filter 该链表用于构造响应消息的响应正文. 大致有以下模块在该链表中插入了自己的函数: ngx_http_range_filter_module: ng ... 
- 第11组 团队Git现场编程实战
			第11组 团队Git现场编程实战 组员职责分工: 前端部分: 陈郑铧:构架的搭建,前端模块开发 陈益:前端模块开发 李镇平:前端模块开发 后端部分: 沈国煜:后端模块开发 王泽鸿:后端模块开发 林铮威 ... 
- pytorch-cifar10分类网络结构
			cifar10主要是由32x32的三通道彩色图, 总共10个类别,这里我们使用残差网络构造网络结构 网络结构: 第一层:首先经过一个卷积,归一化,激活 32x32x16 -> 32x32x16 ... 
- 基于Python使用scrapy-redis框架实现分布式爬虫
			1.首先介绍一下:scrapy-redis框架 scrapy-redis:一个三方的基于redis的分布式爬虫框架,配合scrapy使用,让爬虫具有了分布式爬取的功能.github地址: https: ... 
- java 语言实现豆瓣电影信息查询
			豆瓣上面有很多电影,有时候要查看个电影信息,去豆瓣搜下还是很方便的,但是如何通过接口的形式来查看豆瓣电影,这对于很多网站.app其实是非常实用的功能,这里笔者附上一个java实现的豆瓣电影信息获取的代 ... 
- [ML] LIBSVM Data: Classification, Regression, and Multi-label
			数据库下载:LIBSVM Data: Classification, Regression, and Multi-label 一.机器学习模型的参数 模型所需的参数格式,有些为:LabeledPoin ... 
- SSH客户端神器之 MobaXterm
			SSH客户端神器之 MobaXterm 由于需要连接远程 Linux 服务器,早期使用过 Putty,SecureCRT,后面主要使用 Xshell. 自从接触了 MobaXterm之后,个人感觉比 ... 
- 时间复杂度O(n)
			时间复杂度 算法分析 同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率.算法分析的目的在于选择合适算法和改进算法.一个算法的评价主要从时间复杂度和空间复杂度来考虑. 一.时间复 ... 
- 使用rabbit mq.模拟dubbo,使MQ异步调用代码写起来像是同步方法.
			最近在改造老系统,遇到了需要使用rabbitMq的场景.在以前使用的过程中需要在发送端和消费端各种配置,感觉比较麻烦,然后突然想到了dubbo中@Reference注解的形式,可不可以做一个类似的架子 ... 
