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功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...
随机推荐
- J2EE开发中常用的缓存策略
一.什么是缓存1.Cache是高速缓冲存储器 一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问2.凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之 ...
- PHP学习之[第03讲]PHP5.4 语法、常量、变量、数据类型详解
1.<?php echo "Hello World"; ?> 2.//注释1,#注释2,/* 注释3 */ 3.系统常量: __FILE__默认常量,是指PHP程序文件 ...
- js转码和解码兼容低版本火狐
function HTMLEncode(html) { var temp = document.createElement("div"); ...
- 告诉你LTE-FDD与LTE-TDD的区别
[PConline 技术分析]移动早在去年已经拿下了TD-LTE的4G牌照,而中国联通与中国电信的FDD-LTE的牌照在近日正式拿下,而对于4G网络,有多少真正了解呢?接下来笔者就为大家解释一下4G的 ...
- Android批量图片载入经典系列——使用LruCache、AsyncTask缓存并异步载入图片
一.问题描写叙述 使用LruCache.AsyncTask实现批量图片的载入并达到下列技术要求 1.从缓存中读取图片,若不在缓存中,则开启异步线程(AsyncTask)载入图片,并放入缓存中 2.及时 ...
- 8000401a错误解决方式(Excel)
前一阵子做开发须要用到Excel和Word编程,本人用的是Vista系统,开发环境是VS2005和Office2007,測试无不论什么问题,但是到部署的时候出现了一些令人非常头痛的问题,老是会出现比如 ...
- Com编程入门——什么是COM,如何使用COM
本文的目的是为刚刚接触COM的程序员提供编程指南,并帮助他们理解COM的基本概念.内容包括COM规范简介,重要的COM术语以及如何重用现有的COM组件.本文不包括如何编写自己的COM对象和接口. CO ...
- 在Windows环境中安装并使用kafka
[TOC] 安装部署 安装部署Java 下载需要安装的软件,下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jre8-down ...
- 再探Java基础——String.format(String format, Object… args)的使用
最近看到类似这样的一些代码:String.format("参数%s不能为空", "birthday"); 以前还没用过这功能不知咐意思,后研究了一下,详细讲解如 ...
- IntelliJIdea快捷键
Ctrl+Shift+方向键Up/Down 代码向上/下移动. Ctrl+X 删除行 Ctrl+Y 也是删除行,不知道有啥区别 Ctrl+D 复制行 Ctrl+Alt+L 格式化代码 Ctrl+N 查 ...