/*************************************************************************** encoder_ogg.cpp ------------------- begin : Sat Aug 20 2005 copyright : (C) 2005 by Martin Witte email : witte@kawo1.rwth-aachen.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "encoder_ogg.h" #include #include RecordingEncodingOgg::RecordingEncodingOgg(TQObject *tqparent, SoundStreamID ssid, const RecordingConfig &cfg, const RadioStation *rs, const TQString &filename) : RecordingEncoding(tqparent, ssid, cfg, rs, filename) #ifdef HAVE_OGG , m_OggOutput(NULL), m_OggExportBuffer(NULL), m_OggExportBufferSize(0) #endif { m_config.m_OutputFormat = RecordingConfig::outputOGG; m_config.m_SoundFormat.m_Encoding = "ogg"; openOutput(filename); } RecordingEncodingOgg::~RecordingEncodingOgg() { closeOutput(); } void RecordingEncodingOgg::encode(const char *_buffer, size_t buffer_size, char *&export_buffer, size_t &export_buffer_size) { if (m_error) return; #ifdef HAVE_OGG SoundFormat &sf = m_config.m_SoundFormat; ogg_page ogg_pg; ogg_packet ogg_pkt; size_t samples = buffer_size / sf.frameSize(); // buffer[channel][sample], normalized to -1..0..+1 float **buffer = vorbis_analysis_buffer(&m_VorbisDSP, (samples < 512 ? 512 : samples)); sf.convertSamplesToFloat(_buffer, buffer, samples); /* Tell the library how many samples (per channel) we wrote into the supplied buffer */ vorbis_analysis_wrote(&m_VorbisDSP, samples); /* While we can get enough data from the library to analyse, one block at a time... */ bool eos = false; while(!m_error && !eos && vorbis_analysis_blockout(&m_VorbisDSP, &m_VorbisBlock) == 1) { /* Do the main analysis, creating a packet */ vorbis_analysis(&m_VorbisBlock, NULL); vorbis_bitrate_addblock(&m_VorbisBlock); while(!m_error && vorbis_bitrate_flushpacket(&m_VorbisDSP, &ogg_pkt)) { /* Add packet to bitstream */ ogg_stream_packetin(&m_OggStream,&ogg_pkt); /* If we've gone over a page boundary, we can do actual output, so do so (for however many pages are available) */ while(!m_error && !eos) { int result = ogg_stream_pageout(&m_OggStream, &ogg_pg); if (!result) break; int n = fwrite(ogg_pg.header, 1, ogg_pg.header_len, m_OggOutput); n += fwrite(ogg_pg.body, 1, ogg_pg.body_len, m_OggOutput); m_encodedSize += n; if (n != (ogg_pg.header_len + ogg_pg.body_len)) { m_error = true; m_errorString += i18n("Failed writing data to ogg/vorbis output stream. "); break; } else { if (m_OggExportBufferSize < export_buffer_size + n) { m_OggExportBuffer = (char*)realloc(m_OggExportBuffer, m_OggExportBufferSize + 2 * n); m_OggExportBufferSize += 2 * n; } memcpy (m_OggExportBuffer + export_buffer_size, ogg_pg.header, ogg_pg.header_len); export_buffer_size += ogg_pg.header_len; memcpy (m_OggExportBuffer + export_buffer_size, ogg_pg.body, ogg_pg.body_len); export_buffer_size += ogg_pg.body_len; } if (ogg_page_eos(&ogg_pg)) eos = 1; } } } export_buffer = m_OggExportBuffer; #endif } #ifdef HAVE_OGG static void vorbis_comment_add_tag_new(vorbis_comment *vc, const TQString &tag, const TQString &value) { char *stag = strdup(tag.ascii()); char *svalue = strdup(value.utf8()); vorbis_comment_add_tag(vc, stag, svalue); delete stag; delete svalue; } #endif bool RecordingEncodingOgg::openOutput(const TQString &output) { #ifdef HAVE_OGG m_OggOutput = fopen(output.ascii(), "wb+"); if (!m_OggOutput) { m_errorString += i18n("Cannot open Ogg/Vorbis output file %1. ").tqarg(output); m_error = true; } m_OggExportBuffer = (char*)malloc(m_OggExportBufferSize = 65536); // start with a 64k buffer /* Have vorbisenc choose a mode for us */ vorbis_info_init(&m_VorbisInfo); SoundFormat &sf = m_config.m_SoundFormat; if (vorbis_encode_setup_vbr(&m_VorbisInfo, sf.m_Channels, sf.m_SampleRate, m_config.m_oggQuality)) { m_error = true; m_errorString = i18n("Ogg/Vorbis Mode initialisation failed: invalid parameters for quality\n"); vorbis_info_clear(&m_VorbisInfo); return false; } /* Turn off management entirely (if it was turned on). */ vorbis_encode_ctl(&m_VorbisInfo, OV_ECTL_RATEMANAGE_SET, NULL); vorbis_encode_setup_init(&m_VorbisInfo); /* Now, set up the analysis engine, stream encoder, and other preparation before the encoding begins. */ vorbis_analysis_init(&m_VorbisDSP, &m_VorbisInfo); vorbis_block_init(&m_VorbisDSP, &m_VorbisBlock); ogg_stream_init (&m_OggStream, m_SoundStreamID.getID()); /* Now, build the three header packets and send through to the stream output stage (but defer actual file output until the main encode loop) */ ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; /* Build the packets */ vorbis_comment vc; vorbis_comment_init (&vc); vorbis_comment_add_tag_new(&vc, "creator", "KRadio" VERSION); vorbis_comment_add_tag_new(&vc, "title", m_RadioStation->longName().utf8()); vorbis_comment_add_tag_new(&vc, "date", TQDateTime::tqcurrentDateTime().toString(Qt::ISODate)); vorbis_analysis_headerout(&m_VorbisDSP, &vc, &header_main, &header_comments, &header_codebooks); /* And stream them out */ ogg_stream_packetin(&m_OggStream, &header_main); ogg_stream_packetin(&m_OggStream, &header_comments); ogg_stream_packetin(&m_OggStream, &header_codebooks); int result; ogg_page ogg_page; while((result = ogg_stream_flush(&m_OggStream, &ogg_page))) { if (!result) break; int n = fwrite(ogg_page.header, 1, ogg_page.header_len, m_OggOutput); n += fwrite(ogg_page.body, 1, ogg_page.body_len, m_OggOutput); if(n != ogg_page.header_len + ogg_page.body_len) { m_error = true; m_errorString += i18n("Failed writing Ogg/Vorbis header to output stream\n"); break; } } vorbis_comment_clear (&vc); if (m_error) { if (m_OggOutput) fclose (m_OggOutput); m_OggOutput = NULL; free(m_OggExportBuffer); m_OggExportBuffer = NULL; m_OggExportBufferSize = 0; ogg_stream_clear(&m_OggStream); vorbis_block_clear(&m_VorbisBlock); vorbis_dsp_clear(&m_VorbisDSP); vorbis_info_clear(&m_VorbisInfo); } return !m_error; #endif } void RecordingEncodingOgg::closeOutput() { #ifdef HAVE_OGG if (m_OggOutput) { char *tmp_buf = NULL; size_t tmp_size = 0; // flush buffer encode(tmp_buf, tmp_size, tmp_buf, tmp_size); fclose(m_OggOutput); m_OggOutput = NULL; free(m_OggExportBuffer); m_OggExportBuffer = NULL; m_OggExportBufferSize = 0; ogg_stream_clear(&m_OggStream); vorbis_block_clear(&m_VorbisBlock); vorbis_dsp_clear(&m_VorbisDSP); vorbis_info_clear(&m_VorbisInfo); } #endif }