When I compile ALSA's pcm_min.c example with
gcc -Wall -lasound pcm_min.c -o pcm_min
Everything is fine, but running it, I get the white noise as expected, but I also get this warning/error:
Short write (expected 16384, wrote 7616)
Which comes from the last if-statement.
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void)
{
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_U8,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)sizeof(buffer))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
Can someone see why this warning/error occur?
Hugs,
Louise
The snd_pcm_writei() function might return less than sizeof(buffer) when there's either a signal received or an underrun. In your case, it seems that you're mixing bytes and frames. The last parameter of the call is the number of frames that you have in your buffer. Since you're passing the number of bytes in your buffer instead, you're seeing an underrun.
I was also having some problems with this example. I modified it a bit and now it works.
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void)
{
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
snd_pcm_uframes_t bufferSize, periodSize;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = random() & 0xff;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1, //channels
44100, //sample rate
1, //allow resampling
500000) //required latency in us
) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_prepare(handle)) < 0) {
printf("Pcm prepare error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_get_params( handle, &bufferSize, &periodSize )) < 0) {
printf("Pcm get params error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
printf("Buffer size:%d, Period size:%d\n", (int)bufferSize, (int)periodSize);
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, periodSize);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)periodSize)
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
Related
I have a very simple setup where a camera connected to a Pi Zero is triggered by an external signal.
Problem: Achievable framerate is <0.4fps with my code using v4l2. Works fine with e.g.: raspivid.
With raspivid 1920x1080 # 50fps works as expected (tested using -pts to save timecodes). My code instead will never dequeue a buffer at 50Hz. I have to reduce the trigger rate to 5Hz before receiving frames every ~600ms! This looks like I'm still only receiving every third frame. The returned framebuffers always skip one index:
new frame 0: 3110400
time diff 0.6006
new frame 2: 3110400
time diff 0.6006
new frame 4: 3110400
time diff 0.600601
new frame 6: 3110400
time diff 0.6006
You can find my code below (exposure setup,... removed)
Question: Can you give me a hint what could be the problem? The program does nothing but dequeue and enqueue the buffers so performance should not be an issue.
#define NB_BUFFER 10
int main(int argc, char *argv[])
{
int exposure = 50;
int rows = 1080;
int cols = 1920;
if(argc > 1){
exposure= atoi(argv[1]);
}
if(argc > 3){
cols=atoi(argv[2]);
rows=atoi(argv[3]);
}
struct vdIn vdin;
struct vdIn *vd = &vdin;
if((vd->fd = open("/dev/video0", O_RDWR)) < 0){
perror("open");
exit(1);
}
if(ioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap) < 0){
perror("VIDIOC_QUERYCAP");
exit(1);
}
if(!(vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
fprintf(stderr, "The device does not handle single-planar video capture.\n");
exit(1);
}
vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;//V4L2_PIX_FMT_BGR24;
vd->fmt.fmt.pix.width = cols;
vd->fmt.fmt.pix.height = rows;
if(ioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt) < 0){
perror("VIDIOC_S_FMT");
exit(1);
}
struct v4l2_control control;
control.id = V4L2_CID_EXPOSURE_AUTO ;
control.value = V4L2_EXPOSURE_MANUAL;
if(ioctl(vd->fd, VIDIOC_S_CTRL, &control) < 0){
perror("VIDIOC_S_CTRL EXPOSURE MANUAL");
exit(1);
}
.... iso manual, line frequency off, set exposure, auto whitebalance off
vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->rb.memory = V4L2_MEMORY_MMAP;
vd->rb.count = NB_BUFFER;
if(ioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb) < 0){
perror("VIDIOC_REQBUFS");
exit(1);
}
int ret;
/* map the buffers */
struct v4l2_buffer buf;
for (int i = 0; i < NB_BUFFER; i++) {
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.index = i;
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (vd->fd, VIDIOC_QUERYBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to query buffer (%d).\n", errno);
return -1;
}
std::cout << "buf len " << vd->buf.length<<std::endl;
vd->mem[i] = mmap (0 /* start anywhere */ ,
vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,
vd->buf.m.offset);
if (vd->mem[i] == MAP_FAILED) {
fprintf (stderr, "Unable to map buffer (%d)\n", errno);
return -1;
}
}
/* Queue the buffers. */
for (int i = 0; i < NB_BUFFER; ++i) {
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.index = i;
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to queue buffer (%d).\n", errno);
return -1;
}
}
// Activate streaming
int type = vd->fmt.type;
if(ioctl(vd->fd, VIDIOC_STREAMON, &type) < 0){
perror("VIDIOC_STREAMON");
exit(1);
}
bool capture_is_running = true;
double lastTimestamp=0;
while(capture_is_running){
memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vd->buf.memory = V4L2_MEMORY_MMAP;
std::cout << "try to dequeue" << std::endl;
ret = ioctl (vd->fd, VIDIOC_DQBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to dequeue buffer (%d).\n", errno);
return -1;
}
cout << "new frame "<<vd->buf.index<<": "<< vd->buf.bytesused << endl;
double timestamp = vd->buf.timestamp.tv_sec + vd->buf.timestamp.tv_usec/1000000.;
double timeDiff = timestamp - lastTimestamp;
lastTimestamp = timestamp;
std::cout << "time diff " << timeDiff << std::endl;
ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
if (ret < 0) {
fprintf (stderr, "Unable to requeue buffer (%d).\n", errno);
return -1;
}
}
// Deactivate streaming
if(ioctl(vd->fd, VIDIOC_STREAMOFF, &type) < 0){
perror("VIDIOC_STREAMOFF");
exit(1);
}
close(vd->fd);
return EXIT_SUCCESS;
return 0;
}
I've been trying to use the ALSA lib for a while and I don't understand how I should use it.
I took an example program and I've tried to modify it to use float (32bits) instead of unsigned char (8bits). But now when I'm running it, I have a segmentation fault in the second loop.
Here is my code :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); // segmentation fault
if(frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
break;
}
if (frames > 0 && frames < (long)sizeof(buffer))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
How to use this lib with 32bits?
I've tried this format and others like little endian or big endian.. The only one that doesn't crash is SND_PCM_FORMAT_FLOAT but it's making the error :
ALSA lib pcm.c:8507:(snd_pcm_set_params) Sample format not available for PLAYBACK: Invalid argument
Playback open error: Invalid argument
Thanks in advance.
P.S.: Linux, Ubuntu 19.10 64bits
The segmentation fault may already occur when you write into buffer:
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
sizeof(buffer) will give you the size in bytes not the number of elements. They are only equal for char (and unsigned char) since sizeof(char) is 1. You most likely want to iterate over the elements:
for(i = 0; i < sizeof buffer/sizeof *buffer; i++)
buffer[i] = i % 255; // random();
It was indeed a problem of condition in my loop and my snd_pcm_writei()
Here is the code without errors thanks to #Osiris :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer) / sizeof(*buffer); i++)
buffer[i] = i % 0xffffffff; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / sizeof(*buffer)); // segmentation fault
if(frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
break;
}
if (frames > 0 && frames < (long)(sizeof(buffer) / sizeof(*buffer)))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
Distro: Debian 9.11
Compiler: gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
ALSA version: Advanced Linux Sound Architecture Driver Version k4.9.0-11-amd64.
Please consider the following minimal reproducible example (now updated, with more error handling):
/**
* Trying to set some parameters for ALSA.
*
* Code from: http://equalarea.com/paul/alsa-audio.html
*
*/
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#define PCM_DEVICE "default"
int main(int argc, char **argv)
{
unsigned int err;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
if((err = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Error: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(err));
return 1;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
if((err = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Error: Can't set interleaved mode. %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_FLOAT)) < 0) {
printf("Error: Can't set format. %s\n", snd_strerror(err));
return 1;
}
int channels = 2;
if((err = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0) {
printf("Error: Can't set channels number. %s\n", snd_strerror(err));
return 1;
}
int rate = 44100;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0) {
printf("Error: Can't set rate. %s\n", snd_strerror(err));
return 1;
}
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
int ch;
if((err = snd_pcm_hw_params_get_channels(params, &ch)) < 0) {
printf("Error: Unable to get the number of channels: %s\n", snd_strerror(err));
return 1;
}
if(ch > 1) {
printf("Channels: %i, stereo\n", ch);
}
else if(ch == 1) {
printf("Channels: %i, mono\n", ch);
}
else {
printf("Error: Unknown number of channels (%d).\n", ch);
return 1;
}
if((err = snd_pcm_hw_params_get_rate(params, &ch, 0)) < 0) {
printf("Error: Unable to get the rate: %s\n", snd_strerror(err));
return 1;
}
printf("Rate: %d bps\n", ch);
int dir;
snd_pcm_uframes_t period_size = 1024;
printf("Attempting to set the period size to %d\n", period_size);
if((err = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &period_size, &dir)) < 0) {
printf("Error: Unable to set the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Attempting to get the period size.\n");
if((err = snd_pcm_hw_params_get_period_size(params, &period_size, 0)) < 0) {
printf("Error: Unable to get the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Period size is now: %zd\n", period_size);
int period_time = -1;
printf("Attempting to get the period time.\n");
if((err = snd_pcm_hw_params_get_period_time(params, &period_time, NULL)) < 0) {
printf("Error: Unable to get the period time: %s\n", snd_strerror(err));
return 1;
}
printf("Period time: %d\n", period_time);
int buffer_size = 4096;
printf("Attempting to set the buffer size to: %d\n", buffer_size);
if((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size)) < 0) {
printf("Error: Unable to set the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Finalizing hw params.\n");
if((err = snd_pcm_hw_params(pcm_handle, params)) < 0) {
printf("Error: Can't set harware parameters. %s\n", snd_strerror(err));
return 1;
}
snd_pcm_uframes_t temp;
printf("Attempting to get the buffer size.\n");
if((err = snd_pcm_hw_params_get_buffer_size(params, &temp)) < 0) {
printf("Error: Unable to get the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Buffer size is now: %d\n", temp);
if((err = snd_pcm_drain(pcm_handle)) < 0) {
printf("Error: Unable to drain the pcm handle: %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_close(pcm_handle)) < 0) {
printf("Error: Unable to close the pcm handle: %s\n", snd_strerror(err));
return 1;
}
return 0;
}
When char test[5] exists, I get this output:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set the period size to 1024
Attempting to get the period size.
Period size is now: 1024
Attempting to get the period time.
Period time: -1
Attempting to set the buffer size to: 4096
Finalizing hw params.
Attempting to get the buffer size.
Buffer size is now: 523774
Note the "Buffer size is now: 523774". The expected output is given below:
However, if I change test[5] to test[4] or less (or remove the statement altogether), recompile, and run, I get:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set frames to 1024
Frames is now: 1024
Attempting to set the buffer size to: 4096
Buffer size is now: 4096
I must be abusing the ALSA API, be doing something else weird I don't realize, or the ALSA API is broken.
Why does it react to this trivial stack allocation of 5 bytes?
Note that a higher value than 5 also produces this problem, such as a word aligned number of 32. I don't think has to do with stack alignment or something of that sort.
Compiler notes, how to reproduce:
Copy/paste the minimal example into broken_alsa.c
Then issue:
$ gcc broken_alsa.c -lasound
$ ./a.out
Remove the char temp[5] (or set it to 4), and it should work.
When calling snd_pcm_hw_params_set_period_size_near(), initialize dir to zero.
I wrote a simple file copy application to measure the effectiveness of using sendfile API over normal read-from-file-and-write-to-socket approach for large files. However upon running the application using both the approaches, I found out that the difference in the amount of time taken for the file copy to get completed is very minimal between the two approaches.
I read from multiple sources that "sendfile" API would give tremendous performance improvement over the normal read-from-file-and-write-to-socket approach. But when I tried to benchmark with a single 2GB file following are the numbers I observed (average of 4 iterations):
Normal read-from-file-and-write-to-socket approach: 17 secs 444840 usecs
sendfile API: 17 secs 431420 usecs
I am running both the server and client pieces of the application on two different machines (Linux kernel version 4.4.162-94.72-default) in an isolated 1Gbps network.
Can someone help me what exactly am doing wrong here or missing here?
Server:
#define _GNU_SOURCE
#include "file_details.h"
void calculate_execution_time(struct timeval start, struct timeval end)
{
struct timeval time_diff;
time_diff.tv_sec = end.tv_sec - start.tv_sec;
time_diff.tv_usec = end.tv_usec - start.tv_usec;
// Adjust the time appropriately
while (time_diff.tv_usec < 0) {
time_diff.tv_sec--;
time_diff.tv_usec += 1000000;
}
printf("total execution time: = %lds.%ldus\n", time_diff.tv_sec, time_diff.tv_usec);
}
int read_from_file_pread(int client_sockfd, char *file_name, int fd, off_t file_size_in_bytes, int chunk_size)
{
ssize_t bytes_read = 0, bytes_sent = 0, total_bytes_sent = 0, bytes_sent_this_itr = 0;
off_t offset = 0;
char *buffer = NULL;
struct timeval start_time, end_time;
buffer = calloc(chunk_size, sizeof(char));
if (buffer == NULL) {
printf("Failed to allocate memory of size: %d bytes\n", chunk_size);
return -1;
}
gettimeofday(&start_time, NULL);
do {
bytes_read = pread(fd, buffer, chunk_size, offset);
switch (bytes_read) {
case -1:
printf("Failed to read from file: %s, offset: %lu, error: %d\n", file_name, offset, errno);
free(buffer);
return -1;
case 0:
printf("Completed reading from file and sending\n");
break;
default:
do {
bytes_sent = send(client_sockfd, buffer, (bytes_read - bytes_sent_this_itr), 0);
if (bytes_sent == -1) {
printf("Failed to send %lu bytes, error: %d\n", (bytes_read - bytes_sent_this_itr), errno);
free(buffer);
return -1;
}
bytes_sent_this_itr += bytes_sent;
} while (bytes_sent_this_itr < bytes_read);
bytes_sent = 0;
bytes_sent_this_itr = 0;
offset += bytes_read;
total_bytes_sent += bytes_read;
break;
}
} while (total_bytes_sent < file_size_in_bytes);
gettimeofday(&end_time, NULL);
printf("File size: %lu bytes, total bytes read from file: %lu, ", file_size_in_bytes, total_bytes_sent);
calculate_execution_time(start_time, end_time);
free(buffer);
return 0;
}
int read_from_file_sendfile(int client_sockfd, char *file_name, int fd, off_t file_size_in_bytes, int chunk_size)
{
ssize_t bytes_sent = 0, total_bytes_sent = 0;
off_t offset = 0;
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL);
do {
bytes_sent = sendfile(client_sockfd, fd, &offset, chunk_size);
if (bytes_sent == -1) {
printf("Failed to sendfile: %s, offset: %lu, error: %d\n", file_name, offset, errno);
return -1;
}
total_bytes_sent += bytes_sent;
} while (total_bytes_sent < file_size_in_bytes);
gettimeofday(&end_time, NULL);
printf("File size: %lu bytes, total bytes read from file: %lu, ", file_size_in_bytes, total_bytes_sent);
calculate_execution_time(start_time, end_time);
return 0;
}
int read_from_file(int client_sockfd, char *file_name, char *type, int chunk_size)
{
int error_code = 0, fd = 0;
ssize_t hdr_length = 0, bytes_sent = 0, file_name_length = strlen(file_name);
struct stat file_stat = {0};
struct file_details *file_details_to_send = NULL;
fd = open(file_name, O_RDONLY, S_IRUSR);
if (fd == -1) {
printf("Failed to open file: %s, error: %d\n", file_name, errno);
return -1;
}
error_code = fstat(fd, &file_stat);
if (error_code == -1) {
printf("Failed to get status of file: %s, error: %d\n", file_name, errno);
close(fd);
return -1;
}
hdr_length = (sizeof(struct file_details) + file_name_length + 1);
file_details_to_send = calloc(hdr_length, sizeof(char));
if (file_details_to_send == NULL) {
perror("Failed to allocate memory");
close(fd);
return -1;
}
file_details_to_send->file_name_length = file_name_length;
file_details_to_send->file_size_in_bytes = file_stat.st_size;
strcpy(file_details_to_send->file_name, file_name);
printf("File name: %s, size: %lu bytes\n", file_name, file_stat.st_size);
bytes_sent = send(client_sockfd, file_details_to_send, hdr_length, 0);
if (bytes_sent == -1) {
printf("Failed to send header of size: %lu bytes, error: %d\n", hdr_length, errno);
close(fd);
return -1;
}
if (strcmp(type, "rw") == 0) {
printf("By pread and send\n");
read_from_file_pread(client_sockfd, file_name, fd, file_stat.st_size, chunk_size);
} else {
printf("By sendfile\n");
read_from_file_sendfile(client_sockfd, file_name, fd, file_stat.st_size, chunk_size);
}
close(fd);
return 0;
}
int main(int argc, char *argv[])
{
...
...
option_value = 1;
error_code = setsockopt(client_sockfd, SOL_TCP, TCP_NODELAY, &option_value, sizeof(int));
if (error_code == -1) {
printf("Failed to set socket option TCP_NODELAY to socket descriptor: %d, error: %d", client_sockfd, errno);
}
read_from_file(client_sockfd, file_name, type, chunk_size);
...
}
Your code almost certainly made a big performance improvement. The problem might be that you're measuring wall time. Consider calling getrusage() instead of gettimeofday(). The ru_utime and ru_stime fields represent how much time the kernel and your program spent doing actual work. sendfile() should make those numbers go down. That way you consume less energy, and free up more resources for other programs on your computer. Unfortunately however it can't make the network go faster. Optimal wall time speed to send 2GB on 1GbPS ethernet assuming zero overhead would be ~9s. You're pretty close.
Edit: This question is different than the proposed duplicate because I'm asking How do you set the period/buffer size that will work with multiple targets each with different sound hardware?.
I have created some code that attempts to set up ALSA before playback of an OGG file. The code below works on one embedded Linux platform, but on another it fails with the following output:
Error setting buffersize.
Playback open error: Operation not permitted
I've included only the code that demonstrates the issue. setup_alsa() is not complete and won't completely configure an alsa device.
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0)
{
fprintf(stderr, "Error setting buffersize.\n");
snd_pcm_close(handle);
return(-1);
}
/* 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));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
What is the normal way to setup ALSA that doesn't require a specific buffer/period size be set that provides smooth audio playback?**
As it turns out, I can program my ALSA setup routine to let ALSA determine what the nearest working period/buffer size is by using snd_pcm_hw_params_set_buffer_size_near() instead of snd_pcm_hw_params_set_buffer_size().
The following code now works on both platforms:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
//static char *device = "default";
static char *device = "plughw:0,0";
snd_pcm_uframes_t periodsize = 4096; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_malloc(¶ms);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set the desired hardware parameters. */
/* Non-Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 44100 bits/second sampling rate (CD quality) */
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate.\n");
snd_pcm_close(handle);
return(-1);
}
if (rate != exact_rate)
{
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
}
/* Set number of channels to 1 */
if( snd_pcm_hw_params_set_channels(handle, params, 1 ) < 0 )
{
fprintf(stderr, "Error setting channels.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
snd_pcm_uframes_t size = (periodsize * periods) >> 2;
if( (rc = snd_pcm_hw_params_set_buffer_size_near( handle, params, &size )) < 0)
{
fprintf(stderr, "Error setting buffersize: [%s]\n", snd_strerror(rc) );
snd_pcm_close(handle);
return(-1);
}
else
{
printf("Buffer size = %lu\n", (unsigned long)size);
}
/* 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));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
/* Allocate a software parameters object. */
rc = snd_pcm_sw_params_malloc(&sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
rc = snd_pcm_sw_params_current(handle, sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
if((rc = snd_pcm_sw_params_set_avail_min(handle, sw_params, 1024)) < 0)
{
fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (rc));
return(-1);
}
rc = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1);
if( rc < 0 )
{
fprintf(stderr, "Error setting start threshold\n");
snd_pcm_close(handle);
return -1;
}
if((rc = snd_pcm_sw_params(handle, sw_params)) < 0)
{
fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (rc));
return(-1);
}
snd_pcm_sw_params_free(sw_params);
return 0;
}
/* copied from libvorbis source */
int ov_fopen(const char *path, OggVorbis_File *vf)
{
int ret = 0;
FILE *f = fopen(path, "rb");
if( f )
{
ret = ov_open(f, vf, NULL, 0);
if( ret )
{
fclose(f);
}
}
else
{
ret = -1;
}
return( ret );
}
int main(int argc, char *argv[])
{
// sample rate * bytes per sample * channel count * seconds
//int bufferSize = 44100 * 2 * 1 * 2;
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
buffer = (char *) malloc( periodsize );
if( buffer )
{
if((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
printf("Playback open error #1: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if(err = setup_alsa(handle))
{
printf("Playback open error #2: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen(argv[1], &vf);
if(err != 0)
{
perror("Error opening file");
}
else
{
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n", ov_comment(&vf, -1)->vendor);
while(!eof)
{
long ret = ov_read(&vf, buffer, periodsize, 0, 2, 1, ¤t_section);
if(ret == 0)
{
/* EOF */
eof = 1;
}
else if(ret < 0)
{
/* error in the stream. */
fprintf( stderr, "ov_read error %l", ret );
}
else
{
frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
if(frames < 0)
{
printf("snd_pcm_writen failed: %s\n", snd_strerror(frames));
if( frames == -EPIPE )
{
snd_pcm_prepare(handle);
//frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
}
else
{
break;
}
}
}
}
ov_clear(&vf);
}
free( buffer );
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
return 0;
}