【发布时间】:2019-09-01 15:20:37
【问题描述】:
我下面的代码旨在通过每毫秒传输一次包含音频样本的 RTP 数据包来生成符合 AES67 的音频流。这是通过引用 CLOCK_REALTIME 的间隔计时器来实现的。为了满足AES67的要求,CLOCK_REALTIME通过phc2sys同步到一个PTP硬件时钟,PTP硬件时钟本身由ptp4l管理。该代码在 Beaglebone Black SBC (ARM Cortex-A8) 上运行,该 SBC 还充当启用了硬件时间戳的 PTP 大师。我已将 rt_preempt 补丁应用于内核,并且正在运行 ptp4l、phc2sys 和我的代码,实时优先级分别为 99、98 和 97。 (调度模式为 RR)。
我一直在使用硬件 AES67 接收器测试代码,它报告 PTP 同步偏移在 0.1μS 以内。系统确实可以工作,但是接收器中的接收器缓冲区会慢慢清空,并且在大约 26 分钟后运行不足。我猜是:
a) 时钟同步机制之一存在频率错误或
b) timerfd_create 创建的间隔定时器的持续时间存在长期平均误差
我还没有找到 a) 的任何证据,所以我的问题是,timerfd(或我对它的使用)是否有任何已知的限制或不足之处,可能会导致这个问题?
(定时器代码在代码示例的底部三分之一处。)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include <math.h>
#include <sys/timerfd.h>
#include "random32.h"
#include "timestamp.h"
const int32_t int24_min = -8388608;
const int32_t int24_max = 8388607;
struct in_addr localInterface;
struct sockaddr_in groupSock;
struct timespec packetTime = {0, 1000000L};
struct itimerspec packetTimer;
uint16_t sequenceNumber = 0;
uint32_t timestamp = 0;
uint32_t ssrc = 0;
uint64_t expiration;
ssize_t s;
int type = 0;
int sd;
int td;
int sRate = 48000;
void tx(void);
//set the first two bytes of the RTP header
char txPacket[300] = {0b10000000, 0b01100100};
int packetLen = sizeof(txPacket);
//declare a pointer to the sequenceNumber in txPacket
char *sn = &txPacket[2];
//declare a pointer to the timestamp in txPacket
char *ts = &txPacket[4];
//declare a pointer to the synchronisation source identifier
char *ss = &txPacket[8];
//declare a pointer to the first audio sample in txPacket
char *as = &txPacket[12];
//declare a union of types int32_t and char[4] so that
//sample data can be accessed one byte at a time
union sample
{
int32_t intSample;
char bytes[4];
};
int main (int argc, char *argv[ ])
{
//generate a unique ssrc and put it in the txPacket
ssrc = htonl(random32(type));
memcpy (ss, &ssrc, sizeof(ssrc));
//generate 1kHz tone for the audio samples
double angle = 0.0;
double increment = 7.5 * (M_PI / 180);
double amplitude;
int i;
union sample currentSample;
currentSample.intSample = 0;
for(i = 1; i < 49; i++)
{
//calculate amplitude based on sine of angular position
//scale it to the maximum value of a 24-bit number
amplitude = (sin(angle)) * int24_max;
amplitude = round(amplitude);
currentSample.intSample = (int32_t)amplitude;
//copy the sample into the txpacket (twice for stereo),
//reordiering bytes as big-endian
memcpy (as, ¤tSample.bytes[2], 1);
as++;
memcpy (as, ¤tSample.bytes[1], 1);
as++;
memcpy (as, ¤tSample.bytes[0], 1);
as++;
memcpy (as, ¤tSample.bytes[2], 1);
as++;
memcpy (as, ¤tSample.bytes[1], 1);
as++;
memcpy (as, ¤tSample.bytes[0], 1);
as++;
angle = angle + increment;
}
//create the socket
sd = socket(AF_INET, SOCK_DGRAM, 0);
//initialize the groupSock structure
memset((char *)&groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("225.1.1.1");
groupSock.sin_port = htons(5004);
localInterface.s_addr = inet_addr("192.168.0.4");
setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface));
//set the initial expiration and interval of the timer
packetTimer.it_value = packetTime;
packetTimer.it_interval = packetTime;
//create and arm the timer
td = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(td, TFD_TIMER_ABSTIME, &packetTimer, NULL);
s = read(td, &expiration, sizeof(uint64_t)); //wait for a timer expiration
timestamp = getTimestamp(); //get initial RTP timestamp value
tx(); //send first packet
for( ; ; )
{
s = read(td, &expiration, sizeof(uint64_t)); //wait for next timer expiration
tx(); //send next packet
}
return 0;
}
void tx(void)
{
//convert sequenceNumber to network byte order and copy it to the tx packet
uint16_t beSequenceNumber = htons(sequenceNumber);
memcpy (sn, &beSequenceNumber, sizeof(beSequenceNumber));
//convert timestamp to network byte order and copy it to the tx packet
uint32_t beTimestamp = htonl(timestamp);
memcpy (ts, &beTimestamp, sizeof(beTimestamp));
//send packet
sendto(sd, txPacket, packetLen, 0, (struct sockaddr*)&groupSock, sizeof(groupSock));
//increment sequence number
sequenceNumber++;
//increment timestamp
timestamp = timestamp + 48;
}
【问题讨论】:
-
我无法回答您的问题...但我真的很高兴有人让 AES67 在这些板上运行良好,所以 +1!这么多可能的用例。
标签: time timer real-time audio-streaming rtp