项目描述:

一个本地的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的总结的更多相关文章

  1. 海康&大华&DSS视频拉流-RTSP转RTMP多媒体播放技术

    海康&大华&DSS获取RTSP 实时流 海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/ ...

  2. C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

    看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...

  3. API Monitor简介(API监控工具)

    API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得了API调用. 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题. 64位支持 API ...

  4. Atitit onvif协议获取rtsp地址播放java语言 attilx总结

    Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...

  5. 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)

    前面我们创建了 Pool,VIP 并添加了 Member.今天将创建 Monitor,然后测试 LBaaS 是否能够正常工作. 创建 Monitor LBaaS 可以创建 monitor,用于监控 P ...

  6. 11g新特性:Health Monitor Checks

    一.什么是Health Monitor ChecksHealth Monitor Checks能够发现文件损坏,物理.逻辑块损坏,undo.redo损坏,数据字典损坏等等.Health Monitor ...

  7. 使用vlc播放器做rtsp流媒体服务器

    可参考: 使用vlc播放器播放rtsp视频 web网页中使用vlc插件播放相机rtsp流视频 使用vlc进行二次开发做自己的播放器 首先需要安装vlc播放器,下载及安装步骤略 使用vlc播放器做rts ...

  8. 使用vlc播放器播放rtsp流视频

    可参考: 使用vlc播放器做rtsp服务器 web网页中使用vlc插件播放相机rtsp流视频 使用vlc进行二次开发做自己的播放器 首先需要安装vlc播放器,下载及安装步骤略 使用vlc播放器播放rt ...

  9. web网页中使用vlc插件播放相机rtsp流视频

    可参考: 使用vlc播放器做rtsp服务器 使用vlc播放器播放rtsp视频 使用vlc进行二次开发做自己的播放器 vlc功能还是很强大的,有很多的现成的二次开发接口,不需配置太多即可轻松做客户端播放 ...

随机推荐

  1. Nodejs 上传下载功能的实现(同步)

    上传和下载可分为两种,一种是form表单的形式(同步),另一种是Ajax的形式(异步). 示例一(form表单): html代码如下: <!DOCTYPE html> <html&g ...

  2. 解决NoSuchMethodError with Spring MutableValues异常问题

    今天下午项目启动时,遇到一个异常,导致无法启动: [ 221] ERROR - work.web.context.ContextLoader - Context initialization fail ...

  3. Guava Collect

    Guava是什么 进入新公司就会接触一些新的东东,Guava就是一个,Guava是Google的一个开源类库,丰富了JDK的API,并且使用起来非常方便,本文介绍的是Guava collect包下的一 ...

  4. Target runtime Apache Tomcat v6.0 is not defined. phyy Unknown Faceted Project Problem

    Description Resource Path Location TypeTarget runtime Apache Tomcat v6.0 is not defined. phyy Unknow ...

  5. MM32 备份域学习(兼容STM32)

    MM32 备份域学习(兼容STM32) 内容提要 备份域工作原理 备份域特性 备份域的保护:侵入检测 备份域侵入检测 备份域电源与主要内容 备份域特性 20字节数据后备寄存器(中容量和小容量产品),或 ...

  6. JNI Java调用C代码 示例

    Activity public class MainActivity extends ListActivity {     static {         System.loadLibrary(&q ...

  7. java基础01

    1. /** * JDK: (Java Development ToolKit) java开发工具包.JDK是整个java的核心! * 包括了java运行环境 JRE(Java Runtime Env ...

  8. MFC 中的 “printf” 函数

    怀念C语言的我,MFC没法使用的C语言printf函数,于是: int MFCprintf(const char* m_data, ...){ CString str; char printf_buf ...

  9. JQuery 解析xml

    JQuery 可以通过 $.get() 或 $.post() 方法来加载 xml.     JQuery 解析 XML 与解析 DOM 一样, 可以使用 find(), children() 等函数来 ...

  10. 武汉科技大学ACM:1003: 华科版C语言程序设计教程(第二版)例题6.6.改编

    Problem Description 小明明最喜欢学英语了,英语课从来不翘课,但是英语却一直没学好,因为上课一直在睡觉.为什么会睡觉呢,因为他觉得英文单词太长了.现在小明明有一个很长很长很长的单词, ...