FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux
程序员文章站
2023-11-30 16:29:22
Github https://github.com/gongluck/FFmpeg4.0 study/tree/master/Cff CDemux.h CDemux.cpp C++ / Copyright(c) 2019 All rights reserved. 文件名称: CDemux.cpp 简 ......
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); }
上一篇: 深入Nginx + PHP 缓存详解
下一篇: 基于PHP中的常用函数回顾