Add an example program that streams audio using ffmpeg
[openal-soft/android.git] / examples / alstream.c
blobf25ca0beae30dec22817804678ec86af5d88fa7e
1 /*
2 * OpenAL Audio Stream Example
4 * Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 /* This file contains a relatively simple streaming audio player. */
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <signal.h>
31 #include <assert.h>
33 #include "AL/al.h"
34 #include "AL/alc.h"
35 #include "AL/alext.h"
37 #include "alhelpers.h"
38 #include "alffmpeg.h"
41 /* Define the number of buffers and buffer size (in samples) to use. 4 buffers
42 * with 8192 samples each gives a nice per-chunk size, and lets the queue last
43 * for almost 3/4ths of a second for a 44.1khz stream. */
44 #define NUM_BUFFERS 4
45 #define BUFFER_SIZE 8192
47 typedef struct StreamPlayer {
48 /* These are the buffers and source to play out through OpenAL with */
49 ALuint buffers[NUM_BUFFERS];
50 ALuint source;
52 /* Handles for the audio stream */
53 FilePtr file;
54 StreamPtr stream;
56 /* A temporary data buffer for readAVAudioData to write to and pass to
57 * OpenAL with */
58 ALbyte *data;
59 ALsizei datasize;
61 /* The format of the output stream */
62 ALenum format;
63 ALenum channels;
64 ALenum type;
65 ALuint rate;
66 } StreamPlayer;
68 static StreamPlayer *NewPlayer(void);
69 static void DeletePlayer(StreamPlayer *player);
70 static int OpenPlayerFile(StreamPlayer *player, const char *filename);
71 static void ClosePlayerFile(StreamPlayer *player);
72 static int StartPlayer(StreamPlayer *player);
73 static int UpdatePlayer(StreamPlayer *player);
75 /* Creates a new player object, and allocates the needed OpenAL source and
76 * buffer objects. Error checking is simplified for the purposes of this
77 * example, and will cause an abort if needed. */
78 static StreamPlayer *NewPlayer(void)
80 StreamPlayer *player;
82 player = malloc(sizeof(*player));
83 assert(player != NULL);
85 memset(player, 0, sizeof(*player));
87 /* Generate the buffers and source */
88 alGenBuffers(NUM_BUFFERS, player->buffers);
89 assert(alGetError() == AL_NO_ERROR && "Could not create buffers");
91 alGenSources(1, &player->source);
92 assert(alGetError() == AL_NO_ERROR && "Could not create source");
94 /* Set parameters so mono sources play out the front-center speaker and
95 * won't distance attenuate. */
96 alSource3i(player->source, AL_POSITION, 0, 0, -1);
97 alSourcei(player->source, AL_SOURCE_RELATIVE, AL_TRUE);
98 alSourcei(player->source, AL_ROLLOFF_FACTOR, 0);
99 assert(alGetError() == AL_NO_ERROR && "Could not set source parameters");
101 return player;
104 /* Destroys a player object, deleting the source and buffers. No error handling
105 * since these calls shouldn't fail with a properly-made player object. */
106 static void DeletePlayer(StreamPlayer *player)
108 ClosePlayerFile(player);
110 alDeleteSources(1, &player->source);
111 alDeleteBuffers(NUM_BUFFERS, player->buffers);
112 if(alGetError() != AL_NO_ERROR)
113 fprintf(stderr, "Failed to delete object IDs\n");
115 memset(player, 0, sizeof(*player));
116 free(player);
120 /* Opens the first audio stream of the named file. If a file is already open,
121 * it will be closed first. */
122 static int OpenPlayerFile(StreamPlayer *player, const char *filename)
124 ClosePlayerFile(player);
126 /* Open the file and get the first stream from it */
127 player->file = openAVFile(filename);
128 player->stream = getAVAudioStream(player->file, 0);
129 if(!player->stream)
131 fprintf(stderr, "Could not open audio in %s\n", filename);
132 goto error;
135 /* Get the stream format, and figure out the OpenAL format */
136 if(getAVAudioInfo(player->stream, &player->rate, &player->channels,
137 &player->type) != 0)
139 fprintf(stderr, "Error getting audio info for %s\n", filename);
140 goto error;
143 player->format = GetFormat(player->channels, player->type);
144 if(player->format == 0)
146 fprintf(stderr, "Unsupported format (%s, %s) for %s\n",
147 ChannelsName(player->channels), TypeName(player->type),
148 filename);
149 goto error;
152 /* Allocate enough space for the temp buffer, given the format */
153 player->datasize = FramesToBytes(BUFFER_SIZE, player->channels,
154 player->type);
155 player->data = malloc(player->datasize);
156 if(player->data == NULL)
158 fprintf(stderr, "Error allocating %d bytes\n", player->datasize);
159 goto error;
162 return 1;
164 error:
165 closeAVFile(player->file);
166 player->file = NULL;
167 player->stream = NULL;
168 player->datasize = 0;
170 return 0;
173 /* Closes the audio file stream */
174 static void ClosePlayerFile(StreamPlayer *player)
176 closeAVFile(player->file);
177 player->file = NULL;
178 player->stream = NULL;
180 free(player->data);
181 player->data = NULL;
182 player->datasize = 0;
186 /* Prebuffers some audio from the file, and starts playing the source */
187 static int StartPlayer(StreamPlayer *player)
189 size_t i, got;
191 /* Rewind the source position and clear the buffer queue */
192 alSourceRewind(player->source);
193 alSourcei(player->source, AL_BUFFER, 0);
195 /* Fill the buffer queue */
196 for(i = 0;i < NUM_BUFFERS;i++)
198 /* Get some data to give it to the buffer */
199 got = readAVAudioData(player->stream, player->data, player->datasize);
200 if(got == 0) break;
202 alBufferData(player->buffers[i], player->format, player->data, got,
203 player->rate);
205 if(alGetError() != AL_NO_ERROR)
207 fprintf(stderr, "Error buffering for playback\n");
208 return 0;
211 /* Now queue and start playback! */
212 alSourceQueueBuffers(player->source, i, player->buffers);
213 alSourcePlay(player->source);
214 if(alGetError() != AL_NO_ERROR)
216 fprintf(stderr, "Error starting playback\n");
217 return 0;
220 return 1;
223 static int UpdatePlayer(StreamPlayer *player)
225 ALint processed, state;
227 /* Get relevant source info */
228 alGetSourcei(player->source, AL_SOURCE_STATE, &state);
229 alGetSourcei(player->source, AL_BUFFERS_PROCESSED, &processed);
230 if(alGetError() != AL_NO_ERROR)
232 fprintf(stderr, "Error checking source state\n");
233 return 0;
236 /* Unqueue and handle each processed buffer */
237 while(processed > 0)
239 ALuint bufid;
240 size_t got;
242 alSourceUnqueueBuffers(player->source, 1, &bufid);
243 processed--;
245 /* Read the next chunk of data, refill the buffer, and queue it
246 * back on the source */
247 got = readAVAudioData(player->stream, player->data, player->datasize);
248 if(got > 0)
250 alBufferData(bufid, player->format, player->data, got,
251 player->rate);
252 alSourceQueueBuffers(player->source, 1, &bufid);
254 if(alGetError() != AL_NO_ERROR)
256 fprintf(stderr, "Error buffering data\n");
257 return 0;
261 /* Make sure the source hasn't underrun */
262 if(state != AL_PLAYING && state != AL_PAUSED)
264 ALint queued;
266 /* If no buffers are queued, playback is finished */
267 alGetSourcei(player->source, AL_BUFFERS_QUEUED, &queued);
268 if(queued == 0)
269 return 0;
271 alSourcePlay(player->source);
272 if(alGetError() != AL_NO_ERROR)
274 fprintf(stderr, "Error restarting playback\n");
275 return 0;
279 return 1;
283 int main(int argc, char **argv)
285 StreamPlayer *player;
286 int i;
288 /* Print out usage if no file was specified */
289 if(argc < 2)
291 fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]);
292 return 1;
295 if(InitAL() != 0)
296 return 1;
298 player = NewPlayer();
300 /* Play each file listed on the command line */
301 for(i = 1;i < argc;i++)
303 if(!OpenPlayerFile(player, argv[i]))
304 continue;
306 fprintf(stderr, "Playing %s (%s, %s, %dhz)\n", argv[i],
307 TypeName(player->type), ChannelsName(player->channels),
308 player->rate);
310 if(!StartPlayer(player))
312 ClosePlayerFile(player);
313 continue;
316 while(UpdatePlayer(player))
317 Sleep(10);
319 /* All done with this file. Close it and go to the next */
320 ClosePlayerFile(player);
322 fprintf(stderr, "Done.\n");
324 /* All files done. Delete the player, and close OpenAL */
325 DeletePlayer(player);
326 player = NULL;
328 CloseAL();
330 return 0;