以下为基本推流示意图

首先,rtsp 协议有什么好处呢? 相比与rtmp 协议来说,他更为复杂,rtmp协议的好处是比较单一,就是基于tcp协议做的,当然,完全可以修改为udp 协议来做,不过,我们最需要的是实用,如果做创新去使用udp 来做rtmp协议,可以,但是不那么直接,完全可以创新一种协议而不用rtmp,rtsp。 rtsp既可以使用tcp,也可以使用udp协议,所以创新rtmp使用udp,我简单地认为,不如直接使用rtsp协议了。rtsp协议是国际标准,里面使用了sdp协议, rtp协议,rtcp协议,每一样都包含了很多需要学习的东西, sdp协议叫做会话描述协议, rtp协议叫做实时传输协议,rtcp为实时传输控制协议。
SDP全称是Session Description Protocol,翻译过来就是描述会话的协议。主要用于两个会话实体之间的媒体协商。这个知识要读者自行增加一些
v 表示版本;
o 表示用户、会话ID、会话版本、网络类型、地址类型、地址;
s 会话名称 session的简写
u 会话信息地址;
e Email 地址;
p 电话信息;
c 连接信息 (IN IP4 0.0.0.0) 如果包含在所有媒体中,则不需要该字段;
b 带宽限制;
b=as:150;
b=RR:11250;
b=RS:2750;
m= (media name and transport address) # 媒体名称和传输地址
i=* (media title) # 媒体标题
c=* (connection information - optional if included at session-level) # 连接信息 — 如果包含在会话层则该字段可选
k=* (encryption key) # 加密密钥
a=* (zero or more media attribute lines) #0 个或多个会话属性行
实时传输协议,最重要的就是理解 包大小和mtu大小,理解了才能知道为什么这个才能是实时传输而不是其他协议,按理来说,我自己做的协议也可以实时传输啊,不是这样的,要理解透彻才行
实时传输控制协议,这个也比较麻烦,实时上,我一直认为这个协议用处不大,但是你偏偏要理解,为什么,很多服务器没有这个,就相当于断了心跳,就会停止传输,尤其是udp 上的 rtsp, 信令虽然是tcp,但是传输层使用的是udp,这样什么时候断了不知道,所以就会加上这个协议,问题是加上rtcp以后又加重了网络传输,cpu 和 带宽同时加重,这是个矛盾,rtmp 没有这个问题。
读者自己制作了rtmp 协议的服务器,在理解的过程中,他的trunk概念本身具有rtp协议的特性, 而且由于简单,就是基于tcp,按照ip 不分包的trunk 来说,小于64k 大小就能,如果按照存储来说,完全可以做到4096,也就是4k来分包,适应操作系统的存储块大小。我这里不细说,细说无益,读者必须自己去深刻理解。
如果要完整地自己写完各种协议,需要的时间不言而喻,我们可以简单地先调用ffmpeg的方式去推流和取流,客户端可以这样,但是服务端就不能了,服务端必须自己实现这些协议和解析协议。
bool RGB_2_YUV(){
//BGR 转 YUV
swrCtxBGRA2YUV = sws_getContext(
cap_w, cap_h, AV_PIX_FMT_BGR,
cap_w, cap_h, AV_PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL, NULL, NULL
);
//创建BGRA帧
frame_bgra = av_frame_alloc();
frame_bgra->format = AV_PIX_FMT_BGR;
frame_bgra->width = cap_w;
frame_bgra->height = cap_h;
if (av_frame_get_buffer(frame_bgra, 24) < 0) {
printf("Failed: av_frame_get_buffer\n");
return false;
}
frame_bgra->data[0] = cropImage;
//YUV帧
frame_yuv = av_frame_alloc();
frame_yuv->width = cap_w;
frame_yuv->height = cap_h;
frame_yuv->format = AV_PIX_FMT_YUV420P;
//
uint8_t *picture_buf = (uint8_t *)av_malloc(cap_w * cap_h * 1.5);
if (av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, picture_buf, AV_PIX_FMT_YUV420P, cap_w, cap_h, 1) < 0){
printf("Failed: av_image_fill_arrays\n");
return false;
}
return true;
}
//BGR 转 YUV
if (sws_scale(swrCtxBGR2YUV,
frame_bgra->data, frame_bgra->linesize,
0, cap_h,
frame_yuv->data, frame_yuv->linesize) < 0)
{
printf("fail\n");
return;
}
frame_yuv->pts = av_gettime();
frame_yuv->pts设为当前的时间戳,注意rtsp协议不能这样做,rtsp协议里的rtp协议必须保证是90000的时间基。
2. H264编码
3. bool YUV_to_H264(){
//寻找编码器
codec_h264 = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec_h264){
printf("Fail: avcodec_find_encoder\n");
return false;
}
//编码器上下文
codec_ctx_h264 = avcodec_alloc_context3(codec_h264);
if (!codec_ctx_h264){
printf("Fail: avcodec_alloc_context3\n");
return false;
}
codec_ctx_h264->pix_fmt = AV_PIX_FMT_YUV420P;
codec_ctx_h264->codec_type = AVMEDIA_TYPE_VIDEO;
codec_ctx_h264->width = cap_w;
codec_ctx_h264->height = cap_h;
codec_ctx_h264->channels = 3;
codec_ctx_h264->time_base = { 1, fps};
codec_ctx_h264->gop_size = fps; //关键帧(I帧)的距离
codec_ctx_h264->max_b_frames = 0;
codec_ctx_h264->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //添加PPS、SPS
av_opt_set(codec_ctx_h264->priv_data, "preset", "ultrafast", 0); //快速编码,但会损失质量
av_opt_set(codec_ctx_h264->priv_data, "tune", "zerolatency", 0);
//打开编码器
if (avcodec_open2(codec_ctx_h264, codec_h264, NULL) < 0){
printf("Fail: avcodec_open2\n");
return false;
}
pkt_h264 = av_packet_alloc();
return true;
}
ret = avcodec_send_frame(codec_ctx_h264, frame_yuv);
if (ret < 0){
printf("send frame fail\n");
return;
}
while (ret >= 0) {
ret = avcodec_receive_packet(codec_ctx_h264, pkt_h264);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
break;
}
if (ret < 0){
printf("Error during encoding\n");
break;
}
pkt_h264->stream_index = videoindex;
//printf("pkt_h264 timestamp = %d\n", pkt_h264->pts);
if (av_interleaved_write_frame(fmt_ctx, pkt_h264) < 0) {
printf("Error muxing packet\n");
}
av_packet_unref(pkt_h264);
}
bool PCM_to_AAC(){
codec_aac = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec_aac) {
printf("avcodec_find_encoder fail\n");
return false;
}
codec_ctx_aac = avcodec_alloc_context3(codec_aac);
if (!codec_ctx_aac) {
printf("avcodec_find_encoder fail\n");
return false;
}
codec_ctx_aac->sample_fmt = AV_SAMPLE_FMT_FLTP;
codec_ctx_aac->codec_type = AVMEDIA_TYPE_AUDIO;
codec_ctx_aac->channels = channels;
codec_ctx_aac->channel_layout = av_get_default_channel_layout(channels);
codec_ctx_aac->sample_rate = sample_rete;
if (avcodec_open2(codec_ctx_aac, codec_aac, NULL) < 0) {
printf("open codec fail\n");
return false;
}
swrCtxS162FLTP = swr_alloc_set_opts(NULL,
codec_ctx_aac->channel_layout, codec_ctx_aac->sample_fmt, codec_ctx_aac->sample_rate,
codec_ctx_aac->channel_layout, AV_SAMPLE_FMT_S16, codec_ctx_aac->sample_rate,
0, 0);
if (!swrCtxS162FLTP)
{
printf("swr_alloc_set_opts error\n");
return false;
}
if (swr_init(swrCtxS162FLTP) < 0) {
printf("open resample fail\n");
return false;
}
frame_pcm = av_frame_alloc();
frame_pcm->nb_samples = nbSamples_; //一帧音频存放的样本数量
frame_pcm->format = codec_ctx_aac->sample_fmt;
frame_pcm->channels = codec_ctx_aac->channels;
frame_pcm->channel_layout = codec_ctx_aac->channel_layout;
if (av_frame_get_buffer(frame_pcm, 0) < 0) {
printf("av_frame_get_buffer error\n");
return false;
}
pkt_aac = av_packet_alloc();
return true;
}
pcm_buff是包含pcm数据的数组
const uint8_t *pcm[1];
pcm[0] = pcm_buff;
int len = swr_convert(swrCtxS162FLTP,
frame_pcm->data, frame_pcm->nb_samples,
pcm, nbSamples_);
if (len <= 0) {
printf("---Encodec:PCM->AAC--- swr_convert fail \n");
return;
}
frame_pcm->pts = av_gettime();
//printf("channels = %d\n", frame_pcm->channels);
//printf("framePCM->linesize = %6d %6d\n", frame_pcm->linesize[0], frame_pcm->linesize[1]);
//AAC编码
int ret = avcodec_send_frame(codec_ctx_aac, frame_pcm);
if (ret < 0){
printf("send frame fail\n");
return;
}
ret = avcodec_receive_packet(codec_ctx_aac, pkt_aac);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
return;
}
if (ret < 0){
printf("Error during encoding\n");
return;
}
pkt_aac->stream_index = audioindex;
//printf("pkt_aac timestamp = %d\n", pkt_aac->pts);
if (av_interleaved_write_frame(fmt_ctx, pkt_aac) < 0) {
printf("Error muxing packet\n");
}
av_packet_unref(pkt_aac);
bool init_rtsp_pusher(){
//RTSP
if (avformat_alloc_output_context2(&fmt_ctx, NULL, "RTSP", RTSP_URL.c_str()) < 0){
printf("Fail: avformat_alloc_output_context2\n");
return false;
}
//传输层使用tcp协议
av_opt_set(fmt_ctx->priv_data, "rtsp_transport", "tcp", 0);
//没有数据会等待max_interleave_delta微秒,这个最好不要使用,不是很好
fmt_ctx->max_interleave_delta = 1000000;
//输出视频流
AVStream *video_s = avformat_new_stream(fmt_ctx, codec_h264);
if (!video_s){
printf("Fail: avformat_new_stream\n");
return false;
}
video_s->time_base = { 1, fps };
videoindex = video_s->id = fmt_ctx->nb_streams - 1;
//复制AVCodecContext的设置
if (avcodec_copy_context(video_s->codec, codec_ctx_h264) < 0) {
printf("Fail: avcodec_copy_context\n");
return false;
}
video_s->codec->codec_tag = 0;
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
video_s->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
avcodec_parameters_from_context(video_s->codecpar, codec_ctx_h264);
//输出音频流
AVStream *audio_s = avformat_new_stream(fmt_ctx, codec_ctx_aac->codec);
if (!audio_s){
printf("Fail: avformat_new_stream\n");
return false;
}
audio_s->time_base = { 1, 25 };
audioindex = audio_s->id = fmt_ctx->nb_streams - 1;
//复制AVCodecContext的设置
if (avcodec_copy_context(audio_s->codec, codec_ctx_aac) < 0) {
printf("Fail: avcodec_copy_context\n");
return false;
}
audio_s->codec->codec_tag = 0;
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
audio_s->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
avcodec_parameters_from_context(audio_s->codecpar, codec_ctx_aac);
//printf("fmt_ctx nb_streams = %d\n", fmt_ctx->nb_streams);
av_dump_format(fmt_ctx, 0, fmt_ctx->filename, 1);
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) { //???
//打开输出URL(Open output URL)
if (avio_open(&fmt_ctx->pb, fmt_ctx->filename, AVIO_FLAG_WRITE) < 0) {
printf("Fail: avio_open('%s')\n", fmt_ctx->filename);
return false;
}
}
return true;
}
cmake_minimum_required(VERSION 3.5)
project(rtsp LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5Core)
set(FFMPEG_PREFIX_PATH /path/to/FFmpeg-n5.0.1/install)
include_directories(
${FFMPEG_PREFIX_PATH}/include/
)
link_directories(
${FFMPEG_PREFIX_PATH}/lib/ )
add_executable(rtsp
main.cpp
)
target_link_libraries(rtsp avcodec avformat avfilter avutil swresample swscale swscale )
和rtmp取流基本类似,需要可以修改
#include <iostream>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
}
int main(int argc, char *argv[])
{
int status_error_=-1;
std::string videourl= "rtsp://admin:Admin12345@192.168.3.64:554/Streaming/Channels/1";
AVFormatContext *pFormatCtx = NULL;
AVDictionary *options = NULL;
AVPacket *av_packet = NULL; // AVPacket暂存解码之前的媒体数据
avformat_network_init();
//执行网络库的全局初始化。
//此函数仅用于解决旧版GNUTLS或OpenSSL库的线程安全问题。
//一旦删除对较旧的GNUTLS和OpenSSL库的支持,此函数将被弃用,并且此函数将不再有任何用途。
av_dict_set(&options, "buffer_size", "4096000", 0); //设置缓存大小
av_dict_set(&options, "rtsp_transport", "tcp", 0); //以tcp的方式打开,
av_dict_set(&options, "stimeout", "5000000", 0); //设置超时断开链接时间,单位us, 5s
av_dict_set(&options, "max_delay", "500000", 0); //设置最大时延
pFormatCtx = avformat_alloc_context(); //用来申请AVFormatContext类型变量并初始化默认参数,申请的空间
//打开网络流或文件流
if (avformat_open_input(&pFormatCtx, videourl.c_str(), NULL, &options) != 0)
{
std::cout << "Couldn't open input stream.\n"
<< std::endl;
return status_error_;
}
//获取视频文件信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
std::cout << "Couldn't find stream information."<< std::endl;
return status_error_;
}
std::cout << "av_dict_get:" << std::endl;
AVDictionaryEntry *tag = NULL;
//av_dict_set(&pFormatCtx->metadata, "rotate", "0", 0);这里可以设置一些属性
while ((tag = av_dict_get(pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
{
std::string key = tag->key;
std::string value = tag->value;
std::cout << "av_dict_get:" << key << ":" << value << std::endl;
}
//查找码流中是否有视频流
int videoindex = -1;
unsigned i = 0;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
break;
}
}
if (videoindex == -1)
{
std::cout << "Didn't find a video stream.\n"
<< std::endl;
return status_error_;
}
av_packet = (AVPacket *)av_malloc(sizeof(AVPacket));
while (true)
{
if (av_read_frame(pFormatCtx, av_packet) >= 0)
{
if (av_packet->stream_index == videoindex)
{
std::cout << "\ndata size is:" << av_packet->size;
//这里就是接收到的未解码之前的数据
}
if (av_packet != NULL)
av_packet_unref(av_packet);
}
}
av_free(av_packet);
avformat_close_input(&pFormatCtx);
return 0;
}
linux下运行可执行文件前,可设置从当前文件夹查找so动态库
export LD_LIBRARY_PATH=./
以上讲一些基础知识,rtmp 和 rtsp 的封装推流界面和取流界面,等二再讲了。
我正在寻找一种方法来监视流上的事件,以便我可以确定是否有任何内容通过流。如果有,我将开始使用rtmpdump进行录制。我想象这是通过运行一个每60秒检查一次流的cron任务来实现的。如果它确定流正在通过,则调用rtmpdump开始记录它。如果没有,则什么都不做,并在60秒后再次检查。由于rtmpdump只是在没有流数据时出现错误,因此尝试使用它来监视流似乎不是一个好主意,但也许我错了。如果我在逐个案例的基础上手动执行此操作会很容易,但我正在尝试自动执行自动录制流的任务(如果它们可用)。有没有人遇到过这样做的方法?也许我可以在命令行(linux)中使用其他一些工具?如果有帮助,我正在使用
说起RTMP协议,相信很多人都比较陌生,这个协议相对HTTP、HTTPS、TCP等我们常见的协议而言,我们在工作中确实较少接触它,但是对现在如火如荼的直播行业,RTMP是一个重要的协议,它在实时音视频场景中使用非常广泛,而且目前市占率很高。本文的主要内容是分析RTMP的协议,当然不是纯理论分析,这样没多大意思,还是结合实践抓包文件来具体分析,这样才能较好地理解RTMP的内涵。具体如何抓包见本文末尾的“Android抓包”模块。希望你阅读完本章之后,自己也能简单地动手操作一下,这样理解深刻一下。原版的协议内容太冗长了,感兴趣可以看一下www.adobe.com/devnet/rtmp…RTMP基
我曾经使用V2正常获取它,但在APIV3中有什么替代方法?如果不可用,是否有任何黑客可以做到这一点?例如。猜网址?注意:V2现在已弃用,所以我不能依赖它。 最佳答案 是的,这是v3API的另一大优势。此API的其他好处:不再有多个屏幕截图可供选择不再有视频时长;必须在第二个查询中请求以ISO8601格式加密的视频时长,以确保您无法将其转换为秒数RTSP值的缺乏只是另一个问题。我敢打赌,在APIv4中,他们可能还会隐藏视频标题,以确保API完全无用。 关于javascript-如何使用Yo
大家好,我是虎哥,今天找了一套海康的相机,想后续测试一下DeepStream用网络相机RTSP流做输入看看后续目标识别和分类。但是还是想先实时看看视频,当然,可以选择VLC去查看,顺道我也用GStreamer来测试了一下,并且对比了TX1核心模块下,CPU解码和GPU解码资源占用情况,分享给大家,也是自己做个笔记总结。 我自己找到海康相机的图像尺寸是1280X720的,采用了H.264的压缩。而且,我自己系统安装VLC播放器后没法播放网络流,搜了半天没有解决,所以还是老实搞定GStreamer测试。目录1、显示网络相机RTSP流CPU解码1.1保存一帧照片1.2显示(NoM
我正在尝试在Go中实现RTMP协议(protocol)以配合我的Web应用程序,但是我似乎无法找到在同一端口上同时处理HTTP和RTMP的解决方案。这个想法是这样的。packagemainimport("fmt""io""net/http")funcmain(){http.HandleFunc("/",func(whttp.ResponseWriter,r*http.Request){io.WriteString(w,"Hello!")})http.HandleFunc("/rtmp",func(whttp.ResponseWriter,r*http.Request){//RTMPha
ffmpeg多路同时推流一、ffmpeg常见使用方法1.1利用FFMPEG命令进行文件分割1.2转换格式1.3推流配置方法一:ngnix(不推荐,推流不好使)方法二:srs(强烈推荐)1.4查看nginx启动是否成功二、ffmpeg推流——>ngnix单路推流多(大于两路)路同时推流:方法一:方法二:-map被主进程调用推流脚本后台推流杀死进程三、推送h.264编码的flv视频环境搭建需要x264安装多路推流执行脚本一、ffmpeg常见使用方法后端推流,使用ffmpeg将本地视频推送至ngnix,再拉流,单独推送一路简单,但同时推送多路网上没找到相关的介绍,本文使用ffmpeg的“-map“方
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭1年前。Improvethisquestion我有一个基础设施,其中包含通过Internet连接到视频服务器的七十多个IP摄像机。这种结构是地理分布的,相机型号不同。相机连接到互联网的方法也不同。在我的结论中......这是一个动物园:)不幸的是,用于获取视频流的软件没有监控摄像机状态的特殊功能(该软件是专有的和商业的,没有灵active)。出于这个原因,我想编写一个非常简单的监控工具来检查相机的健康状态。我对G
前言: 之前的rtsp功能,仅仅是对demo的简单修改,(是通过保存本地文件后在读取本地文件数据再播放)。这样存在的主要问题是,如果是先保存好文件,在读取文件传给rtsp播放,有多此一举的嫌疑,而且这样人为的增加了延迟不说,有没有这么大的硬盘让我们一直实时观看呢。更好的一个方法是我们参考海思保存码流的办法,在保存前增加开关,确定是否需要保存(参考我们的配置文件,都不需要重新编译,即可选择是否保存)是否需要实时播放,将要保存的文件直接发给rtsp是个更为明智的选择。另外时间戳也不可忽视,在实时的码流中还是非常重要的demo版本的RTSP播放术语解释RTSP 实时流协议(RTSP)是应用层协议
目录1.设备2.软件iVCam(手机版)iVCam(电脑版)OBSStudio哔哩哔哩直播姬3.构建界面连接摄像头设置OBS推流加入背景加入电脑屏幕加入摄像头加入其他4.开始直播很早之前就想在b站参加一个线上自习室监督自己,但是目前的自习室要么不是免费的,要么人数过多起不到监督效果,在看到b站有相关分区直播后我尝试自己直播这个。先上直播效果1.设备摄像头(可以用手机摄像头代替)电脑2.软件iVCam(手机版)iVCam(电脑版)OBSStudio哔哩哔哩直播姬iVCam(手机版)我用的是iPhone,直接应用市场下载就好了。iVCam(电脑版)下载链接不行直接去这里找,再不济就直接浏览器搜索。
我正在编写一个RTSP客户端并使用MediaFoundation将多个IP摄像机视频源流式传输到Windows显示器。我知道内置的MFRTSP不能很好地处理IP摄像机,所以我必须编写自定义媒体源:编写自定义媒体源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms700134(v=vs.85).aspx此外,以下帖子提供了一些有用的提示,但没有提供太多实现细节:使用MediaFoundation通过RTSP捕获H264/AAC流:https://social.msdn.microsoft.com/Forums/wind