RTSP服务器:RTP传输AAC流
服务器 传输 RTSP RTP aac
2023-09-14 09:05:27 时间
工作流程:
1)读取ADTS头(7字节),解析得到aac帧的信息(频率,声道,帧长度)
2)读取aac原始数据块,使用RTP打包aac原始数据
RTP打包h264码流时,由于h264数据长度不稳定,少则几字节,多则几千字节,所以RTP打包形式较多,需要根据大小决定是否进行分片。
而AAC数据块长度不会像h264那样变化,一般稳定在几百字节,所以它的打包方式比较单一。
打包方式如下
RTP包 = rtpheader(12字节) + 载荷标识(4字节)+ AAC数据块
载荷标识(4字节):
// 前两个字节值固定,第三个字节和第四个字节保存AAC Data的大小
// frameSize = adts.length - 7
rtpPacket->payload[0] = 0x00;
rtpPacket->payload[1] = 0x10;
rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5;
rtpPacket->payload[3] = (frameSize & 0x1F) << 3;
3)传输一个rtp包,并对序列号和时间戳操作
对于rtpheader有一个要注意的点: 传输h264时marker位为0,传输aac时marker位置1
4)回到步骤1,传输下一帧AAC
代码表达:
0)protocol.h
#ifndef __PROTOCOL_H
#define __PROTOCOL_H
// 相关协议的约定
typedef struct {
char *ip;
int tcp_port;
int rtp_port;
int rtcp_port;
} client_t;
// 在rtsp+rtp传输时,客户端的RTP端口不能指定,需要从SETUP请求中获取
// 单独用于RTP_H264/AAC测试
#define RTP_CLIENT_IP "192.168.102.215"
#define RTP_CLIENT_PORT 2018
// 测试文件的路径
#define AAC_FILE "/home/zhou/ffmpeg/testvideo/001.aac"
#endif
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
1)rtp_aac.h
#ifndef __RTP_AAC_H
#define __RTP_AAC_H
#include "protocol.h"
/*
* @func : RTP打包传输AAC码流
* @param:
* sockfd --- UDP socket
* client --- client->ip, client->rtp_port
* path --- AAC_FILE
*/
int rtp_play_aac(int sockfd, client_t *client, const char *path);
/*
* 例程模块测试:
* Linux下直接调用rtp_aac_test(AAC_FILE);
* Windows下创建rtp_test.sdp文件,用VLC打开。
*/
/* rtp_test.sdp文件:
*
* m=audio 2018 RTP/AVP 97
* a=rtpmap:97 mpeg4-generic/44100/2
* a=fmtp:97 SizeLength=13;
* c=IN IP4 127.0.0.1
*/
int rtp_aac_test(const char *file);
#endif
2)rtp_aac.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sock.h"
#include "rtp.h"
#include "rtp_aac.h"
struct adts_header {
char profile[8];
int channel;
int freq;
int length; // 一帧aac长度,包括adts头和原始流
};
static int parser_adts(struct adts_header *adts, uint8_t header[]) {
if ((header[0] != 0xFF) && ((header[1] & 0xF0) != 0xF0)) {
return -1; // syncword(12bits)不是0XFFF
}
memset(adts, 0, sizeof(struct adts_header));
// profile 17:18位, 取第3字节高2位
int pro = ((unsigned int)header[2] & 0xC0) >> 6;
switch (pro) {
case 0: strcpy(adts->profile, "Main");break;
case 1: strcpy(adts->profile, "LC");break;
case 2: strcpy(adts->profile, "SSR");break;
default: strcpy(adts->profile, "Unknown");break;
};
// frequency_idx 19:23位,取第3字节中间4位
int freq_index = ((unsigned int)header[2] & 0x3C) >> 2;
switch (freq_index) {
//...
case 3: adts->freq = 48000; break;
case 4: adts->freq = 44100; break;
default: adts->freq = 0; break;
}
adts->channel = ((unsigned int)header[2] & 0x01) << 2;
adts->channel += ((unsigned int)header[3] & 0xC0) >> 6;
// aac_frame_length 30:42(13位) header[3]后2位+header[4]+header[5]前3位
int size = ((unsigned int)header[5] & 0XE0) >> 5; // low 3bits
size += header[4] << 3; // mid 8bits
size += (header[3] & 0x03) << 11; // high 2bits
adts->length = size;
return 0;
}
static int rtp_send_acc_frame(int sockfd, client_t *client, uint8_t *data, int datalen, struct rtp_packet *packet) {
packet->payload[0] = 0x00;
packet->payload[1] = 0x10;
packet->payload[2] = (datalen & 0x1fe0) >> 5;
packet->payload[3] = (datalen & 0x1f) << 3;
memcpy(packet->payload+4, data, datalen);
int ret = rtp_send_packet(sockfd, client->ip, client->rtp_port, \
packet, RTP_HEADER_SIZE+4+datalen);
if (ret < 0) return -1;
packet->header.seq++;
packet->header.timestamp += 1024;
return 0;
}
int rtp_play_aac(int sockfd, client_t *client, const char *path) {
struct rtp_packet *packet = (struct rtp_packet *)malloc(RTP_PACKET_SIZE);
rtp_header_init(&packet->header, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);
FILE *fp = fopen(path, "r");
if (fp == NULL) {
return -1;
}
uint8_t header[7];
struct adts_header adts;
uint8_t *data = malloc(8*1024);
int ret = 0;
while (1) {
ret = fread(header, 1, 7, fp);
if (ret <= 0) {
break;
}
if (parser_adts(&adts, header) < 0) {
printf("failed parser_adts\n");
break;
}
fread(data, 1, adts.length-7, fp);
rtp_send_acc_frame(sockfd, client, data, adts.length-7, packet);
//printf("packet seq:%d adts freq:%d length: %d\n",\
packet->header.seq, adts.freq, adts.length);
float fps = adts.freq / 1024.0;
usleep(1000*1000 / (int)fps);
}
free(packet);
free(data);
fclose(fp);
return 0;
}
int rtp_aac_test(const char *file)
{
int sockfd = create_udp_socket();
if (sockfd < 0) {
printf("failed to init socket\n");
return -1;
}
client_t client;
client.ip = strdup(RTP_CLIENT_IP);
client.rtp_port = RTP_CLIENT_PORT;
rtp_play_aac(sockfd, &client, file);
close_socket(sockfd);
return 0;
}
如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
相关文章
- 局域网服务器传输大文件方案,局域网快速传输大文件【操作方案】
- tomcat服务器监控工具之probe「建议收藏」
- 技术教程:Windows环境下如何部署FTP服务器传输EasyCVR?
- python 数据库性能提升 - TCP聊天+传输文件服务器服务器套接字v2.7
- TCP聊天+传输文件服务器服务器套接字v2.6 - 登录注册界面更新 - loading界面应用
- TCP聊天+传输文件服务器服务器套接字v2.8 - 修复已知程序4个问题
- 更现代的服务器R开发----服务器VSCode-R搭建
- Redis服务器的启动过程分析
- 主机无法访问虚拟机上的elasticsearch服务器详解程序员
- Linux重启MySQL服务器的命令指南(linux重启mysql命令)
- 如何从Windows到Linux服务器传输文件(windows上传文件到linux服务器)
- serverLinux构建的媒体服务器:实现资源传输的最佳选择.(linux的media)
- 传输极速体验:Linux服务器助力视频传输(linux服务器视频)
- 使用 Fedora 31 和 Nextcloud 服务器构建自己的云
- Linux作为服务器:稳定性与安全性的首选(linuxas服务器)
- 传输从远程服务器使用SSH安全传输文件(linuxssh文件)
- 「教程」Linux服务器搭建指南(linux下搭建服务器)
- 妙用Linux:连接Linux服务器的简明指南(如何连接linux服务器)
- 腾讯云强力推出Redis服务器,实现快速数据存储传输(腾讯云服务器 redis)
- ASP在服务器自动解压RAR文件
- 配置FTP传输记录情况的LinuxFTP服务器