H.264帧结构和RTSP协议源码框架

这篇具有很好参考价值的文章主要介绍了H.264帧结构和RTSP协议源码框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、H264编码原理和基本概念

1.1、h.264编码原理

1.2、h.264编码相关的一些概念

2、H264的NAL单元详解

2.1、VCL和NAL的关系

2.2、H.264视频流分析工具

2.3、h264视频流总体分析

2.4、相关概念

3、H264的NAL单元---sps和pps

3.1、sps和pps详解

3.2、H264的profile和level

3.3、sequence

4、rtsp协议和源码框架

4.1、源码框架函数调用关系

4.2、直接发送与环状buffer发送

4.3、RTP发送一帧数据的两种不同发送格式->整发送和分包发送


1、H264编码原理和基本概念

1.1、h.264编码原理

(1)图像冗余信息:空间冗余、时间冗余

(2)视频编码关键点:压缩比高、算法复杂度小、还原度高

(3)H.264的2大组成部分:VCL和NAL

        VCL关心如何进行视频压缩

        NAL关心压缩后的视频流如何被网络传输和解码播放

1.2、h.264编码相关的一些概念

(1)宏块 MB(macroblock):多个像素组成的一块,宏块是视频压缩算法的最基本单位

由于图像本身在局部,颜色具有相似性,所以可以把一幅图像分成若干个宏块

(2)片 slice:构成帧的一部分

        一帧图像=若干个slice(可以是一个slice)

        一个slice=若干个MB

        一个MB=多个像素

(3)帧 frame:一整幅完整的图像

(4)I帧、B帧、P帧:帧有好几种类型

I帧:非参考帧,这一帧图像的内容只和本身有关,和前一帧后一帧图像的内容无关,一般作为起始帧,因为这一帧没有任何参考,所以只能对这一帧进行帧内压缩(空间冗余上的优化)

B帧:参考帧,这一帧图像的内容不光和本身有关,还和前一帧或后一帧图像的内容有关(空间冗余+时间冗余的优化)

P帧:参考帧,这一帧图像的内容不光和本身有关,还和前一帧图像的内容有关(空间冗余+时间冗余的优化)

(5)帧率 fps:一秒中有多少帧,帧率高(慢动作),帧率低(快动作)

(6)像素->宏块->片->帧->序列->码流

2、H264的NAL单元详解

2.1、VCL和NAL的关系

(1)VCL只关心编码部分,重点在于编码算法以及在特定硬件平台的实现,VCL输出的是编码后的纯视频流信息,没有任何头信息

(2)NAL关心的是VCL的输出纯视频流如何被表达和封包以利于网络传输

(3)SODB:String Of Data Bits(VCL输出的纯视频流)

(4)RBSP:Raw Byte Sequence Payload

(5)NALU:Network Abstraction Layer Units        NALU是H264文件的基本组成单元

(6)关系:

SODB + RBSP trailing bits = RBSP

NALU header(1 byte) + RBSP = NALU

H264文件由若干个序列组成 -> 序列由若干个帧/slice组成 -> 帧/slice由分隔符和NALU单元组成 -> 去掉NALU header得到RBSP -> 去掉RBSP trailing得到SODB-> VLC播放器解码播放SODB

(7)总结:做编码器的人关心的是VCL部分;做视频传输和解码播放的人关心的是NAL部分

2.2、H.264视频流分析工具

(1)雷神作品:SpecialVH264.exe

(2)国外工具:Elecard StreamEye Tools

(3)二进制工具:winhex

(4)网络抓包工具:wireshark

(5)播放器:vlc

2.3、h264视频流总体分析

(1)h264标准有多个版本,可能会有差异,具体差异不详

(2)网上看的资料有时讲法会有冲突,或者无法验证的差异

(3)这里以海思平台为主、为准、为案例,不能保证其他平台也完全一样

(4)海思平台编码出来的H.264码流(就是一个二进制文件)都是一个一个序列组成,序列包含:1sps+1pps+1sei+1I帧+若干p帧

H.264帧结构和RTSP协议源码框架

2.4、相关概念

(1)序列 sequence,一般一秒发一个sequence,也就是说sequence(除去sps、pps、sei)和帧率相等

(2)分隔符:00 00 00 01的四字节组合,是用来做识别的,不是有效数据,表示一个slice的开始,表示我们有一个新的片到来。

H.264帧结构和RTSP协议源码框架

帧的有效数据部分是不会出现(00 00 00 01)的,h264的标准规定有效数据不允许出现连续的3个00,会在第二个00后面,第三个00前面添加一个03进去,用(00 00 03 00)来表示(00 00 00)。

(3)sps

(4)pps

(5)sei

(6)NALU header:分隔符后的第一个字节

H.264帧结构和RTSP协议源码框架

NALU header有8个位

bit7(forbidden_zero_bit):禁止位,默认位0,值为1表示语法出错

bit6~5(nal_ref_idc):重要性,代表这一帧的内容在视频流中的重要程度,重要程度由大到小11>10>01>00

bit4~0(nal_unit_type):NAL单元类型

H.264帧结构和RTSP协议源码框架

1:表示 非I帧,具体是P还是B帧不知道

5:表示 I帧

6:表示 SEI

7:表示 SPS

8:表示 PPS

参考博文:

H264(NAL简介与I帧判断)_h264 判断i帧_jefry_xdz的博客-CSDN博客

3、H264的NAL单元---sps和pps

参考博文:

https://www.cnblogs.com/wainiwann/p/7477794.html

3.1、sps和pps详解

在H.264标准协议中规定了多种不同的NAL Unit类型,其中类型7表示该NAL Unit内保存的数据为Sequence Paramater Set(序列参数集),描述这个序列的参数信息都在这里。在H.264的各种语法元素中,SPS中的这些参数信息至关重要。如果其中的数据丢失或出现错误,那么解码过程很可能会失败。SPS和PPS中的信息会用于播放器的初始化使用。

SPS中保存了一组视频序列的全局参数。所谓的视频序列即原始视频的一帧一帧的图像数据经过编码之后组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集(PPS)中,例如P帧需要参考前面的帧进行编码,那么是怎么参考的,这个参考数据就保存在PPS中。

一般情况SPS和PPS位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:

  • 解码器需要在码流中间开始解码;
  • 编码器在编码的过程中改变了码流的参数(如图像分辨率等);

视频播放器为了让后续的解码过程可以使用SPS中包含的参数,必须对SPS其中的数据进行解析。其中H.264标准协议中规定的SPS格式位于文档的7.3.2.1.1部分,如下图所示:

H.264帧结构和RTSP协议源码框架

(1) profile_idc:

标识当前H.264码流的profile。我们知道,H.264中定义了三种常用的档次profile:

基准档次:baseline profile;

主要档次:main profile;

扩展档次:extended profile;

在H.264的SPS中,第一个字节表示profile_idc,根据profile_idc的值可以确定码流符合哪一种档次。判断规律为:

profile_idc = 66(0x42) → baseline profile;

profile_idc = 77 → main profile;

profile_idc = 88 → extended profile;

在新版的标准中,还包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High 4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra等,每一种都由不同的profile_idc表示。

当前码流中,profile_idc = 0x42 = 66 ,因此profile档次为 baseline profile;

H.264帧结构和RTSP协议源码框架

(2) level_idc

标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。

H.264帧结构和RTSP协议源码框架

当前码流中,level_idc = 0x1F = 31,因此码流的级别为3.1。

Level为3.1的特性:

H.264帧结构和RTSP协议源码框架

3.2、H264的profile和level

参考博文:

h264中profile和level的含义_xiaojun11-的博客-CSDN博客

Profile是对视频压缩特性的描述(CABAC呀、颜色采样数等等),简而言之就是压缩算法的档次。Level是对视频本身特性的描述(码率、分辨率、fps)。

简单来说,Profile越高,就说明采用了越高级的压缩特性(越高级的压缩算法)。Level越高,视频的码率、分辨率、fps越高。

一些移动设备(手机、游戏机、PMP)由于性能有限,不支持全部高级视频压缩特性和高分辨率图像,只支持基础压缩特性和分辨率低一些的图像。为了让这个限制更加清晰明了,H264从低到高划分了很多Profile和Level。

profile主要是定义了编码工具(压缩算法)的集合,不同的profile,包含了不同的编码技术;

h.264的profile有三种Baseline Profile(基本的)、Main Profile(主线的)、High Profile(高等级的)

3.3、sequence

(1)一段h.264的码流其实就是多个sequence组成的

(2)每个sequence均有固定结构:1sps+1pps+1sei+1I帧+若干p帧

(3)sps和pps和sei描述该sequence的图像信息,这些信息有利于网络传输或解码

(4)I帧是关键,丢了I帧整个sequence就废了,因为I帧是自参考的,其它的帧都参考于它,每个sequence有且只有1个I帧

(5)p帧的个数等于fps-1

(6)I帧越大则P帧可以越小,反之I帧越小则P帧会越大

I帧越大,说明I帧本身压缩比例不高,参考信息比较多,比较详细,所以P帧就越好参考,就可以越小

(7)I帧的大小取决于图像本身内容,和压缩算法的空间压缩部分

如果图像本身色彩丰富、画面复制,那么I帧就会很大

(8)P帧的大小取决于图像变化的剧烈程度

(9)CBR和VBR下P帧的大小策略会不同,CBR时P帧大小基本恒定,VBR时变化会比较剧烈

CBR:固定码率

VBR:可变码率

4、rtsp协议和源码框架

rtsp协议参考:

链接:https://pan.baidu.com/s/1IHU8vaFShMq3mOUz3c2iDw?pwd=0000 
提取码:0000

4.1、源码框架函数调用关系

main
	RtspServer_init
		RtspServerListen								    //线程函数---达到PLAY状态
			socket                            		        //创建TCP套接字
			setsockopt                                 	    //设置端口复用
			bind                                      	    //绑定服务器IP和端口
			listen								            //监听、并告诉linux系统这是个服务器
			while(accept)							        //不断等待客户端连接
				setsockopt
				for                                         //实际只执行一次
					RtspClientMsg                           //此函数是重点,创建一个线程去处理这此连接
					    pthread_detach                      //分离线程,子线程结束后自动回收资源
					    while                               //如果当前不是未连接状态
					    {
						    recv                            //读1024个字节
						    if(没读到)
						    {
							    退出线程
						    }
						    ParseRequestString              //解析客户端发来的Requst请求并填充字符串数组    

						    if(是OPTIONS请求)
							    OptionAnswer                //返回服务器提供的可用方法
								    sprintf                 //组包
								    strcat                  //连接RTSP头和sdp信息
								    send                    //发送
						    if(是DESCRIBE请求)
							    DescribeAnswer              //返回sdp包给播放器client去配置解码
								    GetLocalIP              //获取本地IP
     								    	ioctl
								    sprintf                 //组包
								    send                    //发送
						    if(是SETUP请求)
							    SetupAnswer
								    ParseTransportHeader    //解析客户端发来的SETUP Request,然后填充到变量中
								    GetLocalIP              //获取本地IP
								    sprintf                 //组包
								    send                    //发送
						    if(是PLAY请求)
							    PlayAnswer
								    sprintf                 //组包
								    send                    //发送
								    socket                  //创建一个UDPsocket
								    //由此可见,之前的TCPsocket是一个命令通道,这里的UDPsocket是一个传输数据的通道
						    if(是PAUSE请求)                 //暂停请求
							    PauseAnswer                 //源码分析得知,这里并没有实现暂停码流的传输
								    sprintf                 //组包
								    send                    //发送
						    if(是TEARDOWN请求)
							    TeardownAnswer
								    sprintf                 //组包
								    send                    //发送
								    close(udp)              //关闭数据传输通道UDPsocket
							    close(tcp)                  //关闭命令传输通道TCPsocket
					      }   
		vdRTPSendThread                                     //线程函数---发送流媒体的数据
			while(1)                                        //每5ms判断当前是否有数据要发送
			{
				if(!list_empty)                             //链表为空,则表明当前没有数据要发送
				{
					get_first_item                          //取出链表中第一个非空节点
					VENC_Sent//发送码流数据
						sendto                              //UDP发送
					list_del                                //从链表中把节点去掉
				}
				usleep(5000);                               //延时5ms
			}
	SAMPLE_VENC_720P_CLASSIC
		SAMPLE_COMM_VENC_StartGetStream
			SAMPLE_COMM_VENC_GetVencStreamProc              //线程函数
				HI_MPI_VENC_GetChnAttr
				SAMPLE_COMM_VENC_GetFilePostfix
				HI_MPI_VENC_GetFd
				while
				{
					HI_MPI_VENC_Query
					malloc
					HI_MPI_VENC_GetStream                   //获取一帧编码完的码流

					//以下两种方式二选一
					SAMPLE_COMM_VENC_Sentjin                //此函数是重点---编码完后直接发送
						VENC_Sent                           //直接发送    
					saveStream                              //此函数是重点---编码完后保存到环状buf中
						malloc                              //生成一个链表节点
						INIT_LIST_HEAD                      //初始化这个节点的next指针和prev指针都指向自己
						填充节点内容
						list_add_tail                       //将节点加入链表
					//当我将编码完的数据保存到环形链表后,此时链表就有节点数据了
					//然后就可以在 vdRTPSendThread 线程中发送了

					HI_MPI_VENC_ReleaseStream
					free
				}

4.2、直接发送与环状buffer发送

直接发送:编码完直接发送,编多少发多少

代码体现在:

SAMPLE_COMM_VENC_GetVencStreamProc线程函数:
    HI_MPI_VENC_GetStream            //获取编码完的码流
    SAMPLE_COMM_VENC_Sentjin         //发送函数

缺陷:视频采集和编码速度 与 网络传输速度 都是变动的,速度不一定一致,会导致系统进入漫长的等待,不能更好的实时视频监控

环状buffer发送:编码完放到环状buffer中,待发送

环状buffer即环形链表,当生产者(获取码流的线程)编码完一帧数据就将这帧数据作为节点加入链表,消费者(发送线程)只管从中取。

对比代码:SAMPLE_COMM_VENC_GetVencStreamProc线程函数中,调用HI_MPI_VENC_GetStream获取编码完的码流,调用saveStream将码流数据节点加入链表,vdRTPSendThread线程从链表中取走节点,然后调用VENC_Sent发送。

当链表中没有节点,则消费者要阻塞,等待生产者编码完将数据节点加入链表。环形链表相比数组的优点是使用内存可以足够大,直到内存撑爆。

代码体现在:

SAMPLE_COMM_VENC_GetVencStreamProc线程函数:
        HI_MPI_VENC_GetStream         //获取编码完的码流
        saveStream                    //编码完后保存到环状buf中
vdRTPSendThread线程函数:
        get_first_item                //取出链表中第一个非空节点
        VENC_Sent                     //发送码流数据
        list_del                      //从链表中把节点去掉

接下来就只用关注VENC_Sent函数内是怎么组包,然后通过UDP传输码流数据了。。

4.3、RTP发送一帧数据的两种不同发送格式->整发送和分包发送

关于rtp头的timestamp位:
RTP timestamp与帧率及时钟频率的关系_帧率 时钟频率_jasonhwang的博客-CSDN博客

关于RTP发送一帧数据的两种不同发送格式:整包发送和分包发送

https://www.cnblogs.com/yjg2014/p/6144977.html

VENC_Sent函数全解:文章来源地址https://www.toymoban.com/news/detail-488439.html

HI_S32 VENC_Sent(char *buffer,int buflen)
{
    HI_S32 i;
    int is=0;
    int nChanNum=0;

    for(is=0;is<MAX_RTSP_CLIENT;is++)
    {
        if(g_rtspClients[is].status!=RTSP_SENDING)//判断是否是 可发送 状态
        {
            continue;
        }
        int heart = g_rtspClients[is].seqnum % 10000;//seqnum:序列号,对10000取余
        
        char* nalu_payload;
        int nAvFrmLen = 0;
        int nIsIFrm = 0;
        int nNaluType = 0;
        char sendbuf[500*1024+32];

    
        nAvFrmLen = buflen;//音频或视频数据一帧的长度

        struct sockaddr_in server;
        server.sin_family=AF_INET;
           server.sin_port=htons(g_rtspClients[is].rtpport[0]);   //对方的端口       
           server.sin_addr.s_addr=inet_addr(g_rtspClients[is].IP);//对方的IP
        int    bytes=0;
        unsigned int timestamp_increse=0;
        
        timestamp_increse=(unsigned int)(90000.0 / 25);    //  两帧之间RTP timestamp的增量 = 时钟频率 / 帧率

        rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0];         //填充RTP包头
    
        rtp_hdr->payload     = RTP_H264;   
        rtp_hdr->version     = 2;                          //RTP版本号
        rtp_hdr->marker    = 0;           
        rtp_hdr->ssrc      = htonl(10);                   //信源标记

        if(nAvFrmLen<=nalu_sent_len)    //如果这一帧数据的长度<=1400
        {
            rtp_hdr->marker=1;                            //标记一帧的结束
            rtp_hdr->seq_no     = htons(g_rtspClients[is].seqnum++);     //下一帧的序列号
            nalu_hdr =(NALU_HEADER*)&sendbuf[12]; 
            nalu_hdr->F=0; 
            nalu_hdr->NRI=  nIsIFrm; 
            nalu_hdr->TYPE=  nNaluType;
            nalu_payload=&sendbuf[13];
            memcpy(nalu_payload,buffer,nAvFrmLen);
                    g_rtspClients[is].tsvid = g_rtspClients[is].tsvid+timestamp_increse;            
            rtp_hdr->timestamp=htonl(g_rtspClients[is].tsvid);
            bytes=nAvFrmLen+ 13 ;                
            sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
        }
        else if(nAvFrmLen>nalu_sent_len)    //如果这一帧数据的长度>1400
        {
            int k=0,l=0;
            k=nAvFrmLen/nalu_sent_len;                    //这一帧数据的长度/1400
            l=nAvFrmLen%nalu_sent_len;                    //这一帧数据的长度对1400取余
            int t=0;        

            g_rtspClients[is].tsvid = g_rtspClients[is].tsvid+timestamp_increse;    //计算timestamp
            rtp_hdr->timestamp=htonl(g_rtspClients[is].tsvid);                 //把timestamp放入RTP包头       
            while(t<=k)            //循环k+1次,也就是发k+1个包,代表这一帧发完
            {
                rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
                if(t==0)        //第一包
                {
                    rtp_hdr->marker=0;            //填充完RTP_header的12个字节后,接下来填充的是NALU的分包形式
                    fu_ind =(FU_INDICATOR*)&sendbuf[12];//fu_indicato是NALU头的分包形式的第一部分
                    fu_ind->F= 0; 
                    fu_ind->NRI= nIsIFrm;
                    fu_ind->TYPE=28;                    //Type为FU-A
        
                    fu_hdr =(FU_HEADER*)&sendbuf[13];    //fu_header     是NALU头的分包形式的第二部分
                    fu_hdr->E=0;
                    fu_hdr->R=0;
                    fu_hdr->S=1;                        //表示帧开始
                    fu_hdr->TYPE=nNaluType;                //NALU type
    
                    nalu_payload=&sendbuf[14];    //填充完NALU的分包形式后,接下来开始填充有效数据
                    memcpy(nalu_payload,buffer,nalu_sent_len);
    
                    bytes=nalu_sent_len+14;        //发送的长度是有效数据长度+14(14就是前面添加的这些信息)                
                    sendto( udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
                    t++;
    
                }
                else if(k==t)    //最后一包
                {
                    rtp_hdr->marker=1;                    //标记一帧的结束
                    fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
                    fu_ind->F= 0 ;
                    fu_ind->NRI= nIsIFrm ;
                    fu_ind->TYPE=28;

                    fu_hdr =(FU_HEADER*)&sendbuf[13];
                    fu_hdr->R=0;
                    fu_hdr->S=0;
                    fu_hdr->TYPE= nNaluType;
                    fu_hdr->E=1;                        //表示帧结束,和marker一样
                    nalu_payload=&sendbuf[14];
                    memcpy(nalu_payload,buffer+t*nalu_sent_len,l);
                    bytes=l+14;        
                    sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
                    t++;
                }
                else if(t<k && t!=0)    //中间的包
                {

                    rtp_hdr->marker=0;

                    fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
                    fu_ind->F=0; 
                    fu_ind->NRI=nIsIFrm;
                    fu_ind->TYPE=28;
                    fu_hdr =(FU_HEADER*)&sendbuf[13];
                    //fu_hdr->E=0;
                    fu_hdr->R=0;
                    fu_hdr->S=0;
                    fu_hdr->E=0;                        //S=0,E=0表示既不是开头也不是结尾
                    fu_hdr->TYPE=nNaluType;
                    nalu_payload=&sendbuf[14];
                    memcpy(nalu_payload,buffer+t*nalu_sent_len,nalu_sent_len);
                    bytes=nalu_sent_len+14;    
                    sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
                    t++;
                }
            }
        }

    }

    //------------------------------------------------------------
}

到了这里,关于H.264帧结构和RTSP协议源码框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 视频编码标准H.264/AVC,H.265/HEVC,VP8/VP9,AV1的基本原理、优缺点以及适用场景

    视频编码标准是用于压缩数字视频数据的技术规范,以减少存储和传输所需的带宽。以下是关于H.264/AVC、H.265/HEVC、VP8/VP9和AV1这些标准的基本原理、优缺点以及适用场景的简要描述: H.264/AVC (Advanced Video Coding) 基本原理 : H.264是一种块导向的运动补偿视频压缩标准。它使用混

    2024年03月16日
    浏览(48)
  • 【vue2】前端如何播放rtsp 视频流,拿到rtsp视频流地址如何处理,海康视频rtsp h264 如何播放

    最近在写vue2 项目其中有个需求是实时播放摄像头的视频,摄像头是 海康 的设备,搞了很长时间终于监控视频出来了,记录一下,放置下次遇到。文章有点长,略显啰嗦请耐心看完。 测试?测试什么?测试rtsp视频流能不能播放。 video mediaplay官网 即(VLC) 下载、安装完VLC后

    2024年02月05日
    浏览(39)
  • 视频编码(H264编码)

    基本步骤:    1、打开编码器          2、转换NV12到YUV420,这是因为FFmpeg的x264编码器只支持YUV420          3、准备编码数据AVFrame         创建frame:  创建AVPacket: AVPacket* pck = av_packet_alloc(); 4、H264编码       avcodec_send_frame(#AVCodecContext *avctx#, #const AVFrame *frame#)    

    2024年02月03日
    浏览(31)
  • Android 引入FFmpeg 读取RTSP流 解封装获取H264原始数据

    之前 写了Android中怎么引入FFMmpeg的例子 。 本编文章将会写一个简单的demo实现ffmpeg拉去rtsp流并在界面中打印前五个字节 懒得往下细看的可以点击这里下载工程 基于andorid studio  实际效果下图: android 用ffmpeg 拉取rtsp流 解出h264数据 看下目录结构: 很简单 应用进去之后有一个

    2024年02月03日
    浏览(39)
  • JAVA实现H264视频流推送到RTSP、RTMP服务----JavaCV

    前提: 1.准备好rtsp、rtmp服务 2.准备好视频流接收程序 基本思路是:启动两个线程,线程1接收视频流,线程2使用JavaCV将视频流推送到RTSP、RTMP服务,两者之间使用管道流进行通信。线程2接收到视频流后的具体操作:启动grabber接收视频流并捕获视频帧,然后启动recoder将捕获的

    2024年02月11日
    浏览(45)
  • 基于Live555实现RTSP服务器来推送H264实时码流

    实现了一个单播的rtsp服务器来推送实时的h264码流,参考了官方的testProgs目录下的testOnDemandRTSPServer例程和liveMedia目录下的DeviceSource.cpp文件。我这边是把编码出来的h264码流放入了一个缓冲队列,然后从缓冲队列里取出来进行推流。 rtsp.h: rtsp.cpp:  

    2024年02月05日
    浏览(40)
  • H.264编码及AAC编码基础

    本节重点讲解了 H.264 编码以及 AAC 编码,在对其进行讲解前先介绍了视频编码的实现原理。 编码就是为了压缩。要实现压缩,就要设计各种算法,将视频数据中的冗余信息去除。 种类 内容 压缩方法 空间冗余 像素间的相关性 变换编码,预测编码 时间冗余 时间方向上的相关

    2024年02月09日
    浏览(31)
  • Kratos框架源码解读-目录

    日志

    2024年02月11日
    浏览(22)
  • 视频编码流程 YUV数据编码为H264数据

    视频编码时,设置编码器上下文参数有:码率,宽,高,格式,帧率等 frame设置:宽,高,格式 然后计算一帧的数据:宽 * 高 * 格式占用字节数 通常使用av_image_get_buffer_size来计算 av_opt_set就是用来设置参数的 preset preset参数是一个权衡编码速度和压缩率的参数,编码速度越慢,压缩率越高 参数

    2024年02月13日
    浏览(33)
  • 264 编码 profile level 分析

    H.264是一种广泛使用的视频压缩标准,它利用先进的算法和技术,可以将高清视频压缩到相对较小的文件大小,而且不会影响视频的质量。在H.264中,编码profile和level是非常重要的概念,本文将会对它们进行详细分析。 一、H.264编码profile H.264编码profile是用来描述视频压缩的方

    2023年04月08日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包