hi3516a——RTSP播放H.264视频流(1)

前言

由于hi3516a海思自带的开发应用程序是通过摄像头接口或HDMI接口获取视频数据后并进行存储。然而,在实际应用中,多是获取数据后直接通过网络把数据发送出去。那么本文章将开始学习hi3516a获取数据后通过网线和RTP协议把数据实时发送出去。
背景:hi3516a开发板通过HDMI接口获取BT1120数据后进行压缩,并通过RTP协议进行实时的视频直播。
硬件平台:hi3516a
软件平台:Hi3516A_SDK_V1.0.5.0
视频数据接口:HDMI

无私分享,从我做起!

源码解析

下面首先看主程序的源码,源码来源于网络,一步一步进行分析。

int main(int argc, char* argv[])
{
	int s32MainFd,temp;
	struct timespec ts = { 0, 200000000 };  //200000000ns=200000us=200ms=0.2s
	pthread_t id;
	ringmalloc(1920*1080);//分配缓冲区并进行初始化
	printf("RTSP server START\n");
	PrefsInit();//设置服务器信息全局变量,获取主机name
	printf("listen for client connecting...\n");
	signal(SIGINT, IntHandl);  //异常中止信号处理
	s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT);  //以非阻塞方式侦听554端口
	printf("s32MainFd=%d\r\n",s32MainFd);
	if (ScheduleInit() == ERR_FATAL) 
	//线程的结构体进行初始化,创建处理主线程schedule_do,schedule_do里调用sched[i].play_action把数据发送出去
	{
		fprintf(stderr,"Fatal: Can't start scheduler %s, %i \nServer is aborting.\n", __FILE__, __LINE__);
		return 0;
	}
	RTP_port_pool_init(RTP_DEFAULT_PORT); //554,初始化10个RTP port
	pthread_create(&id,NULL,SAMPLE_VENC_1080P_CLASSIC_RTSP,NULL);  //海思芯片内部获取编码后数据
	while (!g_s32Quit)
	{
		nanosleep(&ts, NULL);
		EventLoop(s32MainFd);//RTSP服务器连接处理函数入口
	}
	sleep(2);
	ringfree();
	printf("The Server quit!\n");

	return 0;
}

(1)void ringmalloc(int size)

其中ringmalloc(1920*1080);是分配缓冲区并进行初始化,我的hdmi数据数据分辨率是1920X1080p,下面看下ringmalloc()函数源码。主要是分配64个1920X1080p的fifo空间并初始化一些参数。

/* 环形缓冲区的地址编号计算函数,如果到达唤醒缓冲区的尾部,将绕回到头部。
环形缓冲区的有效地址编号为:0到(NMAX-1)
*/
void ringmalloc(int size)
{
    int i;
    for(i =0; i<NMAX; i++)  //64
    {
        ringfifo[i].buffer = malloc(size);
        ringfifo[i].size = 0;
        ringfifo[i].frame_type = 0;
        printf("FIFO INFO:idx:%d,len:%d,ptr:%x\n",i,ringfifo[i].size,(int)(ringfifo[i].buffer));
    }
    iput = 0; /* 环形缓冲区的当前放入位置 */
    iget = 0; /* 缓冲区的当前取出位置 */
    n = 0; /* 环形缓冲区中的元素总数量 */
}

(2)void PrefsInit()

接着查看PrefsInit()函数,主要是设置服务器信息全局变量,获取主机name。

void PrefsInit()
{
	int l;
	//设置服务器信息全局变量
	stPrefs.port = SERVER_RTSP_PORT_DEFAULT;  //554

	gethostname(stPrefs.hostname,sizeof(stPrefs.hostname));
	
	l=strlen(stPrefs.hostname);
	
	if (getdomainname(stPrefs.hostname+l+1,sizeof(stPrefs.hostname)-l)!=0)
	{
		stPrefs.hostname[l]='.';
	}

#ifdef RTSP_DEBUG
	printf("-----------------------------------\n");
	printf("\thostname is: %s\n", stPrefs.hostname);
	printf("\trtsp listening port is: %d\n", stPrefs.port);
	printf("\tInput rtsp://hostIP:%d/test.264 to play this\n",stPrefs.port);
	printf("\n");
#endif
}

(3)s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT);

接下来,s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT);//以非阻塞方式侦听554端口,554 是rtsp的默认端口号;具体网络编程这一块的知识参考传送门

int tcp_listen(unsigned short port)
{
    int f;
    int on=1;

    struct sockaddr_in s;
    int v = 1;

    /*创建套接字*/
    if((f = socket(AF_INET, SOCK_STREAM, 0))<0)  //AF_INET  ipv4   SOCK_STREAM   流模式,tcp
    {
        fprintf(stderr, "socket() error in tcp_listen.\n");
        return -1;
    }

    /*设置socket的可选参数*/
    setsockopt(f, SOL_SOCKET, SO_REUSEADDR, (char *) &v, sizeof(int));

    s.sin_family = AF_INET;
    s.sin_addr.s_addr = htonl(INADDR_ANY);
    s.sin_port = htons(port);

    /*绑定socket*/
    if(bind(f, (struct sockaddr *)&s, sizeof(s)))
    {
        fprintf(stderr, "bind() error in tcp_listen");
        return -1;
    }

    //设置为非阻塞方式
    if(ioctl(f, FIONBIO, &on) < 0)
    {
        fprintf(stderr, "ioctl() error in tcp_listen.\n");
        return -1;
    }

    /*监听*/
    if(listen(f, SOMAXCONN) < 0)
    {
        fprintf(stderr, "listen() error in tcp_listen.\n");
        return -1;
    }

    return f;
}

(4)int ScheduleInit()

接下来是便是调用ScheduleInit()函数,该函数对线程的结构体进行初始化,创建处理主线程schedule_do,schedule_do里调用sched[i].play_action把数据发送出去。ScheduleInit()函数是本文章分析的重点,下面来详细分析该函数。

int ScheduleInit()
{
    int i;
    pthread_t thread=0;

    /*初始化数据*/
    for(i=0; i<MAX_CONNECTION; ++i)  //MAX_CONNECTION=10,最大允许连接10个客户端
    {
        sched[i].rtp_session=NULL;
        sched[i].play_action=NULL;
        sched[i].valid=0;
        sched[i].BeginFrame=0;
    }

    /*创建处理主线程*/
    pthread_create(&thread,NULL,schedule_do,NULL);  //创建schedule_do线程

    return 0;
}

schedule_do线程的源码如下:

void *schedule_do(void *arg)
{
    int i=0;
    struct ti
  • 14
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值