1 /*
2 * Copyright (C) 2009-2015 Christian Heckendorf <heckendorfc@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details->
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "../plugin.h"
19 #include <vorbis/vorbisfile.h>
20 #include "vorbismeta.c"
21
22 #define VORB_CONTINUE (-50)
23
24 static struct vorbisHandles{
25 OggVorbis_File *vf;
26 unsigned int *total;
27 int rate;
28 int sizemod;
29 }h;
30
plugin_open(const char * path,const char * mode)31 static FILE * plugin_open(const char *path, const char *mode){
32 return plugin_std_fopen(path,mode);
33 }
34
plugin_close(FILE * ffd)35 static void plugin_close(FILE *ffd){
36 plugin_std_fclose(ffd);
37 }
38
filetype_by_data(FILE * ffd)39 static int filetype_by_data(FILE *ffd){
40 unsigned char buf[10];
41 fseek(ffd,0,SEEK_SET);
42 if(!fread(buf,sizeof(buf),1,ffd))return 0;
43 if(buf[0]=='O' && buf[1]=='g' && buf[2]=='g' && buf[3]=='S'){
44 return 1;
45 }
46 return 0;
47 }
48
plugin_seek(struct playerHandles * ph,int modtime)49 static void plugin_seek(struct playerHandles *ph, int modtime){
50 int newtime;
51 int seconds;
52 struct vorbisHandles *h;
53
54 if(ph->dechandle==NULL)
55 return;
56
57 h=(struct vorbisHandles *)ph->dechandle;
58 if(modtime==0){
59 ov_time_seek(h->vf,0);
60 *h->total=0;
61 snd_clear(ph);
62 return;
63 }
64
65 seconds=(*h->total)/((h->rate)*(h->sizemod));
66 seconds+=modtime;
67
68 if(ov_time_seek(h->vf,seconds)!=0)
69 return;
70 newtime=seconds*((h->rate)*(h->sizemod));
71
72 if(newtime<0)
73 newtime=0;
74
75 *h->total=newtime;
76 snd_clear(ph);
77 }
78
79 #define DBGPRNT nullprint
80
nullprint(void * a,void * b,...)81 static void nullprint(void *a,void *b,...){
82 }
83
vorbStatus(int ret)84 static int vorbStatus(int ret){
85 switch(ret){
86 case 0:DBGPRNT(stderr,"\nEOF - done\n");return DEC_RET_SUCCESS;
87 case OV_HOLE:DBGPRNT(stderr,"\nOV_HOLE - data interruption\n");return VORB_CONTINUE;
88 case OV_EBADLINK:DBGPRNT(stderr,"\nOV_EBADLINK - invalid stream\n");break;
89 case OV_EINVAL:DBGPRNT(stderr,"\nOV_EINVAL - read or open error\n");break;
90 default:DBGPRNT(stderr,"\nUnknown return value (%d)\n",ret);
91 }
92 return DEC_RET_ERROR;
93 }
94
95 #if 0
96 static void silencer(){
97 OggVorbis_File vf;
98 (void)ov_open_callbacks(0,&vf,NULL,0,OV_CALLBACKS_DEFAULT);
99 (void)ov_open_callbacks(0,&vf,NULL,0,OV_CALLBACKS_STREAMONLY);
100 (void)ov_open_callbacks(0,&vf,NULL,0,OV_CALLBACKS_STREAMONLY_NOCLOSE);
101 }
102 #endif
103
plugin_run(struct playerHandles * ph,char * key,int * totaltime)104 static int plugin_run(struct playerHandles *ph, char *key, int *totaltime){
105 size_t size;
106 long ret;
107 const ssize_t len=1600;
108 char buf[len]; /* input buffer */
109 unsigned int total=0;
110 int channels, enc, retval=DEC_RET_SUCCESS;
111 unsigned int rate;
112 vorbis_info *vi;
113 OggVorbis_File *vf;
114 struct outputdetail *details=ph->outdetail;
115 int sizemod;
116 char tail[OUTPUT_TAIL_SIZE];
117
118 if(!(vf=malloc(sizeof(OggVorbis_File)))){
119 fprintf(stderr,"Malloc failed (vf).");
120 return DEC_RET_ERROR;
121 }
122
123 if(ov_open_callbacks(ph->ffd,vf,NULL,0,OV_CALLBACKS_NOCLOSE)<0){
124 fprintf(stderr,"ov open failed\n");
125 free(vf);
126 return DEC_RET_ERROR;
127 }
128 details->totaltime=*totaltime;
129 details->percent=-1;
130
131 vi=ov_info(vf,-1);
132 rate=(unsigned int)vi->rate;
133 channels=(unsigned int)vi->channels;
134 enc=16;
135
136 sizemod=2*channels;
137
138 snprintf(tail,OUTPUT_TAIL_SIZE,"New format: %dHz %dch %dbps",rate, channels, (int)vi->bitrate_nominal);
139 addStatusTail(tail,ph->outdetail);
140 snd_param_init(ph,&enc,&channels,&rate);
141
142 h.vf=vf;
143 h.total=&total;
144 h.rate=rate;
145 h.sizemod=sizemod;
146 ph->dechandle=&h;
147
148 details->percent=-1;
149
150 do{ /* Read and write until everything is through. */
151 if((ret=ov_read(vf,buf,len,0,2,1,&vf->current_link))<1){
152 if((retval=vorbStatus(ret))==VORB_CONTINUE)
153 continue;
154 break;
155 }
156 size=ret;
157 details->curtime=total/(rate*sizemod);
158 if(details->totaltime>0)
159 details->percent=(details->curtime*100)/details->totaltime;
160 crOutput(ph->pflag,details);
161
162 #if WITH_ALSA==1
163 if(writei_snd(ph,buf,size/sizemod)<0)break;
164 #else
165 if(writei_snd(ph,buf,size)<0)break;
166 #endif
167 total+=size;
168
169 if(ph->pflag->exit!=DEC_RET_SUCCESS){
170 retval=ph->pflag->exit;
171 break;
172 }
173 }while(1);
174 writei_snd(ph,NULL,0); // drain sound buffer
175
176 /* Done decoding, now just clean up and leave. */
177 ov_clear(vf);
178 *totaltime=details->curtime;
179 return retval;
180 }
181
182 struct pluginitem vorbisitem={
183 .modopen=plugin_open,
184 .modclose=plugin_close,
185 .moddata=filetype_by_data,
186 .modplay=plugin_run,
187 .modseek=plugin_seek,
188 .modmeta=vorbis_plugin_meta,
189 .contenttype="ogg;vorbis",
190 .extension={"ogg","oga",NULL},
191 .name="VORBIS",
192 };
193