You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tderadio/plugins/recording/encoder_ogg.cpp

251 lines
8.3 KiB

/***************************************************************************
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 <tdelocale.h>
#include <stdlib.h>
RecordingEncodingOgg::RecordingEncodingOgg(TQObject *parent, SoundStreamID ssid,
const RecordingConfig &cfg, const RadioStation *rs,
const TQString &filename)
: RecordingEncoding(parent, 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. ").arg(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", "TDERadio" VERSION);
vorbis_comment_add_tag_new(&vc, "title", m_RadioStation->longName().utf8());
vorbis_comment_add_tag_new(&vc, "date", TQDateTime::currentDateTime().toString(TQt::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
}