gpt4 book ai didi

c - UDP 上的 wav 文件无法在带有 Alsa-lib 的 unix 中正确播放

转载 作者:行者123 更新时间:2023-12-02 23:01:51 25 4
gpt4 key购买 nike

我正在尝试通过 UDP 套接字将 .wav 文件从服务器发送到客户端并从客户端播放。

该文件为 48kHz 和 16 位,持续时间为 25 秒。

由于 server.c 是较大 C-RAN 代码模块中的一个小代码部分,因此我通过从标准输入传递一个 wav 文件来检查它:

./SERVER < filename.wav

然后一旦我执行了client.c,服务器就开始发送数据报,client.c 开始播放它们。一切都很好,但音频以更快的速度播放,就像以正确速度的两倍播放一样。有时它会开始正确播放,但几秒钟后它会加速。

我传递了一个大的 .txt 文件而不是 .wav 文件,并且客户端从头到尾收到了所有的 .txt 文件。

为了正确播放音频 wav 文件,我有什么遗漏需要考虑的吗?

client.c 代码:
// Remote Radio Head - client side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <alsa/asoundlib.h>
#include <dirent.h>

/* CONSTANTS */
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 128

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
/*************************************************
* DRIVER CODE *
*************************************************/
int main(int argc, char *argv[]) {
// Sockets variables
int sockfd;
char buffer1[MAXLINE];
char *data = "Hello from client, waiting for audio to playback";
struct sockaddr_in servaddr;

// ALSA playback variables
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer2;
/*********************************************
* ALSA DRIVERS SETUP *
*********************************************/
// Open PCM device for playback.
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(&params);
// Fill it in with default values.
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle, params, 2);
// bits/second sampling rate
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
// Set period size to 32 frames.
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
// Use a buffer large enough to hold one period
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 4; // 2 bytes/sample, 2 channels
buffer2 = (char *) malloc(size);
// We want to loop for 25 seconds
snd_pcm_hw_params_get_period_time(params, &val, &dir);
// 25 seconds in microseconds divided by period time
loops = 25000000/val;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));

// Filling server information
servaddr.sin_family = AF_INET; //ipv4
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY; //Any server
/********************************************
* SEND MSG TO SERVER *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
sendto(sockfd, (const char *)data, strlen(data), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
printf("Waiting for audio!!!\n");
/********************************************
* RECIEVE MSG FROM SERVER *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned

int n, len;
/* We allocate memory to store the payload of the incoming datagram. */
char *payload = (char *)malloc(PAYLOAD_SIZE*sizeof(char));

while(1){

//We receive datagram
int bytes_read = recvfrom(sockfd, (void *)payload, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
fprintf(stderr,"r");
fflush(stderr);

// We write the datagram to stdout.
//write(1, (void *)payload, bytes_read);
//fprintf(stderr,"w");
//fflush(stderr);

while (loops > 0) {
loops--;

rc = read(sockfd, buffer2, size); //Read audio file

if (rc == 0)
{
fprintf(stderr, "end of file on input\n");
break;
}
else if (rc != size)
{
fprintf(stderr, "short read: read %d bytes\n", rc);
}
rc = snd_pcm_writei(handle, buffer2, frames);
if (rc == -EPIPE)
{
// EPIPE means underrun
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
fprintf(stderr, "error from writei: %s\n",
snd_strerror(rc));
}
else if (rc != (int)frames)
{
fprintf(stderr, "short write, write %d frames\n", rc);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer2);
buffer1[n] = '\0';
printf("Server : %s\n", buffer1);
}
//n = recvfrom(sockfd, (char *)buffer1, MAXLINE, MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
close(sockfd); //close file descriptor
return 0;
}

服务器.c
// Server side implementation of UDP client-server model 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//CONSTANTS
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 128

// Driver code
int main(int argc, char *argv[]) {

int sockfd;
char buffer[MAXLINE];
char *msg = "Hello from server, ready to send audio";
struct sockaddr_in servaddr, cliaddr;
/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { /*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr)); //Allocate memory for structure
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;//Any client
servaddr.sin_port = htons(PORT);
/********************************************
* BIND THE SOCKET WITH THE SERVER ADDRESS *
********************************************/
//sockfd: File descriptor of socket to be binded
//addr: Structure in which address to be binded to is specified
//addrlen: Size of addr structure
if ( bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
/********************************************
* RECIEVE MSG FROM CLIENT *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int len, n;
len = sizeof(cliaddr); //len is value/resuslt

n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, ( struct sockaddr *) &cliaddr, &len);

buffer[n] = '\0';
printf("Client : %s\n", buffer);
/********************************************
* SEND MSG TO CLIENT *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure

// We allocate memory for the datagrams payload
char *payload = (char *)malloc(PAYLOAD_SIZE*sizeof(char));

printf("Sending audio in 3, 2, 1.....\n");

while(1){
// Reading from the std in
int bytes_read = read(0, (void *)payload, PAYLOAD_SIZE);
fprintf(stderr, "r");
fflush(stderr);
if(bytes_read < 1) break;

// We write the datagram to stdout.
write(1, (void *)payload, bytes_read);
fprintf(stderr, "w");
fflush(stderr);

//Sending datagram
sendto(sockfd, (void *)payload, bytes_read, 0, (struct sockaddr *) &cliaddr, len);
fprintf(stderr, "s");
fflush(stderr);
}
//sendto(sockfd, (const char *)msg, strlen(msg), MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);
return 0;
}

最佳答案

您实现中的问题是音乐数据的传输是从服务器到客户端的一种方式。当客户端发送第一个请求时,服务器开始以最快的速度广播音频流。结果客户端会丢失一些数据包。如果只有二分之一的数据包被实际写入音频设备,那么看起来音乐的播放速度是原来的两倍。如果将所有 bytes_read 相加,您可以很容易地看到您正在丢失数据包。在客户端。它将比实际文件大小小得多。

也不清楚为什么在客户端你首先recvfrom插入 payload然后 read进入 buffer2 .理论上只需要第一个操作,然后你从 payload 开始写。到音频设备。

如果您想以正确的方式实现流式传输,则需要在客户端实现适当的缓冲解决方案,并在服务器上进行一些速度限制,以避免以远高于所需的速度发送数据。

如果您想以一种简单的方式修复您的代码,一种可能性是添加一个 ACK​​,客户端在收到一个数据包后发送给服务器。服务器将在发送下一个数据包之前等待客户端 ACK。这或多或少会使 UDP 协议(protocol)成为 TCP 协议(protocol)。

我已经稍微修改了您的代码,以向您展示我想要的东西。使用此代码,您可以正确播放 wav 文件。它并不完美,但至少它应该让您了解您的代码有什么问题

服务器.c

// gcc -o server server.c
// Server side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

// CONSTANTS
#define PORT 8080
#define MAXLINE 1024
#define PAYLOAD_SIZE 2048

int main(int argc, char *argv[])
{
int sockfd;
char buffer[MAXLINE];
const char* msg = "SERVER: Sending audio complete";

struct sockaddr_in servaddr, cliaddr;

/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
/*AL_INET: ipv4; SOCK_DGRAM: UPD; 0: default protocol*/
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr)); // Allocate memory for structure
memset(&cliaddr, 0, sizeof(cliaddr));

// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY; // Any client
servaddr.sin_port = htons(PORT);

/********************************************
* BIND THE SOCKET WITH THE SERVER ADDRESS *
********************************************/
//sockfd: File descriptor of socket to be binded
//addr: Structure in which address to be binded to is specified
//addrlen: Size of addr structure
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}

/********************************************
* RECIEVE MSG FROM CLIENT *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned
int len, n;
len = sizeof(cliaddr); // len is value/result

printf("Waiting for client connection...\n");
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);

buffer[n] = '\0';
printf("%s\n", buffer);

/********************************************
* SEND MSG TO CLIENT *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure

// We allocate memory for the datagrams payload
char* payload = (char*)malloc(PAYLOAD_SIZE * sizeof(char));

printf("Sending audio...\n");

while (1) {
// Reading from the stdin
int bytes_read = read(0, (void*)payload, PAYLOAD_SIZE);
if (bytes_read <= 0)
break;

// Sending datagram
sendto(sockfd, (void*)payload, bytes_read, 0, (struct sockaddr *)&cliaddr, len);

// Waiting for ACK
n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
}

return 0;
}

客户.c
// gcc -o client client.c -lasound

// Remote Radio Head - client side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <alsa/asoundlib.h>
#include <dirent.h>

/* CONSTANTS */
#define PORT 8080
#define MAXLINE 1024
#define FRAME_SIZE 512
#define PAYLOAD_SIZE (FRAME_SIZE * 4)


/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API


int main(int argc, char *argv[])
{
// Sockets variables
int sockfd;
const char* ACK = "ack";
const char* START_BROADCAST = "CLIENT: waiting for audio to playback";

struct sockaddr_in servaddr;

// ALSA playback variables
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;

/*********************************************
* ALSA DRIVERS SETUP *
*********************************************/
// Open PCM device for playback.
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
// Allocate a hardware parameters object.
snd_pcm_hw_params_alloca(&params);
// Fill it in with default values.
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters.
// Interleaved mode
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
// Signed 16-bit little-endian format
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
// Two channels (stereo)
snd_pcm_hw_params_set_channels(handle, params, 2);
// bits/second sampling rate
val = 48000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
// Set period size to 32 frames.
frames = FRAME_SIZE;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}

/*********************************************
* CREATING SOCKET FILE DESCRIPTOR *
*********************************************/
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));

// Filling server information
servaddr.sin_family = AF_INET; // ipv4
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY; // Any server

/********************************************
* SEND MSG TO SERVER *
********************************************/
//sockfd: File descriptor of socket
//buffer: Application buffer cointaining the data to be sent
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//dest_addr: Structure containing address of destination
//addrlen: Size of dest_addr structure
sendto(sockfd, START_BROADCAST, strlen(START_BROADCAST), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));
printf("Waiting for audio!!!\n");

/********************************************
* RECIEVE MSG FROM SERVER *
********************************************/
//sockfd: file descriptor of socket
//buffer: Apllication buffer in which to recieve data
//len: Size of buffer
//flags: Bitwise OR flags to modify socket behaviour
//src_addr: Structure containing source address is returned
//addrlen: Variable in which size of src_addr structure is returned

int n, len;
/* We allocate memory to store the payload of the incoming datagram. */
char *payload = (char *)malloc(PAYLOAD_SIZE * sizeof(char));

while (1)
{
len = PAYLOAD_SIZE;
int bytes_read = recvfrom(sockfd, (void *)payload, PAYLOAD_SIZE, MSG_WAITALL, (struct sockaddr *)&servaddr, &len);

rc = snd_pcm_writei(handle, payload, frames);
if (rc == -EPIPE) {
// EPIPE means underrun
fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr, "error from writei: %s\n",
snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "short write, write %d frames\n", rc);
}

// Send ACK
sendto(sockfd, ACK, strlen(ACK), 0, (const struct sockaddr *)&servaddr, len);
}

snd_pcm_drain(handle);
snd_pcm_close(handle);
close(sockfd); //close file descriptor
return 0;
}

关于c - UDP 上的 wav 文件无法在带有 Alsa-lib 的 unix 中正确播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62103879/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com