RTSP Monitor的总结
项目描述:
一个本地的IP Camera 实时发送RTSP视频流到本机上,视频的帧是H264编码,需要解码并显示到屏幕上。并把每帧视频对应的时间戳转换成日期年月日时分秒打印到每帧的图像上显示。
使用到的库:
1.用Qt做了GUI,QLabel显示图像画面,每解码得到一帧转换成图像格式再用Qt的信号机制发送信号到QLabel上显示。
2.用Live555 建立本机与IP Camera的RTSP的会话,需要用到sdp文件。sdp文件提供了编码的描述和相关的H264的SPS和PPS(可以去百度,sdp文件中是以Base64编码的字符串序列)。Live555的相关回调函数会把每帧传递出来(frameBuffer和frameSize) (FFMPEG也提供rtsp流的支持,但是支持不好,它可以直接打开rtsp://129.234.32.123:2324/wesdadawd.sdp这样的URL形式)
3.用FFMPEG的H264解码器去解frameBuffer 包含的帧数据。
关键的知识点:
1.单独一个RTP包可以单独包含一帧,也可以包含很多帧,还有一帧分别打包成多个RTP包,取决于服务端的打包方式。
2.RTP的timestamp是相对的(是个随机的初始值),需要配合RTCP包里面的NTP来相应的计算绝对时间,RTCP包的设计目的是保证RTP包的传输质量的,RTCP包里面有丢包等计数,当RTSP会话建立完成,会建立一个端口对,一个是偶数(RTP包端口),一个奇数(RTCP包端口),这样发送给客户端。当然,可以用WireShark去抓来看看,不过貌似WireShark不能直接识别RTP包,抓出来的UDP,因为RTP包一般来说是基于UDP的包
2.H264的帧是以 NAL单元的单位来传送的,一个NAL单元包含一帧(I帧 或 P帧 或 B帧),这三种类型的帧可以百度。所谓的NAL单元就是去掉SPS、PPS的视频帧, I帧是关键帧,所有的解析都需要靠它,两个I帧之间被称为视频序列,I帧头部需要加入SPS和PPS,这两个之间需要0x00000001来分割, 0x00 0x00 0x00 0x01 + SPS的Base64解码形式 + 0x00 0x00 0x00 0x01 + PPS的解码形式 + 0x00 0x00 0x00 0x01 视频帧(IDR帧) 这样组成的一个buffer,FFMPEG的H264解码器才能成功解码。
大概的关键代码片断就是这样:
if (!m_ffmpeg->Init()) return;
m_scheduler = BasicTaskScheduler::createNew();
m_env = BasicUsageEnvironment::createNew(*m_scheduler);
m_session = MediaSession::createNew(*m_env, readSDPFile(m_url.toStdString().c_str()));
if (!m_session)
{
qDebug() << "Created session failed from SDP file";
return ;
}
MediaSubsessionIterator iter(*m_session);
while ((m_subsession = iter.next()) != NULL)
{
if (!m_subsession->initiate())
{
*m_env << "Failed to initiate the \"" << *m_subsession << "\" subsession: " << m_env->getResultMsg() << "\n";
}
else
{
m_subsession->sink = DummySink::createNew(*m_env, *m_subsession, m_url.toStdString().c_str());
if (m_subsession->sink == NULL)
{
*m_env << "Failed to create a data sink for the \"" << *m_subsession << "\" subsession: " << m_env->getResultMsg() << "\n";
}
else
{
const char* sps = m_subsession->fmtp_spropsps();
const char* pps = m_subsession->fmtp_sproppps();
m_subsession->sink->startPlaying(*m_subsession->rtpSource(), NULL, NULL);
}
}
}
char eventLoopWatchVariable = ;
m_env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
////////////////////frame callback function in Dummy::Sink/////////
void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned durationInMicroseconds)
{
if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
QByteArray frameBuffer((char*)fReceiveBuffer, frameSize);
//插入SPS PPS才能让H264解码器正确解码
QByteArray sps = sprop_parameter_sets;
QByteArray extraData;
QList<QByteArray> recodList = sps.split(',');
for (int i = ; i < recodList.size(); ++i)
{
extraData.append(char(0x00));
extraData.append(char(0x00));
extraData.append(char(0x00));
extraData.append(char(0x01));
extraData += QByteArray::fromBase64(recodList.at(i));
}
QByteArray endMark = QByteArray(, );
endMark[] = 0x01;
frameBuffer.insert(, extraData);
frameBuffer.insert(extraData.size(), endMark);
gffmpeg->decodeFrame((uint8_t*)frameBuffer.data(), frameBuffer.size(), presentationTime.tv_sec, presentationTime.tv_usec);
// Then continue, to request the next frame of data:
continuePlaying();
}
////////////////////////decode frame function/////////////////////////
void QFFmpeg::decodeFrame(uint8_t* frameBuffer, int frameLength, long second, long microSecond)
{
if (frameLength <= ) return;
int frameFinished = ;
AVPacket framePacket;
av_init_packet(&framePacket);
framePacket.size = frameLength;
framePacket.data = frameBuffer;
int ret = avcodec_decode_video2(m_pAVCodecContext, m_pAVFrame, &frameFinished, &framePacket);
if (ret < )
{
qDebug() << "Decode error";
return;
}
if (frameFinished)
{
m_playMutex.lock();
m_videoWidth = m_pAVFrame->width;
m_videoHeight = m_pAVFrame->height;
QImage frame = QImage(m_videoWidth, m_videoHeight, QImage::Format_ARGB32);
m_pSwsContext = sws_getCachedContext(m_pSwsContext, m_videoWidth, m_videoHeight, AVPixelFormat::AV_PIX_FMT_YUV420P, m_videoWidth, m_videoHeight,
AVPixelFormat::AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
uint8_t *dstSlice[] = { frame.bits() };
int dstStride = frame.width() * ;
sws_scale(m_pSwsContext, m_pAVFrame->data, m_pAVFrame->linesize, , m_videoHeight, dstSlice, &dstStride);
char timestamp[];
char millisecond[];
time_t time = second;
struct tm *now_time;
now_time = gmtime(&time);
strftime(timestamp, , "%Y-%m-%d %H:%M:%S", now_time);
sprintf_s(millisecond, , " %ld.%d%d%d",microSecond/,,,);
//发送获取一帧图像信号
QImage image((uchar*)dstSlice[], m_videoWidth, m_videoHeight, QImage::Format_RGB32);
QPainter pen(&image);
pen.setPen(Qt::white);
pen.setFont(QFont("Times", , QFont::Bold));
pen.drawText(image.rect(), Qt::AlignBottom, QString(timestamp) + QString(millisecond));
emit GetImage(image);
m_playMutex.unlock();
}
}
##############################Update 2016-5-13###################
前面几乎都废弃了。
现在我直接发现了一个开源的流媒体解决方案平台
http://www.easydarwin.org
里面很多都是对RTSP IP Camera这类的流媒体库,已经封装的很成熟了。
references:
http://blog.csdn.net/sno_guo/article/details/22388233
http://blog.csdn.net/sunnylgz/article/details/7680262
http://stackoverflow.com/questions/11330764/ffmpeg-cant-decode-h264-stream-frame-data/
http://stackoverflow.com/questions/20786781/ffmpeg-decode-raw-buffer-with-avcodec-decode-video2
http://stackoverflow.com/questions/18857737/decoding-h264-frames-from-rtp-stream
http://stackoverflow.com/questions/32475317/how-to-open-the-local-sdp-file-by-live555
http://blog.csdn.net/leixiaohua1020 (强烈推荐这个博客,在上面学到了很多编码的基础知识)
http://www.zhihu.com/question/20278635 (RTSP、RTP,RTCP的区别)
http://www.zhihu.com/question/20997688 (MP4/RMVB/MKV/AVI 等,这些视频格式与编码压缩标准 mpeg4,H.264.H.265 等有什么关系?)
http://www.zhihu.com/question/27460676
http://www.2cto.com/kf/201408/327163.html
http://stackoverflow.com/questions/4873493/how-can-i-convert-number-of-seconds-since-1970-to-datetime-in-c
RTSP Monitor的总结的更多相关文章
- 海康&大华&DSS视频拉流-RTSP转RTMP多媒体播放技术
海康&大华&DSS获取RTSP 实时流 海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/ ...
- C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent
看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...
- API Monitor简介(API监控工具)
API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得了API调用. 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题. 64位支持 API ...
- Atitit onvif协议获取rtsp地址播放java语言 attilx总结
Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...
- 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)
前面我们创建了 Pool,VIP 并添加了 Member.今天将创建 Monitor,然后测试 LBaaS 是否能够正常工作. 创建 Monitor LBaaS 可以创建 monitor,用于监控 P ...
- 11g新特性:Health Monitor Checks
一.什么是Health Monitor ChecksHealth Monitor Checks能够发现文件损坏,物理.逻辑块损坏,undo.redo损坏,数据字典损坏等等.Health Monitor ...
- 使用vlc播放器做rtsp流媒体服务器
可参考: 使用vlc播放器播放rtsp视频 web网页中使用vlc插件播放相机rtsp流视频 使用vlc进行二次开发做自己的播放器 首先需要安装vlc播放器,下载及安装步骤略 使用vlc播放器做rts ...
- 使用vlc播放器播放rtsp流视频
可参考: 使用vlc播放器做rtsp服务器 web网页中使用vlc插件播放相机rtsp流视频 使用vlc进行二次开发做自己的播放器 首先需要安装vlc播放器,下载及安装步骤略 使用vlc播放器播放rt ...
- web网页中使用vlc插件播放相机rtsp流视频
可参考: 使用vlc播放器做rtsp服务器 使用vlc播放器播放rtsp视频 使用vlc进行二次开发做自己的播放器 vlc功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...
随机推荐
- Mina笔记
1.MINA框架简介 MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用M ...
- Java笔记6之三目运算符
/* 单目运算符:~3 双目运算符:3 + 4 三目运算符: 格式:比较表达式?表达式1:表达式2; 比较表达式:结果是一个boolean ...
- 【ArcGIS 10.2新特性】Geodatabase 10.2 常见问题
地理数据库技术一直以来都是ArcGIS的基础技术.为充分使用ArcGIS的全部功能则需要把数据存储在Geodatabase当中.Geodatabase是一个综合性的信息模型,它可以支持存储几乎任意类型 ...
- Android ImageView 点击更换头像
首先搭建布局 主界面布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" ...
- php中运用GD库实现简单验证码
昨天学习了运用php的GD库进行验证码的实现. 首先可以用phpinfo()函数看一下GD库有没有安装,我用的wampserver是自动给安装的. 主要的步骤是: 1.生成验证码图片 2.随机生成字符 ...
- Java基础知识强化之IO流笔记01:异常的概述和分类
IO流操作的时候会出现很多问题,java中叫作异常,所以我们先介绍一下异常: 1. 程序的异常:Throwable(Throwable类是java中所有异常或错误的超类) (1)严重问题:Error ...
- Chapter 8. Introduction to multi-project builds 多工程构建介绍
Only the smallest of projects has a single build file and source tree, unless it happens to be a mas ...
- 排序算法之奇偶排序 JAVA奇偶排序算法
奇偶排序法的思路是在数组中重复两趟扫描.第一趟扫描选择所有的数据项对,a[j]和a[j+1],j是奇数(j=1, 3, 5……).如果它们的关键字的值次序颠倒,就交换它们.第二趟扫描对所有的偶数数据项 ...
- 富文本文件CKEDITOR增加上传图片功能(.net)
如题,本身的CKEDITOR控件并没有开启上传图片的功能, 打开图像按钮,只有图像信息和高级两个table选项卡,版本不同,显示略有差异,我的实现是有两种方法都可以添加上传功能, 第一种方法使用CKE ...
- WCF调用问题
1---WCF常用的节点绑定 basicHttpBinding和netTcpBinding 当通过Spring对象注入 如 WCFForOrderService 对象注入 public WCFForO ...