当前位置: 首页 > news >正文

使用FFmpeg实现简单的拉流、推流、视频解码Demo

使用FFmpeg实现一个RTSP拉流、RTMP推流及视频解码的Demo可以分为几个主要步骤。首先,利用avformat_open_input函数打开RTSP流并使用avformat_find_stream_info解析流信息。接着,选择视频流并使用avcodec_find_decoder找到合适的解码器,使用avcodec_open2打开解码器。

然后,通过av_read_frame循环读取每一帧数据,判断是否为视频帧,如果是,则调用avcodec_send_packetavcodec_receive_frame进行解码,得到原始的YUV数据。

同时,为了实现RTMP推流,需要创建一个新的RTMP输出上下文,通过avformat_alloc_output_context2创建RTMP推流上下文,并配置输出流参数。将解码后的帧编码为RTMP流格式后,通过av_write_frame将数据推送到RTMP服务器。

该Demo展示了如何通过FFmpeg处理多媒体流,实现从RTSP拉流到RTMP推流的功能,附带了视频的解码、没有编码操作,编码反之亦然

首先是头文件--------Streamer.h

#ifndef NEW_STREAMER_H
#define NEW_STREAMER_Hextern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavformat/avformat.h>
#include <libavutil/dict.h>
}
#include <memory>
#include <string>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>class Streamer {
public:typedef struct {int nCallBackNum = 0;int nExit = 0;int nOpenTimeOut = 0;time_t m_tStart;bool beFirst = true;} avformat_open_input_Runner;Streamer();~Streamer();bool Start(const std::string& pull_url, const std::string& push_url);private:bool openInput();bool openOutPut();bool initVideoCodecInfo();void decodePacket();bool pushVideo();bool readFrame();void threadFunc();void pushPacket(std::unique_ptr<AVPacket> pkt);std::unique_ptr<AVPacket> popPacket();static int interrupt_callback(void* ctx);private:std::unique_ptr<AVFormatContext> pInputCtx_;std::unique_ptr<AVCodecContext> pCodecCtx_;std::unique_ptr<AVFormatContext> pOutCtx_;std::unique_ptr<AVPacket> pCurPacket_;AVCodec* codec_;avformat_open_input_Runner m_Runner_;bool bRun_;bool bRunDecode_;bool bRunRead_;bool bOpenPush_;std::int32_t timebase_num_;std::int32_t timebase_den_;std::thread streamThread_;std::thread decodeThread_;std::string strInputUrl_;std::string strOutUrl_;std::int32_t vIndex_;std::mutex mutex_;std::queue<std::unique_ptr<AVPacket>> qPacket_;std::queue<AVFrame*> qFrame_;std::condition_variable cond_;std::int64_t last_dts_;
};#endif

源文件Streamer.cpp

#include <iostream>
#include "Streamer.h"Streamer::Streamer() {bRun_ = false;bRunDecode_ = false;bOpenPush_ = false;bRunRead_ = false;last_dts_ = 0;
}Streamer::~Streamer() {bRun_ = false;bRunDecode_ = false;bOpenPush_ = false;bRunRead_ = false;last_dts_ = 0;if (streamThread_.joinable()) {streamThread_.join();}if (decodeThread_.joinable()) {decodeThread_.join();}
}bool Streamer::Start(const std::string& pull_url, const std::string& push_url) {av_register_all();avformat_network_init();strInputUrl_ = pull_url;strOutUrl_ = push_url;bRun_ = true;streamThread_ = std::thread(&Streamer::threadFunc, this);decodeThread_ = std::thread(&Streamer::decodePacket, this);return true;
}int Streamer::interrupt_callback(void* ctx) {avformat_open_input_Runner* input = (avformat_open_input_Runner*) ctx;int nTimeOut = input->nOpenTimeOut;if (input->beFirst) {if (time(nullptr) - input->m_tStart >= nTimeOut) {// 等待超过10s则中断input->nExit = 1;}}return input->nExit;
}bool Streamer::openInput() {AVDictionary* pTmp = nullptr;//av_dict_set(&pTmp, "rtsp_transport", "tcp", 0);//av_dict_set(&pTmp, "stimeout", "5000000", 0); // 设置超时15秒//av_dict_set(&pTmp, "maxdelay", "5000000", 0); // 设置最大时延//av_dict_set(&pTmp, "flvflags", "no_duration_filesize", 0);//av_dict_set(&pTmp, "buffer_size", "10240000", 0); // 设置缓存大小,1080p可将值调大//av_dict_set(&pTmp, "tcp_nodelay", "true", 0);AVFormatContext* Ctx{};AVPacket* packet = nullptr;Ctx = avformat_alloc_context();packet = av_packet_alloc();if (Ctx == nullptr || packet == nullptr) {std::cout << "avformat_alloc_context failed" << std::endl;return false;}pCurPacket_.reset(packet);Ctx->interrupt_callback.callback = interrupt_callback;Ctx->interrupt_callback.opaque = &m_Runner_;m_Runner_.nCallBackNum = 0;m_Runner_.nExit = 0;m_Runner_.nOpenTimeOut = 10;m_Runner_.m_tStart = time(NULL);m_Runner_.beFirst = true;if (avformat_open_input(&Ctx, strInputUrl_.c_str(), nullptr, &pTmp) < 0) {std::cout << "avformat_open_input failed, Input Url:" << strInputUrl_ << std::endl;return false;}Ctx->max_analyze_duration = 5 * AV_TIME_BASE;Ctx->probesize = 100 * 1024;std::cout << "start avformat_find_stream_info" << std::endl;if (avformat_find_stream_info(Ctx, nullptr) < 0) {std::cout << "avformat_find_stream_info failed" << std::endl;return false;}m_Runner_.beFirst = false;std::cout << "end avformat_find_stream_info" << std::endl;AVCodec* codec{};auto VIndex = av_find_best_stream(Ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);if (VIndex < 0) {std::cout << "av_find_best_stream failed" << std::endl;return false;}vIndex_ = VIndex;auto&& stream = Ctx->streams[vIndex_];timebase_num_ = stream->time_base.num;timebase_den_ = stream->time_base.den;pInputCtx_.reset(Ctx);return true;
}bool Streamer::openOutPut() {AVFormatContext* OutCtx{};avformat_alloc_output_context2(&OutCtx, nullptr, "flv", strOutUrl_.c_str());if (avio_open2(&OutCtx->pb, strOutUrl_.c_str(), AVIO_FLAG_WRITE, nullptr, nullptr) < 0) {std::cout<<"avio_open2 Failed, Push Url: " << strOutUrl_<< std::endl;return false;}pOutCtx_.reset(OutCtx);if (!initVideoCodecInfo()) {std::cout << "initVideoCodecInfo Failed" << std::endl;return false;}auto nRet = avformat_write_header(pOutCtx_.get(), nullptr);if (nRet != AVSTREAM_INIT_IN_WRITE_HEADER && nRet != AVSTREAM_INIT_IN_INIT_OUTPUT) {std::cout << "avformat_write_header Failed" << std::endl;return false;}return true;
}bool Streamer::initVideoCodecInfo() {codec_ = (AVCodec*) avcodec_find_decoder(pInputCtx_->streams[vIndex_]->codecpar->codec_id);if (!codec_) {std::cout << "avcodec_find_decoder Failed" << std::endl;return false;}AVStream* pVideoStream = avformat_new_stream(pOutCtx_.get(), codec_);if (!pVideoStream) {std::cout << "avformat_new_stream Failed" << std::endl;return false;}AVCodecContext* CodecCtx{};CodecCtx = avcodec_alloc_context3(codec_);if (!CodecCtx) {std::cout << "avcodec_alloc_context3 Failed" << std::endl;return false;}if (avcodec_parameters_to_context(CodecCtx, pInputCtx_->streams[vIndex_]->codecpar) < 0) {std::cout << "avcodec_parameters_to_context Failed" << std::endl;return false;}if (pOutCtx_->oformat->flags & AVFMT_GLOBALHEADER) {CodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}if (avcodec_open2(CodecCtx, codec_, nullptr) < 0) {std::cout << "avcodec_open2 Failed" << std::endl;return false;}if (avcodec_parameters_from_context(pVideoStream->codecpar, CodecCtx) < 0) {std::cout << "avcodec_parameters_from_context Failed" << std::endl;return false;}pCodecCtx_.reset(CodecCtx);return true;
}void Streamer::decodePacket() {std::cout << "Start decode thread" << std::endl;bRunDecode_ = true;while (bRunDecode_) {auto pkt = popPacket();if (pkt->data) {AVPacket* clonePacket = av_packet_alloc();        av_packet_ref(clonePacket, pkt.get());if (!clonePacket){std::cout << "clonePacket is nullptr" << std::endl;}if (avcodec_send_packet(pCodecCtx_.get(), clonePacket) < 0) {std::cout << "avcodec_send_packet Failed" << std::endl;return;}for (;;) {AVFrame* frame = av_frame_alloc();auto nRet = avcodec_receive_frame(pCodecCtx_.get(), frame); // 从解码器中获取被解码后的帧数据AVFrameif (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF) {break;} else if (nRet < 0) {std::cout << "avcodec_receive_frame Failed" << std::endl;return;}av_frame_unref(frame);}av_packet_unref(clonePacket);}av_packet_unref(pkt.get());}
}bool Streamer::pushVideo() {if (pCurPacket_->dts <= 0) {/*std::cout << "Packet PTS: " << pCurPacket_->pts << "Packet DTS: " << pCurPacket_->dts<< "Packet Duration: " << pCurPacket_->duration << std::endl;*/return true;}auto&& input_timebase = pInputCtx_->streams[vIndex_]->time_base; auto&& output_timebase = pOutCtx_->streams[vIndex_]->time_base;// 修复首帧错误时间戳。if (pCurPacket_->dts < last_dts_) {pCurPacket_->dts = last_dts_;}last_dts_ = pCurPacket_->dts;if (pCurPacket_->pts < pCurPacket_->dts) {pCurPacket_->pts = pCurPacket_->dts;}// 重整时间戳。pCurPacket_->pts = av_rescale_q_rnd(pCurPacket_->pts, input_timebase, output_timebase,static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pCurPacket_->dts = av_rescale_q_rnd(pCurPacket_->dts, input_timebase, output_timebase,static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pCurPacket_->duration = av_rescale_q(pCurPacket_->duration, input_timebase, output_timebase);pCurPacket_->pos = -1;// 提供合理的推流延时。auto pts_time = av_rescale_q(pCurPacket_->dts, output_timebase, AVRational{1, 1000});auto now_time = av_gettime() / 1000;if (pts_time > now_time) {av_usleep(static_cast<std::uint32_t>(pts_time - now_time));}auto nRet = av_interleaved_write_frame(pOutCtx_.get(), pCurPacket_.get());if (nRet < 0) {std::cout << "av_interleaved_write_frame Failed, Error Num:" << nRet << " Url: " << strOutUrl_ << std::endl;return false;}return true;
}bool Streamer::readFrame() {std::cout << "start readFrame" << std::endl;while (bRunRead_) {av_packet_unref(pCurPacket_.get());auto nRet = av_read_frame(pInputCtx_.get(), pCurPacket_.get());if (nRet == AVERROR_EOF || nRet == AVERROR_EXIT) {return false;}if (nRet < 0 || pCurPacket_->size < 0) {std::cout << "av_read_frame Failed" << std::endl;return false;}if (pCurPacket_->stream_index != vIndex_) {continue;}//if (pCurPacket_->stream_index == vIndex_ && pCurPacket_->flags == AV_PKT_FLAG_KEY && bOpenPush_) {//    auto clonePacket = std::make_unique<AVPacket>();//    av_packet_ref(clonePacket.get(), pCurPacket_.get());//    pushPacket(std::move(clonePacket));//    //decodePacket();//}if (pCurPacket_->stream_index == vIndex_ && bOpenPush_) {auto clonePacket = std::make_unique<AVPacket>();av_packet_ref(clonePacket.get(), pCurPacket_.get());pushPacket(std::move(clonePacket));}if (!bOpenPush_) {if (!openOutPut()) {std::cout<<"OpenInput Failed, Push Url:" << strOutUrl_<< std::endl;return false;}bOpenPush_ = true;}if (pCurPacket_->stream_index == vIndex_) {if (!pushVideo()) {std::cout << "PushVideo Failed" << std::endl;return false;}}}return true;
}void Streamer::threadFunc() {int nErrorCount = 0;while (bRun_) {nErrorCount++;if (nErrorCount == 10) {return;}if (!openInput()) {std::cout << "OpenInput Failed, RTSP:{}" << strInputUrl_ << std::endl;continue;}bRunRead_ = true;if (!readFrame()) {continue;}}
}void Streamer::pushPacket(std::unique_ptr<AVPacket> pkt) {std::lock_guard<std::mutex> lock(mutex_);qPacket_.push(std::move(pkt));cond_.notify_one();
}std::unique_ptr<AVPacket> Streamer::popPacket() {std::unique_lock<std::mutex> lock(mutex_);cond_.wait(lock, [this]() { return !qPacket_.empty(); });auto pkt = std::move(qPacket_.front());qPacket_.pop();return pkt;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • CoreDownload - WordPress文章下载增强插件v1.0.6
  • C++代码片段
  • 【Redis】redis5种数据类型(哈希)
  • 正点原子阿尔法ARM开发板-IMX6ULL(三)——汇编LED驱动实验-上
  • docker conda
  • 【Postgresql】地理空间数据的存储与查询,查询效率优化策略,数据类型与查询速度的影响
  • 国家商用密码算法——SM4、SM7、SM9
  • 一区霜冰算法+双向深度学习模型+注意力机制!RIME-BiTCN-BiGRU-Attention
  • C语言 ——— 学习并使用条件编译指令
  • 【佳学基因检测】如何使用Letsencrypt对一个网站进行加密?
  • 【Python常用库_1】网络安全清洁专家——Bleach
  • TiDB-从0到1【完结】
  • 细致刨析JDBC ① 基础篇
  • Windows10 如何配置python IDE
  • Apple Intelligence深夜炸场!苹果发布4颗自研芯片,iPhone/iWatch/AirPods大升级
  • 深入了解以太坊
  • php的引用
  • 【知识碎片】第三方登录弹窗效果
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • Babel配置的不完全指南
  • Create React App 使用
  • DOM的那些事
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • isset在php5.6-和php7.0+的一些差异
  • JS变量作用域
  • Linux CTF 逆向入门
  • PermissionScope Swift4 兼容问题
  • PHP的类修饰符与访问修饰符
  • Service Worker
  • Vue ES6 Jade Scss Webpack Gulp
  • 安装python包到指定虚拟环境
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 近期前端发展计划
  • 突破自己的技术思维
  • 异常机制详解
  • 用 Swift 编写面向协议的视图
  • 源码安装memcached和php memcache扩展
  • ​ssh免密码登录设置及问题总结
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • # 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (Note)C++中的继承方式
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (蓝桥杯每日一题)love
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)【Hibernate总结系列】使用举例
  • .NET 5种线程安全集合
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET MVC第三章、三种传值方式
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)