PageRenderTime 52ms CodeModel.GetById 17ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llaudio/llvorbisencode.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 506 lines | 327 code | 90 blank | 89 comment | 51 complexity | 83862154a615793a8a32ddbe39226d66 MD5 | raw file
  1/** 
  2 * @file vorbisencode.cpp
  3 * @brief Vorbis encoding routine routine for Indra.
  4 *
  5 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#include "vorbis/vorbisenc.h"
 30
 31#include "llvorbisencode.h"
 32#include "llerror.h"
 33#include "llrand.h"
 34#include "llmath.h"
 35#include "llapr.h"
 36
 37//#if LL_DARWIN
 38// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
 39#if 0
 40#include "VorbisFramework.h"
 41
 42#define vorbis_analysis				mac_vorbis_analysis
 43#define vorbis_analysis_headerout	mac_vorbis_analysis_headerout
 44#define vorbis_analysis_init		mac_vorbis_analysis_init
 45#define vorbis_encode_ctl			mac_vorbis_encode_ctl
 46#define vorbis_encode_setup_init	mac_vorbis_encode_setup_init
 47#define vorbis_encode_setup_managed	mac_vorbis_encode_setup_managed
 48
 49#define	vorbis_info_init			mac_vorbis_info_init
 50#define	vorbis_info_clear			mac_vorbis_info_clear
 51#define	vorbis_comment_init			mac_vorbis_comment_init
 52#define	vorbis_comment_clear		mac_vorbis_comment_clear
 53#define	vorbis_block_init			mac_vorbis_block_init
 54#define	vorbis_block_clear			mac_vorbis_block_clear
 55#define	vorbis_dsp_clear			mac_vorbis_dsp_clear
 56#define	vorbis_analysis_buffer		mac_vorbis_analysis_buffer
 57#define	vorbis_analysis_wrote		mac_vorbis_analysis_wrote
 58#define	vorbis_analysis_blockout	mac_vorbis_analysis_blockout
 59
 60#define ogg_stream_packetin			mac_ogg_stream_packetin
 61#define	ogg_stream_init				mac_ogg_stream_init
 62#define	ogg_stream_flush			mac_ogg_stream_flush
 63#define	ogg_stream_pageout			mac_ogg_stream_pageout
 64#define	ogg_page_eos				mac_ogg_page_eos
 65#define	ogg_stream_clear			mac_ogg_stream_clear
 66
 67#endif
 68
 69S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
 70{
 71	U16 num_channels = 0;
 72	U32 sample_rate = 0;
 73	U32 bits_per_sample = 0;
 74	U32 physical_file_size = 0;
 75	U32 chunk_length = 0;
 76	U32 raw_data_length = 0;
 77	U32 bytes_per_sec = 0;
 78	BOOL uncompressed_pcm = FALSE;
 79
 80	unsigned char wav_header[44];		/*Flawfinder: ignore*/
 81
 82	error_msg.clear();
 83
 84	//********************************
 85	LLAPRFile infile ;
 86    infile.open(in_fname,LL_APR_RB);
 87	//********************************
 88	if (!infile.getFileHandle())
 89	{
 90		error_msg = "CannotUploadSoundFile";
 91		return(LLVORBISENC_SOURCE_OPEN_ERR);
 92	}
 93
 94	infile.read(wav_header, 44);
 95	physical_file_size = infile.seek(APR_END,0);
 96
 97	if (strncmp((char *)&(wav_header[0]),"RIFF",4))
 98	{
 99		error_msg = "SoundFileNotRIFF";
100		return(LLVORBISENC_WAV_FORMAT_ERR);
101	}
102
103	if (strncmp((char *)&(wav_header[8]),"WAVE",4))
104	{
105		error_msg = "SoundFileNotRIFF";
106		return(LLVORBISENC_WAV_FORMAT_ERR);
107	}
108	
109	// parse the chunks
110	
111	U32 file_pos = 12;  // start at the first chunk (usually fmt but not always)
112	
113	while ((file_pos + 8)< physical_file_size)
114	{
115		infile.seek(APR_SET,file_pos);
116		infile.read(wav_header, 44);
117
118		chunk_length = ((U32) wav_header[7] << 24) 
119			+ ((U32) wav_header[6] << 16) 
120			+ ((U32) wav_header[5] << 8) 
121			+ wav_header[4];
122
123		if (chunk_length > physical_file_size - file_pos - 4)
124		{
125			infile.close();
126			error_msg = "SoundFileInvalidChunkSize";
127			return(LLVORBISENC_CHUNK_SIZE_ERR);
128		}
129
130//		llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
131
132		if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
133		{
134			if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
135			{
136				uncompressed_pcm = TRUE;
137			}
138			num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
139			sample_rate = ((U32) wav_header[15] << 24) 
140				+ ((U32) wav_header[14] << 16) 
141				+ ((U32) wav_header[13] << 8) 
142				+ wav_header[12];
143			bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
144			bytes_per_sec = ((U32) wav_header[19] << 24) 
145				+ ((U32) wav_header[18] << 16) 
146				+ ((U32) wav_header[17] << 8) 
147				+ wav_header[16];
148		}
149		else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
150		{
151			raw_data_length = chunk_length;			
152		}
153		file_pos += (chunk_length + 8);
154		chunk_length = 0;
155	} 
156	//****************
157	infile.close();
158	//****************
159
160	if (!uncompressed_pcm)
161	{	
162		 error_msg = "SoundFileNotPCM";
163		  return(LLVORBISENC_PCM_FORMAT_ERR);
164	}
165	
166	if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
167	{	
168		error_msg = "SoundFileInvalidChannelCount";
169		return(LLVORBISENC_MULTICHANNEL_ERR);
170	}
171
172	if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
173	{	
174		error_msg = "SoundFileInvalidSampleRate";
175		return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
176	}
177	
178	if ((bits_per_sample != 16) && (bits_per_sample != 8))
179	{		 
180		error_msg = "SoundFileInvalidWordSize";
181		return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
182	}
183
184	if (!raw_data_length)
185	{
186		error_msg = "SoundFileInvalidHeader";
187		return(LLVORBISENC_CLIP_TOO_LONG);		 
188	}
189
190	F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
191		
192	if (clip_length > LLVORBIS_CLIP_MAX_TIME)
193	{
194		error_msg = "SoundFileInvalidTooLong";
195		return(LLVORBISENC_CLIP_TOO_LONG);		 
196	}
197
198    return(LLVORBISENC_NOERR);
199}
200
201S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
202{
203#define READ_BUFFER 1024
204	unsigned char readbuffer[READ_BUFFER*4+44];   /* out of the data segment, not the stack */	/*Flawfinder: ignore*/
205
206	ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
207	ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
208	ogg_packet       op; /* one raw packet of data for decode */
209	
210	vorbis_info      vi; /* struct that stores all the static vorbis bitstream settings */
211	vorbis_comment   vc; /* struct that stores all the user comments */
212	
213	vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
214	vorbis_block     vb; /* local working space for packet->PCM decode */
215	
216	int eos=0;
217	int result;
218
219	U16 num_channels = 0;
220	U32 sample_rate = 0;
221	U32 bits_per_sample = 0;
222
223	S32 format_error = 0;
224	std::string error_msg;
225	if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
226	{
227		llwarns << error_msg << ": " << in_fname << llendl;
228		return(format_error);
229	}
230
231#if 1
232	unsigned char wav_header[44];	/*Flawfinder: ignore*/
233
234	S32 data_left = 0;
235
236	LLAPRFile infile ;
237	infile.open(in_fname,LL_APR_RB);
238	if (!infile.getFileHandle())
239	{
240		llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
241			<< llendl;
242		return(LLVORBISENC_SOURCE_OPEN_ERR);
243	}
244
245	LLAPRFile outfile ;
246	outfile.open(out_fname,LL_APR_WPB);
247	if (!outfile.getFileHandle())
248	{
249		llwarns << "Couldn't open upload sound file for reading: " << in_fname
250			<< llendl;
251		return(LLVORBISENC_DEST_OPEN_ERR);
252	}
253	
254	 // parse the chunks
255	 U32 chunk_length = 0;
256	 U32 file_pos = 12;  // start at the first chunk (usually fmt but not always)
257	 
258	 while (infile.eof() != APR_EOF)
259	 {
260		 infile.seek(APR_SET,file_pos);
261		 infile.read(wav_header, 44);
262		 
263		 chunk_length = ((U32) wav_header[7] << 24) 
264			 + ((U32) wav_header[6] << 16) 
265			 + ((U32) wav_header[5] << 8) 
266			 + wav_header[4];
267		 
268//		 llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
269		 
270		 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
271		 {
272			 num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
273			 sample_rate = ((U32) wav_header[15] << 24) 
274				 + ((U32) wav_header[14] << 16) 
275				 + ((U32) wav_header[13] << 8) 
276				 + wav_header[12];
277			 bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
278		 }
279	 	 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
280		 {
281			 infile.seek(APR_SET,file_pos+8);
282			 // leave the file pointer at the beginning of the data chunk data
283			 data_left = chunk_length;			
284			 break;
285		 }
286		 file_pos += (chunk_length + 8);
287		 chunk_length = 0;
288	 } 
289	 
290
291	 /********** Encode setup ************/
292	 
293	 /* choose an encoding mode */
294	 /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
295	 vorbis_info_init(&vi);
296
297	 // always encode to mono
298
299	 // SL-52913 & SL-53779 determined this quality level to be our 'good
300	 // enough' general-purpose quality level with a nice low bitrate.
301	 // Equivalent to oggenc -q0.5
302	 F32 quality = 0.05f;
303//	 quality = (bitrate==128000 ? 0.4f : 0.1);
304
305//	 if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
306	 if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
307//	 if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
308//		vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
309//		vorbis_encode_setup_init(&vi))
310	{
311		llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
312		//		llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
313		return(LLVORBISENC_DEST_OPEN_ERR);
314	}
315	 
316	 /* add a comment */
317	 vorbis_comment_init(&vc);
318//	 vorbis_comment_add(&vc,"Linden");
319	 
320	 /* set up the analysis state and auxiliary encoding storage */
321	 vorbis_analysis_init(&vd,&vi);
322	 vorbis_block_init(&vd,&vb);
323	 
324	 /* set up our packet->stream encoder */
325	 /* pick a random serial number; that way we can more likely build
326		chained streams just by concatenation */
327	 ogg_stream_init(&os, ll_rand());
328	 
329	 /* Vorbis streams begin with three headers; the initial header (with
330		most of the codec setup parameters) which is mandated by the Ogg
331		bitstream spec.  The second header holds any comment fields.  The
332		third header holds the bitstream codebook.  We merely need to
333		make the headers, then pass them to libvorbis one at a time;
334		libvorbis handles the additional Ogg bitstream constraints */
335	 
336	 {
337		 ogg_packet header;
338		 ogg_packet header_comm;
339		 ogg_packet header_code;
340		 
341		 vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
342		 ogg_stream_packetin(&os,&header); /* automatically placed in its own
343											  page */
344		 ogg_stream_packetin(&os,&header_comm);
345		 ogg_stream_packetin(&os,&header_code);
346		 
347		 /* We don't have to write out here, but doing so makes streaming 
348		  * much easier, so we do, flushing ALL pages. This ensures the actual
349		  * audio data will start on a new page
350		  */
351		 while(!eos){
352			 int result=ogg_stream_flush(&os,&og);
353			 if(result==0)break;
354			 outfile.write(og.header, og.header_len);
355			 outfile.write(og.body, og.body_len);
356		 }
357		 
358	 }
359	 
360	 
361	 while(!eos)
362	 {
363		 long bytes_per_sample = bits_per_sample/8;
364
365		 long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
366		 
367		 if (bytes==0)
368		 {
369			 /* end of file.  this can be done implicitly in the mainline,
370				but it's easier to see here in non-clever fashion.
371				Tell the library we're at end of stream so that it can handle
372				the last frame and mark end of stream in the output properly */
373
374			 vorbis_analysis_wrote(&vd,0);
375//			 eos = 1;
376			 
377		 }
378		 else
379		 {
380			 long i;
381			 long samples;
382			 int temp;
383
384			 data_left -= bytes;
385             /* data to encode */
386			 
387			 /* expose the buffer to submit data */
388			 float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
389			
390			 i = 0;
391			 samples = bytes / (num_channels * bytes_per_sample);
392
393			 if (num_channels == 2)
394			 {
395				 if (bytes_per_sample == 2)
396				 {
397					 /* uninterleave samples */
398					 for(i=0; i<samples ;i++)
399					 {
400					 	 temp =  ((signed char *)readbuffer)[i*4+1];	/*Flawfinder: ignore*/
401						 temp += ((signed char *)readbuffer)[i*4+3];	/*Flawfinder: ignore*/
402						 temp <<= 8;
403						 temp += readbuffer[i*4];
404						 temp += readbuffer[i*4+2];
405
406						 buffer[0][i] = ((float)temp) / 65536.f;
407					 }
408				 }
409				 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
410				 {
411					 /* uninterleave samples */
412					 for(i=0; i<samples ;i++)
413					 {
414					 	 temp  = readbuffer[i*2+0];
415						 temp += readbuffer[i*2+1];
416						 temp -= 256;
417						 buffer[0][i] = ((float)temp) / 256.f;
418					 }
419				 } 
420			 }
421			 else if (num_channels == 1)
422			 {
423				 if (bytes_per_sample == 2)
424				 {
425					 for(i=0; i < samples ;i++)
426					 {
427					 	 temp = ((signed char*)readbuffer)[i*2+1];
428						 temp <<= 8;
429						 temp += readbuffer[i*2];
430						 buffer[0][i] = ((float)temp) / 32768.f;
431					 }
432				 }
433				 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
434				 {
435					 for(i=0; i < samples ;i++)
436					 {
437						 temp = readbuffer[i];
438						 temp -= 128;
439						 buffer[0][i] = ((float)temp) / 128.f;
440					 }
441				 }
442			 }
443				
444			 /* tell the library how much we actually submitted */
445			 vorbis_analysis_wrote(&vd,i);
446		 }
447			 
448		 /* vorbis does some data preanalysis, then divvies up blocks for
449			more involved (potentially parallel) processing.  Get a single
450			block for encoding now */
451		 while(vorbis_analysis_blockout(&vd,&vb)==1)
452		 {
453			 
454			 /* analysis */
455			/* Do the main analysis, creating a packet */
456			vorbis_analysis(&vb, NULL);
457			vorbis_bitrate_addblock(&vb);
458
459			while(vorbis_bitrate_flushpacket(&vd, &op)) 
460			{
461			 
462			 /* weld the packet into the bitstream */
463			 ogg_stream_packetin(&os,&op);
464			 
465			 /* write out pages (if any) */
466			 while(!eos)
467			 {
468				 result = ogg_stream_pageout(&os,&og);
469
470				 if(result==0)
471				 	break;
472
473				 outfile.write(og.header, og.header_len);
474				 outfile.write(og.body, og.body_len);
475				 
476				 /* this could be set above, but for illustrative purposes, I do
477					it here (to show that vorbis does know where the stream ends) */
478				 
479				 if(ogg_page_eos(&og))
480				 	eos=1;
481				 
482			 }
483			}
484		 }
485	 }
486	 
487	 
488	 
489	 /* clean up and exit.  vorbis_info_clear() must be called last */
490	 
491	 ogg_stream_clear(&os);
492	 vorbis_block_clear(&vb);
493	 vorbis_dsp_clear(&vd);
494	 vorbis_comment_clear(&vc);
495	 vorbis_info_clear(&vi);
496	 
497	 /* ogg_page and ogg_packet structs always point to storage in
498		libvorbis.  They're never freed or manipulated directly */
499	 
500//	 fprintf(stderr,"Vorbis encoding: Done.\n");
501	 llinfos << "Vorbis encoding: Done." << llendl;
502	 
503#endif
504	 return(LLVORBISENC_NOERR);
505	 
506}