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功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...
随机推荐
- 关于this的使用
一.关于this的使用 javaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境 二.具体到实际应用中,this指向大致可以分为 ...
- 获取CPU使用情况信息(转)
获取了内存使用情况,也可以使用PHP的 getrusage()获取CPU使用情况,该方法在windows下不可用. print_r(getrusage()); /* 输出 Array ( [ru ...
- ASP.NET MVC 文件异步上传问题处理
最近在做一个网站,用asp.net MVC4.0来开发,今天遇到了个小问题,通过查找相关渠道解决了,在这里把这个问题写出来,问题非常简单,不喜勿喷,mark之希望可以给遇到相同问题的初学者一点帮助.我 ...
- Android Studio导入GitHub上的项目常见问题(以图片轮播开源项目为实例)
前言:github对开发者而言无疑是个宝藏,但想利用它可不是件简单的事,用Android studio导入开源项目会遇到各种问题,今天我就以github上的一个图片轮播项目为例,解决导入过程中的常见问 ...
- Java基础知识强化93:算一下你来到这个世界多少天的案例
1. 分析: (1)键盘录入你的出生年月日 (2)把该字符串转换为一个日期 (3)通过该日期得到一个毫秒值 (4)获取当前时间的毫秒值 (5)用(4)-(3)得到一个毫秒值 (6)把E的毫秒值转换为天 ...
- jquery实现图片切换和js实现图片切换
jquery实现图片切换: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http ...
- yii cgridview 如何显示图片
发布的新闻或介绍里有图片,图片存的是Url地址,通过yii的cgridview,默认是数据库里存的啥就显示啥,如何把url地址转为图片?直接上代码 <?php $data = $model-&g ...
- C++学习笔记39:进程概念
进程的基本概念 进程是描述程序执行过程和资源共享的基本单位 主要目的:控制和协调程序的执行 进程相关函数 用户与组ID函数 创建进程:system(),fork(),exec() 终止进程:kill( ...
- Lesson 4: Know Your Tools
Lesson 4: Know Your Tools "差劲的人
- libthrift0.9.0解析(四)之TThreadPoolServer&ServerContext
TThreadPoolServer直接继承自TServer,实现类serve和stop操作. 在serve中可以接受多个连接,每个连接单独开一个线程进行处理,在每个线程中,按顺序处理该线程所绑定连接的 ...