llvorbisencode.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
- /**
- * @file vorbisencode.cpp
- * @brief Vorbis encoding routine routine for Indra.
- *
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "vorbis/vorbisenc.h"
- #include "llvorbisencode.h"
- #include "llerror.h"
- #include "llrand.h"
- #include "llmath.h"
- #include "llapr.h"
- //#if LL_DARWIN
- // MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
- #if 0
- #include "VorbisFramework.h"
- #define vorbis_analysis mac_vorbis_analysis
- #define vorbis_analysis_headerout mac_vorbis_analysis_headerout
- #define vorbis_analysis_init mac_vorbis_analysis_init
- #define vorbis_encode_ctl mac_vorbis_encode_ctl
- #define vorbis_encode_setup_init mac_vorbis_encode_setup_init
- #define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
- #define vorbis_info_init mac_vorbis_info_init
- #define vorbis_info_clear mac_vorbis_info_clear
- #define vorbis_comment_init mac_vorbis_comment_init
- #define vorbis_comment_clear mac_vorbis_comment_clear
- #define vorbis_block_init mac_vorbis_block_init
- #define vorbis_block_clear mac_vorbis_block_clear
- #define vorbis_dsp_clear mac_vorbis_dsp_clear
- #define vorbis_analysis_buffer mac_vorbis_analysis_buffer
- #define vorbis_analysis_wrote mac_vorbis_analysis_wrote
- #define vorbis_analysis_blockout mac_vorbis_analysis_blockout
- #define ogg_stream_packetin mac_ogg_stream_packetin
- #define ogg_stream_init mac_ogg_stream_init
- #define ogg_stream_flush mac_ogg_stream_flush
- #define ogg_stream_pageout mac_ogg_stream_pageout
- #define ogg_page_eos mac_ogg_page_eos
- #define ogg_stream_clear mac_ogg_stream_clear
- #endif
- S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
- {
- U16 num_channels = 0;
- U32 sample_rate = 0;
- U32 bits_per_sample = 0;
- U32 physical_file_size = 0;
- U32 chunk_length = 0;
- U32 raw_data_length = 0;
- U32 bytes_per_sec = 0;
- BOOL uncompressed_pcm = FALSE;
- unsigned char wav_header[44]; /*Flawfinder: ignore*/
- error_msg.clear();
- //********************************
- LLAPRFile infile ;
- infile.open(in_fname,LL_APR_RB);
- //********************************
- if (!infile.getFileHandle())
- {
- error_msg = "CannotUploadSoundFile";
- return(LLVORBISENC_SOURCE_OPEN_ERR);
- }
- infile.read(wav_header, 44);
- physical_file_size = infile.seek(APR_END,0);
- if (strncmp((char *)&(wav_header[0]),"RIFF",4))
- {
- error_msg = "SoundFileNotRIFF";
- return(LLVORBISENC_WAV_FORMAT_ERR);
- }
- if (strncmp((char *)&(wav_header[8]),"WAVE",4))
- {
- error_msg = "SoundFileNotRIFF";
- return(LLVORBISENC_WAV_FORMAT_ERR);
- }
-
- // parse the chunks
-
- U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
-
- while ((file_pos + 8)< physical_file_size)
- {
- infile.seek(APR_SET,file_pos);
- infile.read(wav_header, 44);
- chunk_length = ((U32) wav_header[7] << 24)
- + ((U32) wav_header[6] << 16)
- + ((U32) wav_header[5] << 8)
- + wav_header[4];
- // llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
- if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
- {
- if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
- {
- uncompressed_pcm = TRUE;
- }
- num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
- sample_rate = ((U32) wav_header[15] << 24)
- + ((U32) wav_header[14] << 16)
- + ((U32) wav_header[13] << 8)
- + wav_header[12];
- bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
- bytes_per_sec = ((U32) wav_header[19] << 24)
- + ((U32) wav_header[18] << 16)
- + ((U32) wav_header[17] << 8)
- + wav_header[16];
- }
- else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
- {
- raw_data_length = chunk_length;
- }
- file_pos += (chunk_length + 8);
- chunk_length = 0;
- }
- //****************
- infile.close();
- //****************
- if (!uncompressed_pcm)
- {
- error_msg = "SoundFileNotPCM";
- return(LLVORBISENC_PCM_FORMAT_ERR);
- }
-
- if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
- {
- error_msg = "SoundFileInvalidChannelCount";
- return(LLVORBISENC_MULTICHANNEL_ERR);
- }
- if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
- {
- error_msg = "SoundFileInvalidSampleRate";
- return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
- }
-
- if ((bits_per_sample != 16) && (bits_per_sample != 8))
- {
- error_msg = "SoundFileInvalidWordSize";
- return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
- }
- if (!raw_data_length)
- {
- error_msg = "SoundFileInvalidHeader";
- return(LLVORBISENC_CLIP_TOO_LONG);
- }
- F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
-
- if (clip_length > LLVORBIS_CLIP_MAX_TIME)
- {
- error_msg = "SoundFileInvalidTooLong";
- return(LLVORBISENC_CLIP_TOO_LONG);
- }
- return(LLVORBISENC_NOERR);
- }
- S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
- {
- #define READ_BUFFER 1024
- unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
- ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
- ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
- ogg_packet op; /* one raw packet of data for decode */
-
- vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
- vorbis_comment vc; /* struct that stores all the user comments */
-
- vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
- vorbis_block vb; /* local working space for packet->PCM decode */
-
- int eos=0;
- int result;
- U16 num_channels = 0;
- U32 sample_rate = 0;
- U32 bits_per_sample = 0;
- S32 format_error = 0;
- std::string error_msg;
- if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
- {
- llwarns << error_msg << ": " << in_fname << llendl;
- return(format_error);
- }
- #if 1
- unsigned char wav_header[44]; /*Flawfinder: ignore*/
- S32 data_left = 0;
- LLAPRFile infile ;
- infile.open(in_fname,LL_APR_RB);
- if (!infile.getFileHandle())
- {
- llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
- << llendl;
- return(LLVORBISENC_SOURCE_OPEN_ERR);
- }
- LLAPRFile outfile ;
- outfile.open(out_fname,LL_APR_WPB);
- if (!outfile.getFileHandle())
- {
- llwarns << "Couldn't open upload sound file for reading: " << in_fname
- << llendl;
- return(LLVORBISENC_DEST_OPEN_ERR);
- }
-
- // parse the chunks
- U32 chunk_length = 0;
- U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
-
- while (infile.eof() != APR_EOF)
- {
- infile.seek(APR_SET,file_pos);
- infile.read(wav_header, 44);
-
- chunk_length = ((U32) wav_header[7] << 24)
- + ((U32) wav_header[6] << 16)
- + ((U32) wav_header[5] << 8)
- + wav_header[4];
-
- // llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
-
- if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
- {
- num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
- sample_rate = ((U32) wav_header[15] << 24)
- + ((U32) wav_header[14] << 16)
- + ((U32) wav_header[13] << 8)
- + wav_header[12];
- bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
- }
- else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
- {
- infile.seek(APR_SET,file_pos+8);
- // leave the file pointer at the beginning of the data chunk data
- data_left = chunk_length;
- break;
- }
- file_pos += (chunk_length + 8);
- chunk_length = 0;
- }
-
- /********** Encode setup ************/
-
- /* choose an encoding mode */
- /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
- vorbis_info_init(&vi);
- // always encode to mono
- // SL-52913 & SL-53779 determined this quality level to be our 'good
- // enough' general-purpose quality level with a nice low bitrate.
- // Equivalent to oggenc -q0.5
- F32 quality = 0.05f;
- // quality = (bitrate==128000 ? 0.4f : 0.1);
- // if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
- if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
- // if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
- // vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
- // vorbis_encode_setup_init(&vi))
- {
- llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
- // llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
- return(LLVORBISENC_DEST_OPEN_ERR);
- }
-
- /* add a comment */
- vorbis_comment_init(&vc);
- // vorbis_comment_add(&vc,"Linden");
-
- /* set up the analysis state and auxiliary encoding storage */
- vorbis_analysis_init(&vd,&vi);
- vorbis_block_init(&vd,&vb);
-
- /* set up our packet->stream encoder */
- /* pick a random serial number; that way we can more likely build
- chained streams just by concatenation */
- ogg_stream_init(&os, ll_rand());
-
- /* Vorbis streams begin with three headers; the initial header (with
- most of the codec setup parameters) which is mandated by the Ogg
- bitstream spec. The second header holds any comment fields. The
- third header holds the bitstream codebook. We merely need to
- make the headers, then pass them to libvorbis one at a time;
- libvorbis handles the additional Ogg bitstream constraints */
-
- {
- ogg_packet header;
- ogg_packet header_comm;
- ogg_packet header_code;
-
- vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
- ogg_stream_packetin(&os,&header); /* automatically placed in its own
- page */
- ogg_stream_packetin(&os,&header_comm);
- ogg_stream_packetin(&os,&header_code);
-
- /* We don't have to write out here, but doing so makes streaming
- * much easier, so we do, flushing ALL pages. This ensures the actual
- * audio data will start on a new page
- */
- while(!eos){
- int result=ogg_stream_flush(&os,&og);
- if(result==0)break;
- outfile.write(og.header, og.header_len);
- outfile.write(og.body, og.body_len);
- }
-
- }
-
-
- while(!eos)
- {
- long bytes_per_sample = bits_per_sample/8;
- long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
-
- if (bytes==0)
- {
- /* end of file. this can be done implicitly in the mainline,
- but it's easier to see here in non-clever fashion.
- Tell the library we're at end of stream so that it can handle
- the last frame and mark end of stream in the output properly */
- vorbis_analysis_wrote(&vd,0);
- // eos = 1;
-
- }
- else
- {
- long i;
- long samples;
- int temp;
- data_left -= bytes;
- /* data to encode */
-
- /* expose the buffer to submit data */
- float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
-
- i = 0;
- samples = bytes / (num_channels * bytes_per_sample);
- if (num_channels == 2)
- {
- if (bytes_per_sample == 2)
- {
- /* uninterleave samples */
- for(i=0; i<samples ;i++)
- {
- temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
- temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
- temp <<= 8;
- temp += readbuffer[i*4];
- temp += readbuffer[i*4+2];
- buffer[0][i] = ((float)temp) / 65536.f;
- }
- }
- else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
- {
- /* uninterleave samples */
- for(i=0; i<samples ;i++)
- {
- temp = readbuffer[i*2+0];
- temp += readbuffer[i*2+1];
- temp -= 256;
- buffer[0][i] = ((float)temp) / 256.f;
- }
- }
- }
- else if (num_channels == 1)
- {
- if (bytes_per_sample == 2)
- {
- for(i=0; i < samples ;i++)
- {
- temp = ((signed char*)readbuffer)[i*2+1];
- temp <<= 8;
- temp += readbuffer[i*2];
- buffer[0][i] = ((float)temp) / 32768.f;
- }
- }
- else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
- {
- for(i=0; i < samples ;i++)
- {
- temp = readbuffer[i];
- temp -= 128;
- buffer[0][i] = ((float)temp) / 128.f;
- }
- }
- }
-
- /* tell the library how much we actually submitted */
- vorbis_analysis_wrote(&vd,i);
- }
-
- /* vorbis does some data preanalysis, then divvies up blocks for
- more involved (potentially parallel) processing. Get a single
- block for encoding now */
- while(vorbis_analysis_blockout(&vd,&vb)==1)
- {
-
- /* analysis */
- /* Do the main analysis, creating a packet */
- vorbis_analysis(&vb, NULL);
- vorbis_bitrate_addblock(&vb);
- while(vorbis_bitrate_flushpacket(&vd, &op))
- {
-
- /* weld the packet into the bitstream */
- ogg_stream_packetin(&os,&op);
-
- /* write out pages (if any) */
- while(!eos)
- {
- result = ogg_stream_pageout(&os,&og);
- if(result==0)
- break;
- outfile.write(og.header, og.header_len);
- outfile.write(og.body, og.body_len);
-
- /* this could be set above, but for illustrative purposes, I do
- it here (to show that vorbis does know where the stream ends) */
-
- if(ogg_page_eos(&og))
- eos=1;
-
- }
- }
- }
- }
-
-
-
- /* clean up and exit. vorbis_info_clear() must be called last */
-
- ogg_stream_clear(&os);
- vorbis_block_clear(&vb);
- vorbis_dsp_clear(&vd);
- vorbis_comment_clear(&vc);
- vorbis_info_clear(&vi);
-
- /* ogg_page and ogg_packet structs always point to storage in
- libvorbis. They're never freed or manipulated directly */
-
- // fprintf(stderr,"Vorbis encoding: Done.n");
- llinfos << "Vorbis encoding: Done." << llendl;
-
- #endif
- return(LLVORBISENC_NOERR);
-
- }