显示标签为“协议”的博文。显示所有博文
显示标签为“协议”的博文。显示所有博文

2010年4月27日星期二

VLC如何进行RTSP的支持(源码阅读心得)

今天天气很好,心情大好,Hanyionet和大家分享一下阅读VLC的源码的一些心得。

先小小吹捧和介绍一下VLC。
大家都知道VLC是一个免费,开放源码,跨平台媒体播放器,并集合先进的流媒体功能可以通过IPv4或IPv6的高带宽网络进行流媒体传输。它还支持多种视频格式和流协议。VLC的编解码基于著名的开源项目FFmpeg的libavcodec的开源库。VLC同时提供不同的接口(命令行,网络,图形用户界面,远程登录),以及提供流行的语言如C,C++,C#,Python,和Java可以使用的APIs,这使得VLC变得高度灵活,并且用于任何类型播放或流媒体处理。

VLC支持RTSP功能是通过使用liveMedia库,这个库是live555开源项目一部分。VLC支持RTSP协议,可用于在线或离线转码,视频点播服务和单播或组播流。除了这些以外,VLC还提供各种接口和API,VLC还允许被集成在一个更复杂的环境,可以让我们建立一个更先进的视频应用程序。

进入正题,VLC到底是如何进行RTSP的支持。

VLC对RTSP的支持的实现的方式是通过一种输出链(output chains)。VLC读取各种类型的视频输入(文件,流,DVD等),然后通过定义一个或多个输出链(output chains)的内容,然后用于播放流。一个链可以连接到另一个链条,然后就可以形成一个比较复杂的工作流程。下图为一个简单的例子说明。图中VLC设置并读取本地文件,然后转码,并通过使用两个链将数据传输到一个多播地址。链1将读取文件,然后转码。链2读取链1输出的转码文件,然后将流发送到至指定地址。整个过程类似UNIX管道的工作方式。

VLC RTSP example hanyionet

Enjoy!

2009年11月30日星期一

如何将BGR转成YUV420

今天心情非常之好,花了一个下午时间搞定了困扰了我一个月的事情。写了一个程序可以把openCV的BGR图像格式转换成YUV4:2:0,然后通过FFmpeg的API把YUV4:2:0的图像编码压缩,最后利用live555把压缩后的buffer打包成RTP包,最后用Unicast将它们发送出去。简单的说就是 BGR—>YUV4:2:0—>encode to buffer—>RTP—>Unicast这样的过程。

把预想的程序完成真是一大快事,随便记录一部分比较特别的东西,因为openCV没有BGR转YUV420的函数,所以需要自己写一个,我写的这个函数读入一个三通道的图像数据,然后先使用cvCvtColor函数,将BGR转成YCrCb,这个YCrCb是4:4:4也就是全抽样,然后对Cb和Cr分量进行抽样,最后我们可以得到三个数组,分别存Y, U, V三个分量。这个转换不算复杂,就是使用了以下的概念。

YUV4:2:0

 yuv420 4:2:0并不意味着只有Y,Cb而没有Cr分量。它指得是对每行扫描线来说,只有一种色度分量以2:1的抽样率存储。相邻的扫描行存储不同的色度分量,也就是说,如果一行是4:2:0的话,下一行就是4:0:2,再下一行是4:2:0...以此类推。对每个色度分量来说,水平方向和竖直方向的抽样率都是2:1,所以可以说色度的抽样率是4:1。对非压缩的8比特量化的视频来说,每个由2x2个2行2列相邻的像素组成的宏像素需要占用6字节内存。
八个像素为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3][Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]
存放的码流为:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8
射出的像素点为:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7][Y5 U0 V5] [Y6 U0 V5] [Y7U2 V7] [Y8 U2 V7](来自百度百科)我们可以对每八个像素:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3][Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]进行一次抽样,抽样: Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7 给Y分量,U0 U2 给U分量,V5 V7给V分量。

现在提供另一个简单的转换函数:

void convertBGR2YUV420(IplImage *in, unsigned char* out_y, unsigned char* out_u, unsigned char* out_v)  
{  
    // first, convert the input image into YCbCr 
    IplImage *tmp = cvCreateImage(cvSize(in->width, in->height), 8, 3); 
    cvCvtColor(in, tmp, CV_RGB2YCrCb); 
    /*  
    * widthStep = channel number * width  
    * if width%4 == 0  
    * for example, width = 352, width%4 == 0, widthStep = 3 * 352 = 1056  
    */ 
    int idx_in = 0;  
    int idx_out = 0;  
    int idx_out_y = 0;  
    int idx_out_u = 0;  
    int idx_out_v = 0; 
    
    for(int j = 0; j < in->height; j+=1) { 
        idx_in = j * in->widthStep; 
    
        for(int i = 0; i < in->widthStep; i+=12) { 
        // We use the chroma sample here, and put it into the out buffer 
        // take the luminance sample 
        out_y[idx_out_y] = tmp->imageData[idx_in + i + 0]; // Y 
        idx_out_y++; 
        out_y[idx_out_y] = tmp->imageData[idx_in + i + 3]; // Y 
        idx_out_y++; 
        out_y[idx_out_y] = tmp->imageData[idx_in + i + 6]; // Y 
        idx_out_y++; 
        out_y[idx_out_y] = tmp->imageData[idx_in + i + 9]; // Y 
        idx_out_y++; 

        if((j % 2) == 0) { 
              // take the blue-difference and red-difference chroma components sample  
              out_u[idx_out_u++] = tmp->imageData[idx_in + i + 1]; // Cr U  
              out_u[idx_out_u++] = tmp->imageData[idx_in + i + 7]; // Cr U  
              out_v[idx_out_v++] = tmp->imageData[idx_in + i + 2]; // Cb V  
              out_v[idx_out_v++] = tmp->imageData[idx_in + i + 8]; // Cb V 
        }  

        }    
    }  
    cvReleaseImage(&tmp); 
}

2009年11月24日星期二

挖概念:RTP timestamp, payload, RTP, RTSP, MPEG ES, TS, PS

阅读别人写的程序绝对是一个很好的提高编程水平的途径,同时也是一个快速获取新知的方式。但是如何从源程序的一点“新的知rtp_timestamp_braindiagram 识点”拓展到一个大的“知识面”,这是一个很有意思的问题,今天我想结合自己的小小的经验来谈谈这个问题。
我采用土法炼钢:“挖概念”法,就是从一个小的概念不停地向外延伸,直到对整个知识架构有一定程度的了解。
举一个我自己的例子,前不久我在看一个视频接收程序的源代码,看到有一行关于timestamp的计算,什么是timestamp这就是一个“新的知识点”。然后我就通过这个点挖出一些我需要的知识,几小时我慢慢地对整个知识架构有一定程度的了解,图示说明了我“挖概念”的过程,以下选载一些我找到的知识点,它们都是围绕着timestamp这个问题展开的。

[1]:


时间戳(Timestamp)-->在RTP中反映RTP数据信息包中第一个字节的采样时刻(时间)。接收端可以利用这个时间戳来去除由网络引起的信息包的抖动,并且在接收端为播放提供同步功能。
时间戳字段是RTP首部中说明数据包时间的同步信息,是数据能以正确的时间顺序恢复的关键。时间戳的值给出了分组中数据的第一个字节的采样时间(Sampling Instant),要求发送方时间戳的时钟是连续、单调增长的,即使在没有数据输入或发送数据时也是如此。在静默时,发送方不必发送数据,保持时间戳的增长,在接收端,由于接收到的数据分组的序号没有丢失,就知道没有发生数据丢失,而且只要比较前后分组的时间戳的差异,就可以确定输出的时间间隔。RTP规定一次会话的初始时间戳必须随机选择,但协议没有规定时间戳的单位,也没有规定该值的精确解释,而是由负载类型来确定时钟的颗粒,这样各种应用类型可以根据需要选择合适的输出计时精度。(来自:百度百科+Wiki)


[2]:


RTP实时传送协议Real-time Transport Protocol由两个紧密链接部分组成: RTP ― 传送具有实时属性的数据;RTP 控制协议(RTCP)。
RTCP的一个关键作用就是能让接收方同步多个RTP流,例如:当音频与视频一起传输的时候,由于编码的不同,RTP使用两个流分别进行传输,这样两个流的时间戳以不同的速率运行,接收方必须同步两个流,以保证声音与影像的一致。为能进行流同步,RTCP要求发送方给每个传送一个唯一的标识数据源的规范名(Canonical Name),尽管由一个数据源发出的不同的流具有不同的同步源标识(SSRC),但具有相同的规范名,这样接收方就知道哪些流是有关联的。而发送方报告报文所包含的信息可被接收方用于协调两个流中的时间戳值。发送方报告中含有一个以网络时间协议NTP(Network Time Protocol)格式表示的绝对时间值,接着RTCP报告中给出一个RTP时间戳值,产生该值的时钟就是产生RTP分组中的TimeStamp字段的那个时钟。由于发送方发出的所有流和发送方报告都使用同一个绝对时钟,接收方就可以比较来自同一数据源的两个流的绝对时间,从而确定如何将一个流中的时间戳值映射为另一个流中的时间戳值。RTSP实时流放协议(Real-Time Streaming Protocol)提供控制多种应用数据传送的功能,提供一种选择传送通道的方法,例如UDP, TCP, IP多目标广播通道,以及提供一种基于RTP协议的递送方法。

RTSP将工作在RTP的上层,用来控制和传送实时的内容。RTSP能够与资源保留协议一起使用,用来设置和管理保留带宽的流式会话或者广播。

(来自:百度百科+Wiki)

[3]:


读了RTP的说明(RFC 2250)后发现RTP with payload type 32 (MPEG1/2 video ES): The RTP timestamp 表示了video frame的PRESENTATION time。
RTP with payload type 33 (MPEG2 TS): The RTP timestamp 表示RTP packet的TRANSMISSION time。(来自:RFC 2250)


[4]:


MPEG一2标准的正式名称为“ISO/IEC13818信息技术——活动图象和相关声音信息的一般编码方法”,是其中的一个编码标准,主要是用于传输声音、图象数据压缩标准,它是MPEG-1的进一步发展,码流在1.5Mb/S到50Mb/s之间。

MPEG-2标准是将视、音频及其他数据基本流(ES)组合成一个或多个适宜于存储或传输的数据流的规范。MPEG-2分为压缩层和系统层:压缩层中,数字视音频数据分别经过编码器,生成连续不分段的基本码流(ES),对于视音频来说,ES就是由一系列编码后的视音频帧的存取单元(AU)组成;在系统层将视音频ES分别通过各自的打包器,加上相应的包头,打包分组为包长度可变的基本流(PES)。系统层主要用来描述视、音频数据复用和同步方式,节目复用器和传输复用器分别将视音频PES加入系统层信息组成相应的节目流(PS)和传输流(TS),多条TS流还可以再次复用成一条多节目TS(MPTS)。

1.数字化的视频、音频和辅助数据,经过压缩后形成各自的基本流(ES)。
2.视频和音频的ES流分别按一定的格式打包,构成具有某种格式的打包的基本信息流(PES:Packetized Elementary Stream),分别称为视频PES和音频PES。这一步骤在打包器内实现,PES的长度可在一定范围内变化。
3.将视频、音频的PES流以及辅助数据按不同的格式再打包,然后进行复用,即分别生成了TS流和PS流.
根据传输媒体的质量不同,MPEG-2中定义了两种复合信息流:传送流(TS:Transport Stream)和节目流(PS:Program Stream)原始的视音频数据流经编码器编码输出压缩后的基本码流 ES, 它含有解码器所必需的、用于恢复原始视音频的信息。 基本码流 ES分解打包成 PES数据包, 每个 PES包在复用的过程中被分成固定长度的传输流包( TS Packet)。TS包的长度是固定的,为188字节。
(来自:张佳,电影频道节目中心传送部,有线电视技术,MPEG-2码流的层次分析,2008,9)

-----------
“挖概念”法,有时候挺好用的,可以在短时间内对一个比较大的知识领域得到一定的理解。

2009年11月23日星期一

浅议SDP(会话描述协议)

因为最近常常使用到SDP(会话描述协议Session Description Protocol)写了一些SDP的文本,在linux里使用例如"ffplay test.sdp"来播放媒体流,今天想简单谈谈SDP,做了一个小小的总结和分析,希望对大家有帮助。

SDP是描述的是流媒体的初始化参数,IETF对其的描述可以在RFC 2327找到,SDP是一个纯文本文档,后缀为.sdp,它的基本内容包括:

# 会话信息:
* 会话名和目的;
* 会话时间;
* 会话使用的带宽;
* 会话的用户信息;
# 媒体信息:
* 媒体类型,例如:视频或音频;
* 传输协议,例如:RTP/UDP/IP;
* 媒体格式,例如:H.263视频或者MPEG视频;
* 多播地址和媒体传输端口(IP多播会话);
* IP单播会话的联系地址和媒体传输端口

举一个例子来进行分析(live555的testMPEG1or2VideoStreamer里附加的SDP文本):

----------------------------------------------------------------------------------------------

v=0

o=- 49451 3 IN IP4 127.0.0.1

s=Test MPEG Video session

i=Parameters for the session streamed by "testMPEG1or2VideoStreamer"

t=0 0

a=tool:testMPEG1or2VideoStreamer

a=type:broadcast

m=video 1234 RTP/AVP 32

c=IN IP4 239.255.42.42/127

----------------------------------------------------------------------------------------------

可以发现SDP会话描述由许多文本行组成,它的格式为“类型=值”。其中v,o,s等等代表了是类型。

第1行v代表了协议版本,例子中为0。
第2行o代表所有者/创建者和会话标识符。

第3行s代表会话名称,例子中为Test MPEG Video session,用户可以自己填写。
第4行t代表会话活动时间。

第5行和第6行a代表会话属性行,可写0个或多个。

第7行m代表代表媒体信息;video代表是视频流;1234代表UDP端口号是1234;RTP/AVP指媒体传输协议使用RTP/AVP;32代表媒体格式使用MPV并且使用90KHz的时钟。关于RTP/AVP可以在RFC 3551 RTP A/V Profile July 2003找到。以下是部分截取:

PT encoding media type clock rate
name (Hz)24 unassigned V
25 CelB V 90,000
26 JPEG V 90,000
27 unassigned V
28 nv V 90,000
29 unassigned V
30 unassigned V
31 H261 V 90,000
32 MPV V 90,000 (这就是例子中的RTP/AVP类型)
33 MP2T AV 90,000
34 H263 V 90,000
35-71 unassigned ?
72-76 reserved N/A N/A
77-95 unassigned ?
96-127 dynamic ?
dyn H263-1998 V 90,000

第8行c代表连接信息。

当然了还可以加上更多的信息描述,例如b=AS:104857,b代表了带宽信息。