af_scaletempo: fix crash after channel reconfiguration
[mplayer.git] / libao2 / ao_mpegpes.c
blobfe3f20fc85b59a5ab27f33adde6273d02f9b471e
1 /*
2 * MPEG-PES audio output driver
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <inttypes.h>
29 #include <errno.h>
31 #include "config.h"
33 #include "audio_out.h"
34 #include "audio_out_internal.h"
36 #include "libaf/af_format.h"
37 #include "libmpdemux/mpeg_packetizer.h"
38 #include "subopt-helper.h"
40 #include "mp_msg.h"
42 #ifdef CONFIG_DVB
43 #include <poll.h>
44 #include <sys/ioctl.h>
45 #include <linux/dvb/audio.h>
46 audio_mixer_t dvb_mixer={255,255};
47 #endif
49 #define true 1
50 #define false 0
52 extern int vo_mpegpes_fd;
53 int ao_mpegpes_fd = -1;
55 #include <errno.h>
57 static const ao_info_t info =
59 #ifdef CONFIG_DVB
60 "DVB audio output",
61 #else
62 "MPEG-PES audio output",
63 #endif
64 "mpegpes",
65 "A'rpi",
69 LIBAO_EXTERN(mpegpes)
72 // to set/get/query special features/parameters
73 static int control(int cmd,void *arg){
74 #ifdef CONFIG_DVB
75 switch(cmd){
76 case AOCONTROL_GET_VOLUME:
77 if(ao_mpegpes_fd >= 0){
78 ((ao_control_vol_t*)(arg))->left=dvb_mixer.volume_left/2.56;
79 ((ao_control_vol_t*)(arg))->right=dvb_mixer.volume_right/2.56;
80 return CONTROL_OK;
82 return CONTROL_ERROR;
83 case AOCONTROL_SET_VOLUME:
84 if(ao_mpegpes_fd >= 0){
85 dvb_mixer.volume_left=((ao_control_vol_t*)(arg))->left*2.56;
86 dvb_mixer.volume_right=((ao_control_vol_t*)(arg))->right*2.56;
87 if(dvb_mixer.volume_left>255) dvb_mixer.volume_left=255;
88 if(dvb_mixer.volume_right>255) dvb_mixer.volume_right=255;
89 // printf("Setting DVB volume: %d ; %d \n",dvb_mixer.volume_left,dvb_mixer.volume_right);
90 if ( (ioctl(vo_mpegpes_fd,AUDIO_SET_MIXER, &dvb_mixer) < 0)){
91 mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] DVB audio set mixer failed: %s.\n",
92 strerror(errno));
93 return CONTROL_ERROR;
95 return CONTROL_OK;
97 return CONTROL_ERROR;
99 #endif
100 return CONTROL_UNKNOWN;
103 static int freq=0;
104 static int freq_id=0;
106 #ifdef CONFIG_DVB
107 static int init_device(int card)
109 char ao_file[30];
110 sprintf(ao_file, "/dev/dvb/adapter%d/audio0", card);
111 mp_msg(MSGT_VO,MSGL_INFO, "Opening %s\n", ao_file);
112 if((ao_mpegpes_fd = open(ao_file,O_RDWR|O_NONBLOCK)) < 0)
114 mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO DEVICE: %s\n", strerror(errno));
115 return -1;
117 if( (ioctl(ao_mpegpes_fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY) < 0))
119 mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SELECT SOURCE: %s\n", strerror(errno));
120 goto fail;
122 if((ioctl(ao_mpegpes_fd, AUDIO_PLAY) < 0))
124 mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO PLAY: %s\n", strerror(errno));
125 goto fail;
127 if((ioctl(ao_mpegpes_fd, AUDIO_SET_AV_SYNC, true) < 0))
129 mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET AV SYNC: %s\n", strerror(errno));
130 goto fail;
132 //FIXME: in vo_mpegpes audio was initialized as MUTEd
133 if((ioctl(ao_mpegpes_fd, AUDIO_SET_MUTE, false) < 0))
135 mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET MUTE: %s\n", strerror(errno));
136 goto fail;
138 return ao_mpegpes_fd;
139 fail:
140 close(ao_mpegpes_fd);
141 ao_mpegpes_fd = -1;
142 return -1;
144 #endif
146 static int preinit(const char *arg)
148 int card = -1;
149 char *ao_file = NULL;
151 const opt_t subopts[] = {
152 {"card", OPT_ARG_INT, &card, NULL},
153 {"file", OPT_ARG_MSTRZ, &ao_file, NULL},
154 {NULL}
157 if(subopt_parse(ao_subdevice, subopts) != 0)
159 mp_msg(MSGT_VO, MSGL_ERR, "AO_MPEGPES, Unrecognized options\n");
160 return -1;
162 if(card==-1)
164 //search the first usable card
165 int n;
166 char file[30];
167 for(n=0; n<4; n++)
169 sprintf(file, "/dev/dvb/adapter%d/audio0", n);
170 if(access(file, F_OK | W_OK)==0)
172 card = n+1;
173 break;
177 if((card < 1) || (card > 4))
179 mp_msg(MSGT_VO, MSGL_ERR, "DVB card number must be between 1 and 4\n");
180 return -1;
182 card--;
184 #ifdef CONFIG_DVB
185 if(!ao_file)
186 return init_device(card);
187 #else
188 if(!ao_file)
189 return vo_mpegpes_fd; //video fd
190 #endif
192 ao_mpegpes_fd = open(ao_file, O_WRONLY | O_CREAT, 0666);
193 if(ao_mpegpes_fd < 0)
195 mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes: %s\n", strerror(errno));
196 return -1;
198 return ao_mpegpes_fd;
201 static int my_ao_write(const unsigned char* data,int len){
202 int orig_len = len;
203 #ifdef CONFIG_DVB
204 #define NFD 1
205 struct pollfd pfd[NFD];
207 pfd[0].fd = ao_mpegpes_fd;
208 pfd[0].events = POLLOUT;
210 while(len>0){
211 if(poll(pfd,NFD,1)){
212 if(pfd[0].revents & POLLOUT){
213 int ret = write(ao_mpegpes_fd, data, len);
214 if(ret<=0){
215 mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes write: %s\n", strerror(errno));
216 usleep(0);
217 } else {
218 len-=ret;
219 data+=ret;
221 } else usleep(1000);
225 #else
226 if(ao_mpegpes_fd < 0) return 0; // no file
227 write(ao_mpegpes_fd, data, len); // write to file
228 #endif
229 return orig_len;
233 // open & setup audio device
234 // return: 1=success 0=fail
235 static int init(int rate,int channels,int format,int flags){
236 if(preinit(NULL)<0) return 0;
238 ao_data.channels=2;
239 ao_data.outburst=2000;
240 switch(format){
241 case AF_FORMAT_S16_BE:
242 case AF_FORMAT_MPEG2:
243 case AF_FORMAT_AC3_BE:
244 ao_data.format=format;
245 break;
246 case AF_FORMAT_AC3_LE:
247 ao_data.format=AF_FORMAT_AC3_BE;
248 break;
249 default:
250 ao_data.format=AF_FORMAT_S16_BE;
253 switch(rate){
254 case 48000: freq_id=0;break;
255 case 96000: freq_id=1;break;
256 case 44100: freq_id=2;break;
257 case 32000: freq_id=3;break;
258 default:
259 mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] %d Hz not supported, try to resample.\n", rate);
260 #if 0
261 if(rate>48000) rate=96000; else
262 if(rate>44100) rate=48000; else
263 if(rate>32000) rate=44100; else
264 rate=32000;
265 goto retry;
266 #else
267 rate=48000; freq_id=0;
268 #endif
271 ao_data.bps=rate*2*2;
272 freq=ao_data.samplerate=rate;
274 return 1;
277 // close audio device
278 static void uninit(int immed){
279 if (ao_mpegpes_fd >= 0)
280 close(ao_mpegpes_fd);
281 ao_mpegpes_fd = -1;
284 // stop playing and empty buffers (for seeking/pause)
285 static void reset(void){
289 // stop playing, keep buffers (for pause)
290 static void audio_pause(void)
292 // for now, just call reset();
293 reset();
296 // resume playing, after audio_pause()
297 static void audio_resume(void)
301 extern int vo_pts;
303 // return: how many bytes can be played without blocking
304 static int get_space(void){
305 float x=(float)(vo_pts-ao_data.brokenpts)/90000.0;
306 int y;
307 //FIXME: is it correct?
308 if(vo_mpegpes_fd < 0) return 32000; //not using -vo mpegpes
309 // printf("vo_pts: %5.3f ao_pts: %5.3f\n",vo_pts/90000.0,ao_data.brokenpts/90000.0);
310 if(x<=0) return 0;
311 y=freq*4*x;y/=ao_data.outburst;y*=ao_data.outburst;
312 if(y>32000) y=32000;
313 // printf("diff: %5.3f -> %d \n",x,y);
314 return y;
317 // plays 'len' bytes of 'data'
318 // it should round it down to outburst*n
319 // return: number of bytes played
320 static int play(void* data,int len,int flags){
321 // printf("\nao_mpegpes: play(%d) freq=%d\n",len,freq_id);
322 if(ao_data.format==AF_FORMAT_MPEG2)
323 send_mpeg_pes_packet (data, len, 0x1C0, ao_data.brokenpts, 1, my_ao_write);
324 else {
325 // if(len>2000) len=2000;
326 // printf("ao_mpegpes: len=%d \n",len);
327 send_mpeg_lpcm_packet(data, len, 0xA0, ao_data.brokenpts, freq_id, my_ao_write);
329 return len;
332 // return: delay in seconds between first and last sample in buffer
333 static float get_delay(void){
335 return 0.0;