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