项目描述:

一个本地的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. SqlServer中计算列详解

    计算列区别于需要我们手动或者程序给予赋值的列,它的值来源于该表中其它列的计算值.比如,一个表中包含有数量列Number与单价列Price,我们就可以创建计算列金额Amount来表示数量*单价的结果值, ...

  2. Android BaseAdapter ListView (明星简介列表)

    1.搭建布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" andro ...

  3. Android(java)学习笔记254:ContentProvider使用之内容观察者(观察发出去的短信)

    1.新建一个案例如下: 2. 不需要添加权限,同时这里布局文件不做修改,来到MainActivity,如下: package com.itheima.sendsmslistener; import a ...

  4. 2_Linux_文件和权限处理命令

    三.Linux的常用命令 1.文件命名规范 1)除了/之外,所有的字符都合法,文件名中不能包含/符号 2)有些字符最好不用,如空格.制表符.退格符和字符@#$&() 3)避免使用.作为普通文件 ...

  5. linux常见设备类型及文件系统

    As you can see in  Table   14.3   , all disk device names end with the letter a. That is because it ...

  6. id名和class名有什么区别

    从概念上来说: id是先找到结构/内容,再给它定义样式: class是先定义好一种样式,再套给多个结构/内容. 从样式效果上来说:    id的优先级要比class高出一个层次 html中不管有几个i ...

  7. 腾讯云升级到PHP7

    1.删除之前安装的PHP yum remove php* php-common 2.安装yum源 rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel ...

  8. asp.net微信开发第八篇----永久素材管理

    除了3天就会失效的临时素材外,开发者有时需要永久保存一些素材,届时就可以通过本接口新增永久素材. 最近更新,永久图片素材新增后,将带有URL返回给开发者,开发者可以在腾讯系域名内使用(腾讯系域名外使用 ...

  9. Android中滑动关闭Activity

    继承SwipeBackActivity即可实现向右滑动删除Activity效果 点击下载所需文件

  10. iOS 网络与多线程--5.异步Post方式的网络请求(非阻塞)

    通过Post请求方式,异步获取网络数据,异步请求不会阻塞主线程,而会建立一个新的线程来操作. 代码如下 ViewController.h文件 #import <UIKit/UIKit.h> ...