rtp h264 发送和接收程序的问题

news/2024/11/23 13:16:08/

目的

为了自己写一个投屏协议,目前重新启用rtp协议,使用rtp协议直传效率最高,并且使用的是udp

ffmpeg 发送rtp

ffmpeg的rtp发送时一般把sps和pps放在一个包里面,写接收代码的时候要注意,在单包里面可以直接接收到两个,不过自己写的代码就不一样了,很多人喜欢把sps和pps 分开来发送,写错了,就会丢包

libx264 和 ffmpeg编码发送

直接使用libx264发送

int X264Encoder::Encode4RTP(unsigned char * szYUVFrame, bool &isKeyframe)
{int numPixels = m_param.i_width * m_param.i_height;m_pic.img.plane[0] = szYUVFrame;m_pic.img.plane[1] = szYUVFrame + numPixels;m_pic.img.plane[2] = szYUVFrame + numPixels * 5 / 4;m_pic.img.i_stride[0] = m_param.i_width;m_pic.img.i_stride[1] = m_param.i_width / 2;m_pic.img.i_stride[2] = m_param.i_width / 2;m_param.i_frame_total++;m_pic.i_pts = (int64_t)m_param.i_frame_total * m_param.i_fps_den;//dts赋值和pts相同的值m_pic.i_dts = m_pic.i_pts;if (isKeyframe)m_pic.i_type = X264_TYPE_IDR;elsem_pic.i_type = X264_TYPE_AUTO;int i_frame_size = x264_encoder_encode(m_h, &_naluIter_t, &_naluIter, &m_pic, &_pic_out);if (i_frame_size > 0){isKeyframe = (_pic_out.i_type == X264_TYPE_IDR);return 0;}return -1;
}

初始化 x264encoder, 发送rtp的关键在于每个关键帧的前面有sps pps, 实际上就是让

m_param.b_repeat_headers = 1;

不过默认值就是1 不用设置。


int X264Encoder::Initialize(int iWidth, int iHeight, int iRateBit, int iFps)
{x264_param_default_preset(&m_param, "ultrafast", "zerolatency");m_param.i_csp = X264_CSP_I420;m_param.i_width = iWidth;m_param.i_height = iHeight;m_param.i_fps_num = iFps;m_param.i_fps_den = 1;m_param.rc.i_bitrate = iRateBit;m_param.rc.i_rc_method = X264_RC_ABR;m_param.i_frame_reference = 2; /* 参考帧的最大帧数 *///m_param.i_keyint_max = 8;//m_param.i_keyint_min = 4;m_param.i_frame_total = 0;m_param.i_bframe = 0;m_param.i_threads = 1;m_param.rc.i_lookahead = 0;//m_param.i_sync_lookahead = X264_SYNC_LOOKAHEAD_AUTO;m_param.i_sync_lookahead = 0;m_param.b_cabac = 1;m_param.analyse.b_transform_8x8 = 1;//m_param.b_repeat_headers = 1;m_param.i_level_idc = 12;//x264_param_apply_profile(&m_param, x264_profile_names[0]);m_param.i_log_level = X264_LOG_NONE;//X264_LOG_WARNING;// X264_LOG_ERROR;//X264_LOG_NONE;x264_param_apply_profile(&m_param, "baseline");/* 根据输入参数param初始化总结构 x264_t *h     */if( ( m_h = x264_encoder_open( &m_param ) ) == NULL ){//fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" );return -1;}//x264_picture_alloc( &m_pic, X264_CSP_I420, m_param.i_width, m_param.i_height );x264_picture_init(&m_pic);memset(&m_pic, 0, sizeof(x264_picture_t));m_pic.i_type = X264_TYPE_AUTO;m_pic.i_qpplus1 = 0;m_pic.img.i_csp = X264_CSP_I420;m_pic.img.i_plane = 3;return 0;
}

ok,怎么发送比较效率高怎么改代码

ffmpeg 编码发送

对于ffmpeg来说,比较关键的就是 AV_CODEC_FLAG_GLOBAL_HEADER,这个值就是决定param中是否repeat header, 也就是:是否在关键帧前面加sps 和pps,如果是rtmp协议,我们加上,而rtp协议,我们不加。 这样在

_vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //全局参数

每个关键帧的前面都会加上sps和pps,这样使用vlc 这种工具写一个sdp文件,打开的时候碰到关键帧就可以解码出来。

时间戳

注意时间戳的基数是90000,为什么使用90000,因为这个数字让一些比较奇怪的帧数间隔时间也能成为整数。

发送事件间隔

这个太关键了,发送的时候一定要按照正式的帧率来发送,以下为示例代码
代码使用libx264 进行编码,当然使用openh254 , ffmpeg也是一样的,计算好时间间隔发送,如果为20帧,那就要每帧间隔50毫秒。

void VideoEncoderThread::Run()
{// 开始循环获取每一帧,并编码unsigned int timestamp = 0;unsigned int last_idr_timestamp = 0;bool is_first = true;//int x264buf_len = 1024 * 512;//unsigned char* x264buf = (unsigned char*)malloc(x264buf_len);int inter = 1000 / v_fps; // 50;while (false == IsStop()){//char* rgbbuf = ds_video_graph_->GetBuffer();unsigned int now_tick = ::GetTickCount();unsigned int next_tick = now_tick + inter;bool is_keyframe = false;timestamp = now_tick;if (timestamp - last_idr_timestamp >= 2000 || last_idr_timestamp ==0 ){is_keyframe = true;last_idr_timestamp = timestamp;}{std::unique_lock<std::mutex> lock(v_mt);x264_encoder_->Encode4RTP(v_yuvbuf, is_keyframe);}if (ok){int num = x264_encoder_->GetNaluNumber();int size = 0;for (int i = 0; i < num; i++){uint8_t *buf = x264_encoder_->GetNalu(i, size);sendrtp(buf, size, timestamp, is_keyframe);}}now_tick = ::GetTickCount();if (next_tick > now_tick){Sleep(next_tick - now_tick);}}//free(yuvbuf);//free(x264buf);//free(encoder_rgbbuf);/* if (fp_264){fclose(fp_264);}*/
}

摄像头问题

摄像头问题就很多了,web摄像头 也就是usb摄像头等什么时候拿到一帧是不确定的,就算是设定20帧,也未一秒钟必能拿到20帧,所以当中的缓存至关重要,如果不够,就要加帧,
1简单的做法 : 使用上一帧加帧
2 使用中间缓存,定时取帧,不要管上一帧下一帧是否相同,但是中间缓存要枷锁。

sdp 文件

v=0
m=video 6000 RTP/AVP 96
a=rtpmap:96 H264/90000
c=IN IP4 127.0.0.1

写好rtp 发送以后, 将以上文件存成test.sdp, 任何时候直接用vlc打开sdp文件 ,就可以看到图像,以上是在本机的6000端口 等待数据

界面显示

为了跨平台,用qt新做了一个发送界面,
在这里插入图片描述
使用vlc 接收
在这里插入图片描述


http://www.ppmy.cn/news/69601.html

相关文章

Vue3添加动态路由及项目搭建

安装项目 yarn create vite vue3-project安装依赖包 yarn add types/node -D在tsconfig.json中配置别名 "baseUrl": "./","paths": {"/*":["src/*"]},在vite中进行配置 import { defineConfig } from vite import vue fr…

Java-锁相关

线程不安全的原因 1.调度器随机调度,抢占式执行(无能为力) 举个例子 有一个int变量 叫count 就俩线程同时count一万次 结果应该为两万 可多次运行程序 这结果每次都不一样(而且小于2w) 是为什么呢 因为count这行代码是分三步运行的 load 把数据读到cpu add 在cpu寄存器实现加法…

5.Golang、Java面试题—Spring Cloud、Docker、kubernets(k8s)

本文目录如下&#xff1a; Golang、Java面试题二十、Spring Cloud什么是微服务架构&#xff1f;服务拆分 有哪些注意事项&#xff1f;什么是分布式集群?分布式的 CAP 原则&#xff1f;组件 - Spring Cloud 哪几个组件比较重要&#xff1f;组件 - 为什么要使用这些组件&#xf…

使用Spring Boot集成Spring Scenery的例子

使用Spring Boot集成Spring Scenery的例子 1 介绍 Spring Boot是一个用于构建独立应用程序的快速开发框架,而Spring Scenery是一个用于管理和构建Spring应用程序的命令行工具。 在这个例子中,我们将介绍如何使用Spring Boot集成Spring Scenery来创建和运行一个Spring应用程…

typeScript中的类以及类的限制

传统方法中&#xff0c;JavaScript通过构造函数实现类的概念&#xff0c;通过原型链实现继承。而在Es6中&#xff0c;我们终于迎来了class. typeScript除了实现了所有Es6中类的功能以外&#xff0c;还添加了一些新的用法。 类的概念 虽然 JavaScript 中有类的概念&#xff0…

分拆数简介

分拆数是对正整数 n n n 进行无序分拆的方案数。 举个例子&#xff0c;对 5 5 5 进行分拆&#xff0c;有以下几种方案。 1 1 1 1 1 11111 11111 2 1 1 1 2111 2111 2 2 1 221 221 3 1 1 311 311 3 2 32 32 4 1 41 41 5 5 5 如何求分拆数呢&#xff1f; 前置知…

Apache Zeppelin系列教程第六篇——Zengine调用Interpreter原理分析

Apache Zeppelin系列教程第五篇——Interpreter原理分析_诸葛子房_的博客-CSDN博客 Apache Zeppelin系列教程第四篇——JDBCInterpreter原理分析_诸葛子房_的博客-CSDN博客 前文介绍jdbc interpreter和interpreter模块交互代码&#xff0c;本篇文章主要分析Zengine调用Interp…

Python自动化测试实现的思路及方法

Python自动化测试常用于Web应用、移动应用、桌面应用等的测试 Python自动化实现思路通常分为以下几步&#xff1a; 1. 确定自动化测试的范围和目标&#xff1a; 首先需要明确需要进行自动化测试的范围和目标&#xff0c;包括测试场景、测试用例、测试数据等。 2. 选择自动化测…