Related
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 C server that should send to a client the file it requested through the XDR library, using the buffer paradigm. The program uses a custom struct.
enum tagtype {
GET = 0,
OK = 1,
QUIT = 2,
ERR = 3
};
struct file {
opaque contents<>;
unsigned int last_mod_time;
};
union message switch (tagtype tag) {
case GET:
string filename<256>;
case OK:
struct file fdata;
case QUIT:
void;
case ERR:
void;
};
This is the core part of the XDR server.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <rpc/xdr.h>
#include "xdr_file.h"
//main() and call to manage_clixrequest()
int manage_clixrequest(int clisocket_fd) {
FILE *req_file;
struct stat metadata;
char rcv_message[CHUNK_SIZE] = "";
char *buffer, buffer2[2048];
size_t nbytes = 0;
int sent, n;
XDR xdrs_r, xdrs_w; // Pointer to XDR streams
message msg, send_msg;
/* Create codification stream */
xdrmem_create(&xdrs_r, rcv_message, sizeof(rcv_message), XDR_DECODE);
memset(&msg, 0, sizeof(msg));
memset(&send_msg, 0, sizeof(send_msg));
msg.tag = ERR;
msg.message_u.filename = NULL;
msg.message_u.fdata.contents.contents_len = 0;
msg.message_u.fdata.contents.contents_val = NULL;
msg.message_u.fdata.last_mod_time = 0;
/* The server waits for data */
n = recv(clisocket_fd, rcv_message, sizeof(rcv_message), 0);
/* Check if the server received some data */
if (n < 0) {
printf("Errno %d in recv(): %s\n", errno, strerror(errno));
}
if(!xdr_message(&xdrs_r, &msg)){
printf("Error in xdr_message()\n");
return -1;
}
xdr_destroy(&xdrs_r);
/* For a GET message the function extracts the filename
* and send the corresponding file to the client */
if (msg.tag == GET) {
req_file = fopen(msg.message_u.filename, "rb");
/* Check if the file is valid */
if (req_file == NULL) {
if (errno == 2){
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
/* The requested file does not exist, so an error message is sent
* and the connection is going to be shutdown */
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_r);
xdr_destroy(&xdrs_w);
return 0;
} else {
printf("Errno %d in fopen(): %s\n", errno, strerror(errno));
return -1;
}
}
/* Check if the file metadata are correctly retrieved */
if (fstat(fileno(req_file), &metadata) < 0) {
printf("Errno %d in fstat(): %s\n", errno, strerror(errno));
return -1;
}
/* File is read and memorized in a temporary buffer */
buffer = (char *) malloc(sizeof(char)*metadata.st_size);
memset(buffer, 0, metadata.st_size);
nbytes = fread(buffer, sizeof(char), metadata.st_size, req_file);
memset(buffer2, 0, sizeof(buffer2));
/* Create codification stream */
xdrmem_create(&xdrs_w, buffer2, sizeof(buffer2), XDR_ENCODE);
/* This is the beginning of the message for the client
* when the file is going to be sent */
send_msg.tag = OK;
/* The file size and last modification time are retrieved */
send_msg.message_u.fdata.contents.contents_len= metadata.st_size;
send_msg.message_u.fdata.last_mod_time = metadata.st_mtime;
send_msg.message_u.filename = msg.message_u.filename;
send_msg.message_u.fdata.contents.contents_val = buffer;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Error in xdr_message()\n");
return -1;
}
/* The XDR struct is sent with the send() function */
sent = send(clisocket_fd, buffer2, nbytes, 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
return -1;
}
free(buffer);
xdr_destroy(&xdrs_w);
} else if (msg.tag == QUIT) {
/* The client asked to shutdown the connection.
* Unused if the client itself closes the connection */
return 0;
} else {
/* The client sent a request with a not expected tag, so an error message is sent
* and the connection is going to be shutdown*/
xdrmem_create(&xdrs_w, buffer, sizeof(buffer), XDR_ENCODE);
send_msg.tag = ERR;
if(!xdr_message(&xdrs_w, &send_msg)){
printf("Errno %d in xdr_message(): %s\n", errno, strerror(errno));
return -1;
}
sent = send(clisocket_fd, buffer, sizeof(buffer), 0);
/* Check if the server sent the data */
if (sent < 0) {
printf("Errno %d in send(): %s\n", errno, strerror(errno));
}
xdr_destroy(&xdrs_w);
return 0;
}
return 1;
}
Unfortunately the encodification of the message causes a segmentation fault.
Program received signal SIGSEGV, Segmentation fault.
xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58) at xdr_mem.c:124
124 xdr_mem.c: No such file or directory.
I checked the program with GDB and I obtained this backtrace
(gdb) backtrace
#0 xdrmem_putlong (xdrs=0x7fffffffd050, lp=0x7fffffffcf58)
at xdr_mem.c:124
#1 0x00007ffff7b4d99c in __GI_xdr_enum (xdrs=<optimized out>,
ep=0x7fffffffd000) at xdr.c:500
#2 0x000055555555617e in xdr_tagtype (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:13
#3 0x0000555555556214 in xdr_message (xdrs=0x7fffffffd050,
objp=0x7fffffffd000) at xdr_file.c:35
#4 0x0000555555555847 in manage_clixrequest (clisocket_fd=4)
at server3-4.c:273
#5 0x00005555555552a6 in main (argc=3, argv=0x7fffffffdd78)
at server3-4.c:129
where manage_clixrequest(socket_fd) is the function I wrote. I don't know if I am using badly the XDR library, or if I am forgetting to include something so that the xdr_mem.c is not linked correctly. Do you have any ideas/tips?
EDIT
I reported the whole server function and the xdr_file.c.
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "xdr_file.h"
bool_t
xdr_tagtype (XDR *xdrs, tagtype *objp)
{
register int32_t *buf;
if (!xdr_enum (xdrs, (enum_t *) objp))
return FALSE;
return TRUE;
}
bool_t
xdr_file (XDR *xdrs, file *objp)
{
register int32_t *buf;
if (!xdr_bytes (xdrs, (char **)&objp->contents.contents_val, (u_int *) &objp->contents.contents_len, ~0))
return FALSE;
if (!xdr_u_int (xdrs, &objp->last_mod_time))
return FALSE;
return TRUE;
}
bool_t
xdr_message (XDR *xdrs, message *objp)
{
register int32_t *buf;
if (!xdr_tagtype (xdrs, &objp->tag))
return FALSE;
switch (objp->tag) {
case GET:
if (!xdr_string (xdrs, &objp->message_u.filename, 256))
return FALSE;
break;
case OK:
if (!xdr_file (xdrs, &objp->message_u.fdata))
return FALSE;
break;
case QUIT:
break;
case ERR:
break;
default:
return FALSE;
}
return TRUE;
}
I found out that I used the same buffer to memorize the file bytes and to create the XDR stream, that is an error. Now there is no segmentation fault and the XDR struct message is correctly filled, but now the call to the generated function xdr_bytes() returns false and the encodification still fails.
I need to read a file from a separate thread, to avoid hiccups in the flow of my opengl program. I already do that for loading textures and blending them using global variables, that works fine.
However now I need some separate threads to read small data files.
I have created a struct, which basically contains 2 args and 1 result.
It seems I can't do that or I got it wrong somewhere (or in many places)
Here is the sample code of my non proof of concept:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
int* reading; // 0 = not reading , 1 = reading , 2 = finished reading
char* content; // content of the file
char* file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file ( void* data ) {
struct_file_content *thisdata = (struct_file_content*) data;
long length;
int i = 0;
char readchar;
char * thiscontent = 0;
int reading_finished = 2;
int *ptr_to_reading_finished = (int*)malloc(sizeof(int));
ptr_to_reading_finished = &reading_finished;
fprintf(stdout,"thread_Read_file called with file: %s and reading: %u\n",(*thisdata).file,*(*thisdata).reading);
FILE * f = fopen ((*thisdata).file, "r");
if (f) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr,"file %s is too big\n",(*thisdata).file);
exit (1);
}
thiscontent = (char*) malloc (length*sizeof(char));
if ( thiscontent ) {
fread (thiscontent, 1, length, f);
}
fclose (f);
thisdata->content = thiscontent;
} else {
fprintf (stderr, "cannot read file %s\n",(*thisdata).file);
exit (1);
}
sleep(1);
thisdata->reading = ptr_to_reading_finished;
fprintf(stdout,"finished reading: %u\n",*(*thisdata).reading);
fprintf(stdout,"content: %s\n",thiscontent);
pthread_exit(NULL);
return NULL;
}
main()
{
pthread_t thread1;
int filename_length = strlen(content_file);
int rfinish = 2;
int rbegin = 1;
first_file.reading = (int*) malloc(sizeof(int));
first_file.file = (char*) malloc((filename_length+1)*sizeof(char));
first_file.reading = &rbegin;
strcpy(first_file.file,content_file);
pthread_create( &thread1, NULL, thread_read_file, (void*) &first_file);
while ( *(first_file.reading) != 2 ) {
fprintf(stdout,"still reading, reading: %u\n",*(first_file.reading));
sleep(1);
}
fprintf(stdout,"exited control loop with file: %s, reading: %u, content: %s\n",first_file.file, *(first_file.reading), first_file.content);
}
Here's the result:
~/repository/thread/test$ ./tt
still reading, reading: 1
thread_Read_file called with file: /home/tias/content.txt and reading: 1
still reading, reading: 1
still reading, reading: 1
still reading, reading: 1
finished reading: 2
content: foobar!!
still reading, reading: 0
still reading, reading: 0
I was expecting reading = 2 to get out of the loop, instead it is 0.
Any idea on what I have to modify to make it work?
I have read about mutex and so, may be it is the way?
I had found my solution elegant and not working, your help is greatly appreciated.
typedef struct {
int* reading; // 0 = not reading , 1 = reading , 2 = finished reading
char* content; // content of the file
char* file; // file to read
} struct_file_content;
Why is reading a pointer type? It is used in the code as a flag, there is absolutely no need to make it a pointer, especially given that you dynamically allocate memory for this structure field. It complicates the design and is unnecessary. Go ahead and make that an int, not a pointer:
typedef struct {
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
This simplifies the code in main() used to set up first_file:
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
Note that there is no need to have first_file.file be dynamically allocated memory, since you know the file name (and size) at compile time. Keep things simple.
Next, you ignore a possible error return value from pthread_create(3). It returns non-zero in case of failure, and you should check for that. Something like this will do:
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
The code to wait for reading to finish is buggy and racy, you need to either synchronize access to the reading field of struct_file_content with a mutex, or properly wait for the thread to terminate before accessing the first_file again. Since the code does nothing but wait for the thread, pthread_join(3) is a much more reasonable choice here. You'd do something like:
int join_res = pthread_join(thread1, NULL);
if (join_res != 0) {
fprintf(stderr, "pthread_join(3) error: %s\n", strerror(join_res));
exit(EXIT_FAILURE);
}
Inside thread_read_file(), you probably want pthread_exit(3) when handling errors instead of exit(2), since the latter will terminate the entire process, not just the local thread. You also need to handle malloc(3) errors.
Here's the code with all of these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
long length;
char *thiscontent = NULL;
fprintf(stdout,"thread_Read_file called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
FILE *f = fopen (thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr,"file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
int main(void) {
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
int join_res = pthread_join(thread1, NULL);
if (join_res != 0) {
fprintf(stderr, "pthread_join(3) error: %s\n", strerror(join_res));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Read file %s, reading: %u, content: %s\n", first_file.file, first_file.reading, first_file.content);
return 0;
}
UPDATE
From the comments, it seems like you want to do some additional processing and periodically test (at your convenience) whether the thread is done reading. Your approach of using a flag to test for termination is mostly correct, but you should synchronize access to the reading field of struct_file_content to make sure that you always get consistent values. As such, I suggest adding a mutex to struct_file_content that is used to control concurrent access to the reading field. You should lock the mutex every time you need to read or update reading.
So, the structure definition becomes:
typedef struct {
pthread_mutex_t read_mutex; // synchronize access to reading flag
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
Then, as part of initializing struct_file_content, you need to remember to initialize the mutex. Here's how you'd do it in main():
int mutex_err = pthread_mutex_init(&first_file.read_mutex, NULL);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_init(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
Now, the loop in main() simply locks the mutex, checks the status of the reading field (breaking out if it is equal to 2), and unlocks the mutex. Something like:
int read_done = 0;
while (!read_done) {
mutex_err = pthread_mutex_lock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_lock(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
/* Reading is finished when first_file.reading == 2 */
read_done = (first_file.reading == 2);
if (first_file.reading != 2)
printf("Still reading, reading: %u\n", first_file.reading);
mutex_err = pthread_mutex_unlock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_unlock(3) error: %s\n", strerror(mutex_err));
}
}
Of course, you also need to update the thread function to lock the mutex before modifying reading:
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
int mutex_res;
long length;
char *thiscontent = NULL;
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "thread_read_file() called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
FILE *f = fopen(thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr, "file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
That should be enough. Here's the full code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define content_file "/home/tias/content.txt" //this file contains "foobar!!"
typedef struct {
pthread_mutex_t read_mutex; // synchronize access to reading flag
int reading; // 0 = not reading , 1 = reading , 2 = finished reading
char *content; // content of the file
char *file; // file to read
} struct_file_content;
struct_file_content first_file;
void *thread_read_file(void *data) {
struct_file_content *thisdata = data;
int mutex_res;
long length;
char *thiscontent = NULL;
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "thread_read_file() called with file: %s and reading: %u\n", thisdata->file, thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
FILE *f = fopen(thisdata->file, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
if (length > 39) {
fprintf(stderr, "file %s is too big\n", thisdata->file);
pthread_exit(NULL);
}
thiscontent = malloc(length);
if (thiscontent) {
fread(thiscontent, 1, length, f);
} else {
perror("malloc(3) error");
pthread_exit(NULL);
}
fclose(f);
thisdata->content = thiscontent;
} else {
fprintf(stderr, "cannot open file %s\n", thisdata->file);
pthread_exit(NULL);
}
mutex_res = pthread_mutex_lock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
thisdata->reading = 2;
fprintf(stdout, "finished reading: %u\n", thisdata->reading);
mutex_res = pthread_mutex_unlock(&thisdata->read_mutex);
if (mutex_res != 0) {
fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res));
pthread_exit(NULL);
}
fprintf(stdout, "content: %s\n", thiscontent);
return NULL;
}
int main(void) {
int mutex_err = pthread_mutex_init(&first_file.read_mutex, NULL);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_init(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
first_file.reading = 1;
first_file.content = NULL;
first_file.file = content_file;
pthread_t thread1;
int thread_res = pthread_create(&thread1, NULL, thread_read_file, &first_file);
if (thread_res != 0) {
fprintf(stderr, "pthread_create(3) error: %s\n", strerror(thread_res));
exit(EXIT_FAILURE);
}
int read_done = 0;
while (!read_done) {
mutex_err = pthread_mutex_lock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_lock(3) error: %s\n", strerror(mutex_err));
exit(EXIT_FAILURE);
}
/* Reading is finished when first_file.reading == 2 */
read_done = (first_file.reading == 2);
if (first_file.reading != 2)
printf("Still reading, reading: %u\n", first_file.reading);
mutex_err = pthread_mutex_unlock(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "pthread_mutex_unlock(3) error: %s\n", strerror(mutex_err));
}
}
/* Here we don't need to lock because the thread has finished and no other thread is
* using this struct
*/
fprintf(stdout, "Read file %s, reading: %u, content: %s\n", first_file.file, first_file.reading, first_file.content);
free(first_file.content);
mutex_err = pthread_mutex_destroy(&first_file.read_mutex);
if (mutex_err != 0) {
fprintf(stderr, "Warning: Error destroying mutex: %s\n", strerror(mutex_err));
}
return 0;
}
Note that I added cleanup code in the end of main(). Even though it is not necessary (because the program is about to terminate), it is there to make sure you don't forget what kind of cleanup needs to be done once a thread terminates.
This code does not work - issues with passing the data from subroutine to main and allocating memory.
Computations are correct inside the subroutine but the values received by the main are incorrect - variables in main has random values, eg sRates.
#include <stdio.h>
#include <malloc.h>
#include "sndfile.h"
int main(int argc, char *argv[])
{
int sRates , sRatem , ret;
long nSamples=0, nSamplem;
float *datas, *datam;
printf("Read Test\n");
if (argc != 3) {
fprintf(stderr, "Expecting two wav file as argument\n");
return 1;
}
ret = readWav(argv[1], nSamples, sRates, &datas );
if (ret != 0) {
printf("Error\n");
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
// free(datas);
return 0;
}
int readWav(char *fname, long *numFrames, int *sRate, float **buffer )
{
// Open sound file
SF_INFO sndInfo;
SNDFILE *sndFile = sf_open(fname, SFM_READ, &sndInfo);
if (sndFile == NULL) {
fprintf(stderr, "Error reading source file '%s': %s\n", fname, sf_strerror(sndFile));
return 1;
}
printf("1Format of the audio file = %i\n", sndInfo.format);
printf("2Number of channels = %i\n", sndInfo.channels);
printf("3Sample Rate = %d\n", sndInfo.samplerate);
printf("4 Sample count = %ld\n", (long)sndInfo.frames);
sRate= sndInfo.samplerate;
// Allocate memory
buffer = (float *)malloc(sndInfo.frames * sndInfo.channels * sizeof(float));
if (buffer == NULL) {
fprintf(stderr, "Could not allocate memory for file\n");
sf_close(sndFile);
return 1;
}
// Load data
numFrames = sf_readf_float(sndFile, buffer, sndInfo.frames);
// Check correct number of samples loaded
if (numFrames != sndInfo.frames) {
fprintf(stderr, "Did not read enough frames for source\n");
sf_close(sndFile);
free(buffer);
// return 1;
}
else {
printf("Successfully read file\n");
numFrames = sndInfo.frames;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
// numFrames, fname, sndInfo.samplerate, (float)numFrames/sndInfo.samplerate);
numFrames, fname, sRate, (float)numFrames/sndInfo.samplerate);
sf_close(sndFile);
// return(buffer);
return(0);
}
In C all arguments are passed by-value, so if you want a by-ref-like argument you must pass a pointer. And since you want to return a float* you need to pass a float**.
Actually you are passing that, but you are not using it correctly (please use -Wall or equivalent for your compiler to enable warnings).
The code should like more or less like this:
int readWav(const char *fname, long *numFrames, int *sRate, float **buffer)
{
*buffer = malloc(...);
//if you do not feel comfortable writing `*buffer` everywhere:
float *data = *buffer;
///....
*numFrames = sf_readf_float(...);
///....
*sRate = sndInfo.samplerate;
///....
}
int main()
{
long nSamples;
int sRates;
float *datas;
ret = readWav(argv[1], &nSamples, &sRates, &datas);
//...
}
You have several errors in your code
You don't declare readWav() and you call it from main(), it's working by coincidence, namely because it does return int.
You are passing the address of datas to readWav(), note that &datas has type float ** and readWav() is expecting a float *.
If you had compiler warnings turned on, youl'd have noticed this.
You are passing the value of nSamples and sRate to readWav() and you are expecting the nSamples and sRate in your main to get initialized, you need to pass their addresses instead.
You check the return value of readWav() and yet you still try to acces the datas pointer.
This is a fixed version of your code
#include <stdio.h>
#include "sndfile.h"
int readWav(const char *const fname, long *numFrames, int *sRate, float **buffer);
int main(int argc, char *argv[])
{
int sRates, sRatem, ret;
long nSamples = 0, nSamplem;
float *datas, *datam;
printf("Read Test\n");
if (argc != 3) {
fprintf(stderr, "Expecting two wav file as argument\n");
return 1;
}
ret = readWav(argv[1], &nSamples, &sRates, &datas);
if (ret != 0) {
printf("Error\n");
return 1;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
nSamples, argv[1], sRates, (float)nSamples/sRates);
free(datas);
return 0;
}
int readWav(const char *const fname, long *numFrames, int *sRate, float **buffer)
{
// Open sound file
SF_INFO sndInfo;
if ((sRate == NULL) || (numFrames == NULL) || (buffer == NULL)) {
fprintf(stderr, "Invalid arguments passed to readWav()\n");
return 1;
}
SNDFILE *sndFile = sf_open(fname, SFM_READ, &sndInfo);
if (sndFile == NULL) {
fprintf(stderr, "Error reading source file '%s': %s\n", fname, sf_strerror(sndFile));
return 1;
}
printf("1Format of the audio file = %i\n", sndInfo.format);
printf("2Number of channels = %i\n", sndInfo.channels);
printf("3Sample Rate = %d\n", sndInfo.samplerate);
printf("4 Sample count = %ld\n", (long)sndInfo.frames);
// Allocate memory
*buffer = malloc(sndInfo.frames * sndInfo.channels * sizeof(float));
if (*buffer == NULL) {
fprintf(stderr, "Could not allocate memory for file\n");
sf_close(sndFile);
return 1;
}
*sRate = sndInfo.samplerate;
// Load data
*numFrames = sf_readf_float(sndFile, *buffer, sndInfo.frames);
// Check correct number of samples loaded
if (*numFrames != sndInfo.frames) {
fprintf(stderr, "Did not read enough frames for source\n");
sf_close(sndFile);
free(*buffer);
}
else {
printf("Successfully read file\n");
*numFrames = sndInfo.frames;
}
// Output Info
printf("Read %ld frames from %s, Sample rate: %d, Length: %fs\n",
*numFrames, fname, *sRate, (float)*numFrames/sndInfo.samplerate);
sf_close(sndFile);
return(0);
}
Tip: You should try to write your function in such a way that it has only one exit point, I like using goto for that, despite what religious programmers believe about goto, it makes your code more readable consistent and maintainable.
What I mean is you can have a label where you return the error code from the function and do all the cleanup, something like this
int function()
{
/* set errorCode */
if (firstFailureCondition == 1)
goto cleanup;
if (secondFailureCondition == 1)
goto cleanup;
.
.
.
if (nthFailureCondition == 2)
goto cleanup;
cleanup:
/* do your cleanup */
return errorCode;
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Segmentation fault
Currently I am upgrading an open source program used for HTTP streaming. It needs to support the latest FFMPEG.
The code compiles fine with no warnings although I am getting a segmentation fault error.
I would like to know how to fix the issue? and / or the best way to debug? Please find attached a portion of the code due to size. I will try to add the project to github :) Thanks in advance!
Sample Usage
# segmenter --i out.ts --l 10 --o stream.m3u8 --d segments --f stream
Makefile
FFLIBS=`pkg-config --libs libavformat libavcodec libavutil`
FFFLAGS=`pkg-config --cflags libavformat libavcodec libavutil`
all:
gcc -Wall -g segmenter.c -o segmenter ${FFFLAGS} ${FFLIBS}
segmenter.c
/*
* Copyright (c) 2009 Chase Douglas
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include <sys/stat.h>
#include "segmenter.h"
#include "libavformat/avformat.h"
#define IMAGE_ID3_SIZE 9171
void printUsage() {
fprintf(stderr, "\nExample: segmenter --i infile --d baseDir --f baseFileName --o playListFile.m3u8 --l 10 \n");
fprintf(stderr, "\nOptions: \n");
fprintf(stderr, "--i <infile>.\n");
fprintf(stderr, "--o <outfile>.\n");
fprintf(stderr, "--d basedir, the base directory for files.\n");
fprintf(stderr, "--f baseFileName, output files will be baseFileName-#.\n");
fprintf(stderr, "--l segment length, the length of each segment.\n");
fprintf(stderr, "--a, audio only decode for < 64k streams.\n");
fprintf(stderr, "--v, video only decode for < 64k streams.\n");
fprintf(stderr, "--version, print version details and exit.\n");
fprintf(stderr, "\n\n");
}
void ffmpeg_version() {
// output build and version numbers
fprintf(stderr, " libavutil version: %s\n", AV_STRINGIFY(LIBAVUTIL_VERSION));
fprintf(stderr, " libavutil build: %d\n", LIBAVUTIL_BUILD);
fprintf(stderr, " libavcodec version: %s\n", AV_STRINGIFY(LIBAVCODEC_VERSION));
fprintf(stdout, " libavcodec build: %d\n", LIBAVCODEC_BUILD);
fprintf(stderr, " libavformat version: %s\n", AV_STRINGIFY(LIBAVFORMAT_VERSION));
fprintf(stderr, " libavformat build: %d\n", LIBAVFORMAT_BUILD);
fprintf(stderr, " built on " __DATE__ " " __TIME__);
#ifdef __GNUC__
fprintf(stderr, ", gcc: " __VERSION__ "\n");
#else
fprintf(stderr, ", using a non-gcc compiler\n");
#endif
}
static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream) {
AVCodecContext *input_codec_context;
AVCodecContext *output_codec_context;
AVStream *output_stream;
output_stream = avformat_new_stream(output_format_context, 0);
if (!output_stream) {
fprintf(stderr, "Segmenter error: Could not allocate stream\n");
exit(1);
}
input_codec_context = input_stream->codec;
output_codec_context = output_stream->codec;
output_codec_context->codec_id = input_codec_context->codec_id;
output_codec_context->codec_type = input_codec_context->codec_type;
output_codec_context->codec_tag = input_codec_context->codec_tag;
output_codec_context->bit_rate = input_codec_context->bit_rate;
output_codec_context->extradata = input_codec_context->extradata;
output_codec_context->extradata_size = input_codec_context->extradata_size;
if (av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(input_stream->time_base) && av_q2d(input_stream->time_base) < 1.0 / 1000) {
output_codec_context->time_base = input_codec_context->time_base;
output_codec_context->time_base.num *= input_codec_context->ticks_per_frame;
} else {
output_codec_context->time_base = input_stream->time_base;
}
switch (input_codec_context->codec_type) {
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_AUDIO:
#else
case AVMEDIA_TYPE_AUDIO:
#endif
output_codec_context->channel_layout = input_codec_context->channel_layout;
output_codec_context->sample_rate = input_codec_context->sample_rate;
output_codec_context->channels = input_codec_context->channels;
output_codec_context->frame_size = input_codec_context->frame_size;
if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == CODEC_ID_MP3) || input_codec_context->codec_id == CODEC_ID_AC3) {
output_codec_context->block_align = 0;
} else {
output_codec_context->block_align = input_codec_context->block_align;
}
break;
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_VIDEO:
#else
case AVMEDIA_TYPE_VIDEO:
#endif
output_codec_context->pix_fmt = input_codec_context->pix_fmt;
output_codec_context->width = input_codec_context->width;
output_codec_context->height = input_codec_context->height;
output_codec_context->has_b_frames = input_codec_context->has_b_frames;
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
break;
default:
break;
}
return output_stream;
}
int write_index_file(const char index[], const char tmp_index[], const unsigned int planned_segment_duration, const unsigned int actual_segment_duration[],
const char output_directory[], const char output_prefix[], const char output_file_extension[],
const unsigned int first_segment, const unsigned int last_segment) {
FILE *index_fp;
char *write_buf;
unsigned int i;
index_fp = fopen(tmp_index, "w");
if (!index_fp) {
fprintf(stderr, "Could not open temporary m3u8 index file (%s), no index file will be created\n", tmp_index);
return -1;
}
write_buf = malloc(sizeof (char) * 1024);
if (!write_buf) {
fprintf(stderr, "Could not allocate write buffer for index file, index file will be invalid\n");
fclose(index_fp);
return -1;
}
unsigned int maxDuration = planned_segment_duration;
for (i = first_segment; i <= last_segment; i++)
if (actual_segment_duration[i] > maxDuration)
maxDuration = actual_segment_duration[i];
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n", maxDuration);
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
for (i = first_segment; i <= last_segment; i++) {
snprintf(write_buf, 1024, "#EXTINF:%u,\n%s-%u%s\n", actual_segment_duration[i], output_prefix, i, output_file_extension);
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
}
snprintf(write_buf, 1024, "#EXT-X-ENDLIST\n");
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write last file and endlist tag to m3u8 index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
free(write_buf);
fclose(index_fp);
return rename(tmp_index, index);
}
int main(int argc, const char *argv[]) {
//input parameters
char inputFilename[MAX_FILENAME_LENGTH], playlistFilename[MAX_FILENAME_LENGTH], baseDirName[MAX_FILENAME_LENGTH], baseFileName[MAX_FILENAME_LENGTH];
char baseFileExtension[5]; //either "ts", "aac" or "mp3"
int segmentLength, outputStreams, verbosity, version;
char currentOutputFileName[MAX_FILENAME_LENGTH];
char tempPlaylistName[MAX_FILENAME_LENGTH];
//these are used to determine the exact length of the current segment
double prev_segment_time = 0;
double segment_time;
unsigned int actual_segment_durations[2048];
double packet_time = 0;
//new variables to keep track of output size
double output_bytes = 0;
unsigned int output_index = 1;
AVOutputFormat *ofmt;
AVFormatContext *ic = NULL;
AVFormatContext *oc;
AVStream *video_st = NULL;
AVStream *audio_st = NULL;
AVCodec *codec;
int video_index;
int audio_index;
unsigned int first_segment = 1;
unsigned int last_segment = 0;
int write_index = 1;
int decode_done;
int ret;
int i;
unsigned char id3_tag[128];
unsigned char * image_id3_tag;
size_t id3_tag_size = 73;
int newFile = 1; //a boolean value to flag when a new file needs id3 tag info in it
if (parseCommandLine(inputFilename, playlistFilename, baseDirName, baseFileName, baseFileExtension, &outputStreams, &segmentLength, &verbosity, &version, argc, argv) != 0)
return 0;
if (version) {
ffmpeg_version();
return 0;
}
fprintf(stderr, "%s %s\n", playlistFilename, tempPlaylistName);
image_id3_tag = malloc(IMAGE_ID3_SIZE);
if (outputStreams == OUTPUT_STREAM_AUDIO)
build_image_id3_tag(image_id3_tag);
build_id3_tag((char *) id3_tag, id3_tag_size);
snprintf(tempPlaylistName, strlen(playlistFilename) + strlen(baseDirName) + 1, "%s%s", baseDirName, playlistFilename);
strncpy(playlistFilename, tempPlaylistName, strlen(tempPlaylistName));
strncpy(tempPlaylistName, playlistFilename, MAX_FILENAME_LENGTH);
strncat(tempPlaylistName, ".", 1);
//decide if this is an aac file or a mpegts file.
//postpone deciding format until later
/* ifmt = av_find_input_format("mpegts");
if (!ifmt)
{
fprintf(stderr, "Could not find MPEG-TS demuxer.\n");
exit(1);
} */
av_log_set_level(AV_LOG_DEBUG);
av_register_all();
ret = avformat_open_input(&ic, inputFilename, NULL, NULL);
if (ret != 0) {
fprintf(stderr, "Could not open input file %s. Error %d.\n", inputFilename, ret);
exit(1);
}
if (avformat_find_stream_info(ic, NULL) < 0) {
fprintf(stderr, "Could not read stream information.\n");
exit(1);
}
oc = avformat_alloc_context();
if (!oc) {
fprintf(stderr, "Could not allocate output context.");
exit(1);
}
video_index = -1;
audio_index = -1;
for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) {
switch (ic->streams[i]->codec->codec_type) {
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_VIDEO:
#else
case AVMEDIA_TYPE_VIDEO:
#endif
video_index = i;
ic->streams[i]->discard = AVDISCARD_NONE;
if (outputStreams & OUTPUT_STREAM_VIDEO)
video_st = add_output_stream(oc, ic->streams[i]);
break;
#ifdef USE_OLD_FFMPEG
case CODEC_TYPE_AUDIO:
#else
case AVMEDIA_TYPE_AUDIO:
#endif
audio_index = i;
ic->streams[i]->discard = AVDISCARD_NONE;
if (outputStreams & OUTPUT_STREAM_AUDIO)
audio_st = add_output_stream(oc, ic->streams[i]);
break;
default:
ic->streams[i]->discard = AVDISCARD_ALL;
break;
}
}
if (video_index == -1) {
fprintf(stderr, "Stream must have video component.\n");
exit(1);
}
//now that we know the audio and video output streams
//we can decide on an output format.
if (outputStreams == OUTPUT_STREAM_AUDIO) {
//the audio output format should be the same as the audio input format
switch (ic->streams[audio_index]->codec->codec_id) {
case CODEC_ID_MP3:
fprintf(stderr, "Setting output audio to mp3.");
strncpy(baseFileExtension, ".mp3", strlen(".mp3"));
ofmt = av_guess_format("mp3", NULL, NULL);
break;
case CODEC_ID_AAC:
fprintf(stderr, "Setting output audio to aac.");
ofmt = av_guess_format("adts", NULL, NULL);
break;
default:
fprintf(stderr, "Codec id %d not supported.\n", ic->streams[audio_index]->id);
}
if (!ofmt) {
fprintf(stderr, "Could not find audio muxer.\n");
exit(1);
}
} else {
ofmt = av_guess_format("mpegts", NULL, NULL);
if (!ofmt) {
fprintf(stderr, "Could not find MPEG-TS muxer.\n");
exit(1);
}
}
oc->oformat = ofmt;
if (outputStreams & OUTPUT_STREAM_VIDEO && oc->oformat->flags & AVFMT_GLOBALHEADER) {
oc->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
/* Deprecated: pass the options to avformat_write_header directly.
if (av_set_parameters(oc, NULL) < 0) {
fprintf(stderr, "Invalid output format parameters.\n");
exit(1);
}
*/
av_dump_format(oc, 0, baseFileName, 1);
//open the video codec only if there is video data
if (video_index != -1) {
if (outputStreams & OUTPUT_STREAM_VIDEO)
codec = avcodec_find_decoder(video_st->codec->codec_id);
else
codec = avcodec_find_decoder(ic->streams[video_index]->codec->codec_id);
if (!codec) {
fprintf(stderr, "Could not find video decoder, key frames will not be honored.\n");
}
if (outputStreams & OUTPUT_STREAM_VIDEO)
ret = avcodec_open2(video_st->codec, codec, NULL);
else
avcodec_open2(ic->streams[video_index]->codec, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open video decoder, key frames will not be honored.\n");
}
}
snprintf(currentOutputFileName, strlen(baseDirName) + strlen(baseFileName) + strlen(baseFileExtension) + 10, "%s%s-%u%s", baseDirName, baseFileName, output_index++, baseFileExtension);
if (avio_open(&oc->pb, currentOutputFileName, URL_WRONLY) < 0) {
fprintf(stderr, "Could not open '%s'.\n", currentOutputFileName);
exit(1);
}
newFile = 1;
int r = avformat_write_header(oc,NULL);
if (r) {
fprintf(stderr, "Could not write mpegts header to first output file.\n");
debugReturnCode(r);
exit(1);
}
//no segment info is written here. This just creates the shell of the playlist file
write_index = !write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
do {
AVPacket packet;
decode_done = av_read_frame(ic, &packet);
if (decode_done < 0) {
break;
}
if (av_dup_packet(&packet) < 0) {
fprintf(stderr, "Could not duplicate packet.");
av_free_packet(&packet);
break;
}
//this time is used to check for a break in the segments
// if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY))
// {
// segment_time = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
// }
#if USE_OLD_FFMPEG
if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY))
#else
if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY))
#endif
{
segment_time = (double) packet.pts * ic->streams[video_index]->time_base.num / ic->streams[video_index]->time_base.den;
}
// else if (video_index < 0)
// {
// segment_time = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
// }
//get the most recent packet time
//this time is used when the time for the final segment is printed. It may not be on the edge of
//of a keyframe!
if (packet.stream_index == video_index)
packet_time = (double) packet.pts * ic->streams[video_index]->time_base.num / ic->streams[video_index]->time_base.den; //(double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
else if (outputStreams & OUTPUT_STREAM_AUDIO)
packet_time = (double) audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
else
continue;
//start looking for segment splits for videos one half second before segment duration expires. This is because the
//segments are split on key frames so we cannot expect all segments to be split exactly equally.
if (segment_time - prev_segment_time >= segmentLength - 0.5) {
fprintf(stderr, "looking to print index file at time %lf\n", segment_time);
avio_flush(oc->pb);
avio_close(oc->pb);
if (write_index) {
actual_segment_durations[++last_segment] = (unsigned int) rint(segment_time - prev_segment_time);
write_index = !write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
fprintf(stderr, "Writing index file at time %lf\n", packet_time);
}
struct stat st;
stat(currentOutputFileName, &st);
output_bytes += st.st_size;
snprintf(currentOutputFileName, strlen(baseDirName) + strlen(baseFileName) + strlen(baseFileExtension) + 10, "%s%s-%u%s", baseDirName, baseFileName, output_index++, baseFileExtension);
if (avio_open(&oc->pb, currentOutputFileName, URL_WRONLY) < 0) {
fprintf(stderr, "Could not open '%s'\n", currentOutputFileName);
break;
}
newFile = 1;
prev_segment_time = segment_time;
}
if (outputStreams == OUTPUT_STREAM_AUDIO && packet.stream_index == audio_index) {
if (newFile && outputStreams == OUTPUT_STREAM_AUDIO) {
//add id3 tag info
//fprintf(stderr, "adding id3tag to file %s\n", currentOutputFileName);
//printf("%lf %lld %lld %lld %lld %lld %lf\n", segment_time, audio_st->pts.val, audio_st->cur_dts, audio_st->cur_pkt.pts, packet.pts, packet.dts, packet.dts * av_q2d(ic->streams[audio_index]->time_base) );
fill_id3_tag((char*) id3_tag, id3_tag_size, packet.dts);
avio_write(oc->pb, id3_tag, id3_tag_size);
avio_write(oc->pb, image_id3_tag, IMAGE_ID3_SIZE);
avio_flush(oc->pb);
newFile = 0;
}
packet.stream_index = 0; //only one stream in audio only segments
ret = av_interleaved_write_frame(oc, &packet);
} else if (outputStreams & OUTPUT_STREAM_VIDEO) {
if (newFile) {
//fprintf(stderr, "New File: %lld %lld %lld\n", packet.pts, video_st->pts.val, audio_st->pts.val);
//printf("%lf %lld %lld %lld %lld %lld %lf\n", segment_time, audio_st->pts.val, audio_st->cur_dts, audio_st->cur_pkt.pts, packet.pts, packet.dts, packet.dts * av_q2d(ic->streams[audio_index]->time_base) );
newFile = 0;
}
if (outputStreams == OUTPUT_STREAM_VIDEO)
ret = av_write_frame(oc, &packet);
else
ret = av_interleaved_write_frame(oc, &packet);
}
if (ret < 0) {
fprintf(stderr, "Warning: Could not write frame of stream.\n");
} else if (ret > 0) {
fprintf(stderr, "End of stream requested.\n");
av_free_packet(&packet);
break;
}
av_free_packet(&packet);
} while (!decode_done);
//make sure all packets are written and then close the last file.
avio_flush(oc->pb);
av_write_trailer(oc);
if (video_st && video_st->codec)
avcodec_close(video_st->codec);
if (audio_st && audio_st->codec)
avcodec_close(audio_st->codec);
for (i = 0; i < oc->nb_streams; i++) {
av_freep(&oc->streams[i]->codec);
av_freep(&oc->streams[i]);
}
avio_close(oc->pb);
av_free(oc);
struct stat st;
stat(currentOutputFileName, &st);
output_bytes += st.st_size;
if (write_index) {
actual_segment_durations[++last_segment] = (unsigned int) rint(packet_time - prev_segment_time);
//make sure that the last segment length is not zero
if (actual_segment_durations[last_segment] == 0)
actual_segment_durations[last_segment] = 1;
write_index_file(playlistFilename, tempPlaylistName, segmentLength, actual_segment_durations, baseDirName, baseFileName, baseFileExtension, first_segment, last_segment);
}
write_stream_size_file(baseDirName, baseFileName, output_bytes * 8 / segment_time);
return 0;
}
What rudimentary debugging steps have you tried? Have you run your code under gdb or a debugger to discover what line it is crashing on? Or at the very least, added print statements to see how far into your program it is getting before the crash?
Start by compiling your source with the "-g" option in the compiler to add debugging symbols. Then run your code under gdb.
valgrind is another useful tool that might help you find where your program is crashing if it's touching invalid memory.