参考链接: 1. PES,TS,PS,RTP等流的打包格式解析之RTP流 https://blog.csdn.net/appledurian/article/details/73135343
      2. RTP协议全解析(H264码流和PS流)https://blog.csdn.net/chen495810242/article/details/39207305

(重要)以下代码并未实测,除ts的发送外,其余都是伪代码(并且未搜集资料查询思路是否正确), 这边只为自己记录,参考请谨慎, 自己记录下而已。

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h> static union { char c[]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } }; #define ENDIANNESS ((char)endian_test.mylong) #define PRINTF_DEBUG
#define TAB44 " " #define MAX_ARGS_FILEFORMAT 10
#define MAX_ARGS_FILEPATH 128
#define MAX_RTPURL_IP 128
#define MAX_ARGS_RTPURL 256
#define MTU 1400 #define DEFAULT_FILE_PATH "./videos/mux/ts_test.ts"
#define DEFAULT_FILE_FORMAT "ts"
#define DEFAULT_RTP_URL "rtp://127.0.0.1:8888" #define DEFAULT_ARGS {0, DEFAULT_FILE_PATH, DEFAULT_FILE_FORMAT, DEFAULT_RTP_URL} /* define4ts */
#define MAX_TS_PACKET_COUNT 7
#define TS_PACKET_LEN 188 /* define4ps */
#define SCODE_PS_END 0x000001B9
#define SCODE_PS_HEADER 0x000001BA
#define SCODE_PS_SYSTEM_HEADER 0x000001BB
#define SCODE_PS_SYSTEM_MAP_HEADER 0x000001BC /* define4mpeg2 */
typedef enum e_mpeg2_sc_type
{
E_SC_MPEG2_SEQ_HEADER = 0x000001B3,
E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER = 0x000001B5,
E_SC_MPEG2_SEQ_END = 0x000001B7,
E_SC_MPEG2_GROUP_HEADER = 0x000001B8,
E_SC_MPEG2_PICTURE_HEADER = 0x00000100
} E_MPEG2_SC_TYPE; typedef enum e_rtp_playload_type
{
E_RTP_PLAYLOAD_TS = ,
E_RTP_PLAYLOAD_PS = ,
E_RTP_PLAYLOAD_MPEG4 = ,
E_RTP_PLAYLOAD_H264 = ,
} E_RTP_PLAYLOAD_TYPE; typedef struct t_args
{
unsigned short isLoop; unsigned char filePath[MAX_ARGS_FILEPATH+];
unsigned char fileFormat[MAX_ARGS_FILEFORMAT+];
unsigned char rtpUrl[MAX_ARGS_RTPURL+];
} T_ARGS; /******************************************************
个人理解
1. 位域内单字节的内存排布是定义的先后, 先定义的在内存的低地址;
2. 位域内单字节, 字节由高到低, 先定义的为高字节;
3. 因此对于小端(低地址放低字节).
******************************************************/
typedef struct t_rtp_header
{
#if 1 /* 小端, BIG_ENDIAN系统宏, 暂不知道怎么用 */
/* bytes 0 */
unsigned char csrc_len:;
unsigned char extension:;
unsigned char padding:;
unsigned char version:;
/* bytes 1*/
unsigned char playload:;
unsigned char marker:;
#else
/* bytes 0 */
unsigned char version:;
unsigned char padding:;
unsigned char extension:;
unsigned char csrc_len:;
/* bytes 1*/
unsigned char marker:;
unsigned char playload:;
#endif /* byte 2, 3 */
unsigned short seq_no;
/* bytess 4-7 */
unsigned int timestamp;
/* bytes 8-11 */
unsigned int ssrc;
} T_RTP_HEADER; /* gloabl data */
FILE *fp = NULL; struct sockaddr_in servAddr; T_ARGS defaultArgs = DEFAULT_ARGS; static void Usage(void)
{
fprintf(stderr, "usage: rtpserver [options]\n\n"
"Options:\n"
"-l | --stream_loop Read and send strame for loop\n"
"-i | --filepath File need to send\n"
"-f | --fileformat Container of file(support ts, ps, h264, mpeg2, flv)\n"
"-s | --rtpurl Rtp url include ip and port\n"
"-h | --help Print this message\n");
} /******************************************************************************
1. const char shortOpt[] = "li:f:s:h";
单个字符表示选项;
单个字符后接一个冒号, 表示后面必须跟一个参数. 参数紧跟选项后或者加一个空格;
单个字符后接两个冒号, 表示可有也可没有, 参数紧跟选项后, 不能加空格.
2. 参数的值赋给了optarg;
3. c = getopt_long(argc, argv, shortOpt, longOpt, NULL);
返回值为参数字符, 若全部解析完成则返回-1.
******************************************************************************/
static void ParseArgs(int argc, char *argv[], T_ARGS *args)
{
int c = ; const char shortOpt[] = "li:f:s:h";
const struct option longOpt[] = {
{"stream_loop", no_argument, NULL, 'l'},
{"filepath", required_argument, NULL, 'i'},
{"fileformat", required_argument, NULL, 'f'},
{"rtpurl", required_argument, NULL, 's'},
{"help", no_argument, NULL, 'h'},
{, , , }
}; for (;;)
{
c = getopt_long(argc, argv, shortOpt, longOpt, NULL); if (- == c)
{
break;
} switch (c)
{
case 'l':
args->isLoop = ; break; case 'i':
memcpy(args->filePath, optarg, strlen(optarg)); args->filePath[strlen(optarg)] = '\0'; break; case 'f':
if (( != strcmp(optarg, "ts"))
&& ( != strcmp(optarg, "ps")
&& ( != strcmp(optarg, "h264")
&& ( != strcmp(optarg, "mpeg2")
&& ( != strcmp(optarg, "flv"))
{
Usage(); exit();
} memcpy(args->fileFormat, optarg, strlen(optarg)); args->fileFormat[strlen(optarg)] = '\0'; break; case 's':
memcpy(args->rtpUrl, optarg, strlen(optarg)); args->rtpUrl[strlen(optarg)] = '\0'; break; default:
Usage(); exit();
}
}
} static void Parse_RtpUrl(unsigned char* const rtpUrl, unsigned char *urlIp, unsigned short *urlPort)
{
unsigned short port = ; unsigned char *url = NULL;
unsigned char *portStart = NULL; url = rtpUrl; url += strlen("rtp://"); portStart = strstr(url, ":"); port = atoi(portStart+); *urlPort = port; memcpy(urlIp, url, portStart-url);
} static unsigned long GetTickCount()
{
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * + ts.tv_nsec / );
} static void Rtp_Header_Costruct(T_RTP_HEADER *rtpHeader, E_RTP_PLAYLOAD_TYPE playloadType)
{
static unsigned short seqNo = ; rtpHeader->version = ;
rtpHeader->padding = ;
rtpHeader->extension = ;
rtpHeader->csrc_len = ;
rtpHeader->marker = ;
rtpHeader->playload = playloadType;
rtpHeader->seq_no = seqNo++;
rtpHeader->timestamp = htonl(GetTickCount()*/);
rtpHeader->ssrc = htonl();
} static void Rtp_DealTs(int socketFd)
{
int readLen = ;
int bufCount = ;
int packetCount = ; unsigned char rtpBuf[MTU] = {}; memset(rtpBuf, 0x0, MTU); Rtp_Header_Costruct((T_RTP_HEADER*)rtpBuf, E_RTP_PLAYLOAD_TS); bufCount = sizeof(T_RTP_HEADER); while ()
{
if (feof(fp))
{
if (defaultArgs.isLoop)
{
rewind(fp); packetCount = ; memset(rtpBuf, 0x0, MTU); Rtp_Header_Costruct((T_RTP_HEADER*)rtpBuf, E_RTP_PLAYLOAD_TS); bufCount = sizeof(T_RTP_HEADER);
}
else
{
break;
}
} readLen = fread(rtpBuf+bufCount, , TS_PACKET_LEN, fp); packetCount++;
bufCount += readLen; if (packetCount>=MAX_TS_PACKET_COUNT)
{
sendto(socketFd, rtpBuf, bufCount, , (const struct sockaddr*)&servAddr, sizeof(servAddr)); packetCount = ; memset(rtpBuf, 0x0, MTU); Rtp_Header_Costruct((T_RTP_HEADER*)rtpBuf, E_RTP_PLAYLOAD_TS); bufCount = sizeof(T_RTP_HEADER); usleep(); // 应根据帧率发送或者可实现rtcp来动态控制发送速度
}
}
} static void Rtp_DealPs(int socketFd)
{
int readLen = ; unsigned int startCode = ; while ()
{
if ( != fread(&startCode, , , fp))
{
break;
} switch (startCode)
{
case SCODE_PS_END:
break; case SCODE_PS_HEADER:
/* get and send, like psparse.c */ break; case SCODE_PS_SYSTEM_HEADER:
/* get and send, like psparse.c */ break; case SCODE_PS_SYSTEM_MAP_HEADER:
/* get and send, like psparse.c */ break; default:
/*
1. get and send, like psparse.c;
2. here data mybe>MTU, 分包, 每次发MTU, 直到全部完成;
3. rtp头上的marker标识了一帧的开始/结束, 分包的时候刚开始写0, 最后一包填1;
4. 未证实: 分包时rtp头的timestamp应该是不变的.
*/ break;
}
}
} /**************************************************************************************************************************
1. 组合封包模式
在NALU单元很小的时候, 可以将多个NALU封装到一个RTP包里面进行传输, 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24;
那么这里的类型值分别是24, 25, 26以及27;
我们主要介绍STAP-A, 其封包格式如下所示:
[RTP Header] [Nalu头, type: 24(一个字节78)] [Nalu1 len(2 bytes)] [Nalu1 data] [Nalu2 len(2 bytes)] [Nalu2 data] ...
2. 分片封包模式
当一个NALU长度超过了MTU, 就需要采用分片的方式进行RTP封包, 将一个NALU分到多个RTP包中进行传输;
存在两种分片类型FU-A和FU-B, 类型值分别是28和29.
RTP+FU-A分片封包的组合方式如下:
[RTP Header][FU indicator][FU header][payload]: 其中RTP Header占12字节, FU indicator和FU header各占1个字节; [FU indicator]有以下格式:
      +---------------+  
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type   |
      +---------------+
type=28表示FU-A分包 [FU header]的格式如下:
      +---------------+  
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+  
      |S|E|R|  Type   |
      +---------------+
S: 设置成1表示此FU-A分片包为NAL单元的起始包, 其他情况设置为0;
E:设置成1表示此FU-A分片为NAL单元的结束包, 其他情况设置为0;
  R:保留位,必须为0;
Type: 为被分包的Nalu的type. 简单说就是加了一个字节描述分包的开始和结束.
*******************************************************************************************************************************/
static void Rtp_DealH264(int socketFd)
{
while (!feof(fp))
{
/*
1. get nalu data;
2. sps, pps等较小的, 可采用组合封包;
3. 帧数据大于MTU, 需分包. 如FU-A, 将帧拆分, 加上FU-A的格式, 再加上FTP的头发送出去;
4. 以上都可参照h264parse.c
*/
}
} static void Rtp_DealMpeg2(int socketFd)
{
while (!feof(fp))
{
/*
1. get data by startcode(seq, gop, pic...);
2. data_len<MTU, send;
3. data_len>MTU, 分包, 每次最大MTU;
4. 以上都可参照mpeg2parse.c
*/
}
} static void Rtp_DealFlv(int socketFd)
{
while (!feof(fp))
{
/*
1. get data by tag(script, video, audio...);
2. data_len<MTU, send;
3. data_len>MTU, 分包, 每次最大MTU;
4. 以上都可参照flvparse.c
*/
}
} /*
1. rtp client, send data to servAddr;
2. server can play used rtp://ip:port
*/
int main(int argc, char *argv[])
{
int socketFd = ; unsigned short serverPort = ; unsigned char serverIp[MAX_RTPURL_IP] = {}; ParseArgs(argc, argv, &defaultArgs); memset(serverIp, 0x0, MAX_RTPURL_IP); Parse_RtpUrl(defaultArgs.rtpUrl, serverIp, &serverPort); socketFd = socket(AF_INET, SOCK_DGRAM, );
if (socketFd < )
{
printf("%s\n", strerror(errno)); exit();
} memset(&servAddr, , sizeof(servAddr)); servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(serverPort);
servAddr.sin_addr.s_addr = inet_addr(serverIp); fp = fopen(defaultArgs.filePath, "r+");
if (!fp)
{
printf("%s\n", strerror(errno)); exit();
} if ( == strcmp(defaultArgs.fileFormat, "ts"))
{ Rtp_DealTs(socketFd); fclose(fp);
}
else if ( == strcmp(defaultArgs.fileFormat "ps"))
{
Rtp_DealPs(socketFd); fclose(fp);
}
else if ( == strcmp(defaultArgs.fileFormat "h264"))
{
Rtp_DealH264(socketFd); fclose(fp);
}
else if ( == strcmp(defaultArgs.fileFormat "mpeg2"))
{
Rtp_DealMpeg2(socketFd); fclose(fp);
}
else if ( == strcmp(defaultArgs.fileFormat "flv"))
{
Rtp_DealFlv(socketFd); fclose(fp);
} return ;
}

 最后如果您觉得本篇对您有帮助,可以打赏下,谢谢!!!

rtp传输音视频(纯c代码)的更多相关文章

  1. ffmpeg解码音视频过程(附代码)

    0. 引言 最近一直在使用和学习ffmpeg. 工作中需要拉流解码, 获取音频和视频数据. 这些都是使用ffmpeg处理. 因为对ffmpeg接触不多, 用的不深, 在使用的过程中经常遇到不太懂的地方 ...

  2. Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析

    本文主要介绍WebRTC中的RTP/RTCP协议,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...

  3. Android IOS WebRTC 音视频开发总结(五七)-- 网络传输上的一种QoS方案

    本文主要介绍一种QoS的解决方案,文章来自博客园RTC.Blacker,欢迎关注微信公众号blacker,更多详见www.rtc.help QoS出现的背景: 而当网络发生拥塞的时候,所有的数据流都有 ...

  4. 5┃音视频直播系统之 WebRTC 中的协议UDP、TCP、RTP、RTCP详解

    一.UDP/TCP 如果让你自己开发一套实时互动直播系统,在选择网络传输协议时,你会选择使用UDP协议还是TCP协议 假如使用 TCP 会怎样呢?在极端网络情况下,TCP 为了传输的可靠性,将会进行反 ...

  5. 腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

    1.概述 本文来自腾讯视频云终端技术总监rexchang(常青)技术分享,内容分别介绍了微信小程序视音视频和WebRTC的技术特征.差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebR ...

  6. C++实现RTMP协议发送H.264编码及AAC编码的音视频

    http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP ...

  7. C++实现RTMP协议发送H.264编码及AAC编码的音视频(转)

    C++实现RTMP协议发送H.264编码及AAC编码的音视频(转) RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia ...

  8. 【转】C++实现RTMP协议发送H.264编码及AAC编码的音视频

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系F ...

  9. 鹅厂优文|打通小程序音视频和webRTC

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯视频云终端技术总监常青, 2008 年毕业加入腾讯,一直从事客户端研发相关工作,先后参与过 PC QQ.手机QQ.QQ物联 等产品 ...

随机推荐

  1. redux&&createStore

    const createStore = (reducer,presetState, enhancer) => { if (typeof presetState === "functio ...

  2. 2019-4-23 plan

    需要制作springcloud es6的技术文档和demo

  3. Ubuntu如何启用root用户登录

    默认安装Ubuntu都是不允许以root用户进行登录的,想要以root用户进行登录需要进行一些操作,主要是以下几个步骤: 第一步 在终端输入命令:sudo passwd root 以普通用户登录系统, ...

  4. Confluence-6.10.0+Jira-7.13+Crowd-3.2.1最全破解文档,附下载包

    =========================================2019.4.19更改================================================ ...

  5. 【Core】当前 .NET SDK 不支持将 .NET Core 2.2 设置为目标。请将 .NET Core 2.1 或更低版本设置

    问题起因: 新的电脑,打开core2.2的项目时,因为没有安装2.2 sdk,项目编译失败 所以在选择目标框架下拉框选择安装其他目标框架 会跳转到官网下载sdk:https://dotnet.micr ...

  6. react简书笔记一 环境, git 和 项目 关联

    1.. 建立git项目  ( 码云, github 都可以 ), 具体步骤: https://www.cnblogs.com/andy-lehhaxm/p/10720717.html 1.1  git ...

  7. Go语言学习之13 日志管理平台开发

    主要内容: 1. ElasticSearch介绍与使用2. kibana介绍与使用 1. ElasticSearch安装 详见上节内容2. kibana安装 (1) 下载ES,下载地址:https:/ ...

  8. Facebook主页照片和封面照片的尺寸要求

    为什么好好的照片上传到Facebook后效果总不理想?为了避免你的照片在上传时被压缩,建议你尽量调整一下图片大小和格式,下面一起来看看Facebook主页照片和封面照片的尺寸要求. 1. Facebo ...

  9. 异常:Error resolving template "xxx", template might not exist or might not be accessible...解决办法

    在开发环境下正常,但使用jar运行时,报错Error resolving template template might not exist or might not be accessible,意思 ...

  10. js vue 在页面中将摄像头放在一个标签里展示,(模仿手机拍照功能)

    1.HTML <video id="video" autoplay class="fileImg"></video> <canva ...