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() ...
随机推荐
- ios端,input框,汉字输入不上问题
input{ -webkit-transform: translate3d(, , ); } 在input框上加上这段代码就可以了 另外,我在一个页面上,用一个开关去控制一部分内容显示隐藏与显示时,当 ...
- [转载]作为理工科学生,我们为什么要练就好的文笔?我们需要发blog来记录学习历程?
文/JoeyChen 工程师该怎样才能突破自己的能力瓶颈?写 blog! 工程师该怎样精进自己在职涯上所需要的能力?写 blog! 工程师该怎样才能保持学习与成长的动能?写 blog! 工程师该怎样才 ...
- Liunx反弹shell的几种方式
什么是反弹shell? 简单理解,通常是我们主动发起请求,去访问服务器(某个IP的某个端口),比如我们常访问的web服务器:http(https)://ip:80,这是因为在服务器上面开启了80端口的 ...
- Tomcat配置 https SSL证书
HTTP与HTTPS的区别:HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Soc ...
- linux下查看tomcat的日志
工作期间有碰到服务器日志相关的,需要看tomcat运行日志,简单搜了下,摘为随笔,以供参考 一种是利用docker查看 1.使用dockerdocker logs -f -t --since=&quo ...
- 外网访问内网的FTP服务器
转自 外网访问内网的FTP服务器 首先感谢作者给出的总结,原文是介绍Serv-U的,我针对FileZilla Server进行了稍微修改,仅看操作可直接跳到分割线后第3部分. 1. 背景简介最近研究如 ...
- iTerm2使用Profiles自动登录
http://blog.csdn.net/wandershi/article/details/75088310 1.创建Profiles文件 cd ~/.ssh/iTerm2SSH/ vi 172.1 ...
- Filebeat使用
1.Beat家族 Beats可以直接(或者通过Logstash)将数据发送到Elasticsearch,在那里你可以进一步处理和增强数据,然后在Kibana中将其可视化. 2.FileBeat安装 2 ...
- echarts中国地图3D各个城市标点demo
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <meta ...
- HearthBuddy 日志模块
// Triton.Common.LogUtilities.CustomLogger // Token: 0x04000BD8 RID: 3032 private Level level_0 = Le ...