Re: vorbis decoder stuffs
Ed Sweetman <[email protected]>
| Newsgroups | gmane.comp.audio.zinf.devel |
|---|---|
| Message-ID | <[email protected]> |
Just wanted to update the current progress of the decoder and clear up why i'm spending so much time on a new one while the current one "works". 1. I dont think the callback method of using libvorbisfile is able to be protected. We'd have to force a workaround so the situation doesn't occur and that's very very ugly. 2. It doesn't follow the lmc design. The callback method basically hands over control of the plugin to libvorbisfile. The format of the plugin may look the same on the surface, but it doesn't function like the wav and xing plugins do. These are the unreparable issues with using libvorbisfile to do our decoding. There are fixable ones as well in all current plugins. FILE handles have no place in the lmc's. They will be removed and with it all depending code on them. There are also global variables in each plugin that have the same name as other global variables and such. This cannot be a good thing. If these variables are used outside of the plugin we will need to make a standard way to retrieve their data via method calls to the plugin's class, otherwise they will just be private data to each plugin's lmc class. Some of the issues mentioned above are of no fault to the code in question. There are architectural issues with zinf that create some of these problems and we cant do anything about that without major rewrites. Other players dont have the same issues we're having because they dont have the same structural flaws. But the code written for zinf should not be written so that it simply "works" it should be written so that it works while complying with the as yet unwritten guidelines. and for the status of my vorbis decoder. It plays, seeks (somewhat), stops, pauses, track changes but does not fast track or automatically continue to the next track if you've seeked. This has to do with some discrepency with how many frames are in the current file. IF you left it play through, it has a good estimate but if you seek, it seems to be using a completely different number than you'd think would be used. Seeking is still flakey. I need to fix the discrepency with the "position" number and the way the decoder works with m_frameCounter so that they're measuring the same thing. Anyways, enough of my bs, here's the code. And my work continues. Finals weeks is approaching however, so I hope to have it completely functional before then. Ed Sweetman wrote: > One would think figuring out why your decoder is running way too fast > would be pretty straight forward...but that person would be wrong. > > > I rewrote the decoder to look much like the wavlmc and xinglmc does. In > the process i have new issues to deal with so yea...not very fun stuff. > I'll be working on it as much as i can. It is so close to working that > it hurts and is extremely aggrivating. In the end though the extra > responsiveness is worth it :) > >
vorbislmc.cpp
(text/x-c++src, 20.1 KB)
/*____________________________________________________________________________
Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)
Portions Copyright (C) 2000 Monty
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, Write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: vorbislmc.cpp,v 1.16 2003/03/20 22:25:33 kgk Exp $
____________________________________________________________________________*/
/* system headers */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <vector>
#include <math.h>
using namespace std;
#include "config.h"
#include "errors.h"
#include "pmoevent.h"
#include "eventbuffer.h"
#include "event.h"
#include "eventdata.h"
#include "mutex.h"
#include "semaphore.h"
#include "preferences.h"
#include "lmc.h"
#include "facontext.h"
#include "log.h"
#include "debug.h"
#include "vorbislmc.h"
#define DB printf("%s:%d\n", __FILE__, __LINE__);
extern "C"
{
LogicalMediaConverter *Initialize(FAContext *context) {
return new VorbisLMC(context);
}
}
/*
These are global variables....WHY?!
*/
const int iDefaultBufferUpInterval = 3;
const int iDecodeBlockSize = 8192*2;
const int iFramesPerSecond = 10;
const int iBitrateLoopsPerUpdate = iFramesPerSecond * 3;
const int iInitialOutputBufferSize = 65536;
//const char *szFailRead = N_("Cannot read vorbis data from input plugin.");
const char *szFailWrite = N_("Cannot write audio data to output buffer.");
const char *szCannotDecode = N_("Skipped corrupted file.");
const char *szFailRead = _("Cannot read vorbis data from input plugin.");
VorbisLMC::VorbisLMC(FAContext *context) :
LogicalMediaConverter(context), m_pPmi(NULL), m_pPmo(NULL),
m_decoderThread(NULL), m_szUrl(NULL), m_szError(NULL), pBuffer(NULL),
pOutBuffer(NULL)
{
m_pContext = context;
m_bInit = false;
m_decodeInfo.sendInfo = true;
iMaxFrameSize = 4096;
iReadSize = 0;
}
VorbisLMC::~VorbisLMC()
{
if (m_decoderThread) {
m_bExit = true;
m_pPauseSem->Signal();
m_pSleepSem->Signal();
m_decoderThread->Join();
if(m_bInit) ClrDecode();
delete m_decoderThread;
// m_decoderThread=NULL;
}
}
Error VorbisLMC::Prepare(PullBuffer *pInputBuffer, PullBuffer *&pOutBuffer)
{
m_pInputBuffer = pInputBuffer;
m_pOutputBuffer = new EventBuffer(iInitialOutputBufferSize, 0,
m_pContext);
if (!m_decoderThread) {
m_decoderThread = Thread::CreateThread();
if (!m_decoderThread) {
return kError_CreateThreadFailed;
}
m_decoderThread->Create(VorbisLMC::DecodeWorkerThreadFunc, this);
}
pOutBuffer = m_pOutputBuffer;
m_pInputBuffer->SetName("Input");
m_pOutputBuffer->SetName("Output");
return kError_NoErr;
}
vector<string> *VorbisLMC::GetExtensions(void)
{
vector<string> *extList = new vector<string>;
extList->push_back("OGG");
return extList;
}
void VorbisLMC::ClrDecode()
{
ogg_stream_clear(&os);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
ogg_sync_clear(&oy);
}
// Only possible use for this method is external to the decoder.
Error VorbisLMC::CanDecode()
{
Error err;
if (!m_bInit) {
err = InitDecoder();
if (err != kError_NoErr)
return err;
ClrDecode();
m_bInit = false;
}
return kError_NoErr;
}
Error VorbisLMC::VorbisInit()
{
Error Err = kError_PluginNotInitialized;
int headers = 0, result;
ogg_sync_init(&oy);
(char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
memcpy(pOutBuffer, pBuffer, iReadSize);
ogg_sync_wrote(&oy, iReadSize);
if (ogg_sync_pageout(&oy, &og) != 1) {
return (kError_PluginNotInitialized);
}
serialno = ogg_page_serialno(&og);
ogg_stream_init(&os, serialno);
vorbis_info_init(&vi);
vorbis_comment_init(&vc);
if (ogg_stream_pagein(&os, &og) < 0) {
return (kError_PluginNotInitialized);
}
if (ogg_stream_packetout(&os, &op) != 1) {
return (kError_PluginNotInitialized);
}
if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0) {
return (kError_PluginNotInitialized);
}
// Now read next two identification headers.
headers++;
while (headers < 3) {
while (headers < 3) {
if ((result = ogg_sync_pageout(&oy, &og)) == 0)
break;
if (result == 1) {
ogg_stream_pagein(&os, &og);
while (headers < 3) {
if ((result = ogg_stream_packetout(&os, &op)) == 0)
break;
if (result < 0) {
return (kError_PluginNotInitialized);
}
if (m_bExit) {
return kError_Interrupt;
}
vorbis_synthesis_headerin(&vi, &vc, &op);
headers++;
}
}
}
if (headers < 3) {
m_pInputBuffer->EndRead(iReadSize);
(char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
while ((Err = m_pInputBuffer->BeginRead(pBuffer, iReadSize)) == kError_NoDataAvail) {
m_pPmi->Wake();
if (Sleep()) {
m_pInputBuffer->EndRead(0);
return kError_Interrupt;
}
}
if (Err == kError_EndOfStream) {
m_pInputBuffer->EndRead(0);
return kError_PluginNotInitialized;
}
if (Err != kError_NoErr) {
m_pInputBuffer->EndRead(0);
return (kError_PluginNotInitialized);
}
memcpy(pOutBuffer, pBuffer, iReadSize);
ogg_sync_wrote(&oy, iReadSize);
}
}
vorbis_synthesis_init(&vd, &vi);
vorbis_block_init(&vd, &vb);
return kError_NoErr;
}
Error VorbisLMC::InitDecoder()
{
int iNewSize, bytes;
int result;
OutputInfo *info = NULL;
Error Err = kError_PluginNotInitialized;
Error Err2 = kError_PluginNotInitialized;
if (!m_pTarget || !m_pPmi || !m_pPmo || !m_pInputBuffer || !m_pOutputBuffer) {
return kError_PluginNotInitialized;
}
if (m_bExit)
return kError_Interrupt;
if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&
m_pInputBuffer->GetNumBytesInBuffer() > 0 &&
m_pInputBuffer->IsEndOfStream())
iReadSize = m_pInputBuffer->GetNumBytesInBuffer();
else
iReadSize = iMaxFrameSize;
while ((Err = m_pInputBuffer->BeginRead(pBuffer, iReadSize)) == kError_NoDataAvail) {
m_pPmi->Wake();
if (Sleep()) {
m_pInputBuffer->EndRead(0);
return kError_Interrupt;
}
}
if (Err == kError_NoErr) {
Err2 = VorbisInit();
if (Err2 != kError_NoErr) {
m_pInputBuffer->EndRead(0);
return Err2;
} else
m_pInputBuffer->EndRead(iReadSize);
} else {
m_pInputBuffer->EndRead(0);
return kError_PluginNotInitialized;
}
m_channels = vi.channels;
m_rate = vi.rate;
info = new OutputInfo;
info->bits_per_sample = 16;
info->number_of_channels = vi.channels;
info->samples_per_second = vi.rate;
iMaxWriteSize = info->number_of_channels * (info->bits_per_sample / 8) *
8192 * 2;
info->samples_per_frame = 8192;
info->max_buffer_size = iMaxWriteSize;
m_pContext->prefs->GetPrefInt32(kOutputBufferSizePref, &iNewSize);
iNewSize = max(iNewSize, iMinimumOutputBufferSize);
iNewSize *= 2048;
result = m_pOutputBuffer->Resize(iNewSize, iNewSize / 6);
if (IsError((Error)result)) {
ReportError(_("Internal buffer sizing error occurred."));
m_pContext->log->Error("Resize output buffer failed.");
return (Error)result;
}
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOInitEvent(info));
m_bInit = true;
return kError_NoErr;
}
Error VorbisLMC::ExtractMediaInfo()
{
Error err;
float totalSeconds;
int32_t filesize;
MediaInfoEvent *pMIE;
// If we are here and m_bInit is false then we must not actually be
// decoding the file.
if (!m_bInit) {
err = InitDecoder();
if (err != kError_NoErr)
return err;
m_bInit = false;
}
if (m_bExit) {
if (!m_bInit)
ClrDecode();
return kError_Interrupt;
}
// If we're streaming we should just return the total time to the
// current position we're at. Seems reasonable.
if (m_pPmi->IsStreaming())
totalSeconds = (double)m_pPmi->Tell(filesize) /
((double)vi.bitrate_nominal / 8.0);
else {
// Calculates a rough estimate of the total length of file.
// To get an exact length we'd need to read the last page of the
// file. Not sure it's worth the added cpu time. but it wouldn't be
// too much more cpu time.
err = m_pPmi->GetLength((size_t)filesize);
if (err == kError_NoErr)
totalSeconds = (double)filesize / ((double)vi.bitrate_nominal / 8.0);
else {
// something bad happened
if (!m_bInit)
ClrDecode();
return (err);
}
}
pMIE = new MediaInfoEvent(m_pPmi->Url().c_str(), totalSeconds);
if (!pMIE) {
// Something got screwed up
if (!m_bInit)
ClrDecode();
return kError_OutOfMemory;
}
VorbisInfoEvent *mie = new VorbisInfoEvent(vi.bitrate_nominal,
vi.channels,
vi.rate,
1. / (float)iFramesPerSecond);
if (mie) {
pMIE->AddChildEvent((Event *) mie);
} else {
if (!m_bInit)
ClrDecode();
return kError_OutOfMemory;
}
if (m_pTarget)
m_pTarget->AcceptEvent(pMIE);
// Since we aren't decoding the file, clear our variables up.
if (!m_bInit) {
ClrDecode();
}
return kError_NoErr;
}
// This method is only used external from the decoder, not sure by what
uint32_t VorbisLMC::CalculateSongLength(const char *url)
{
int totalSeconds;
size_t filesize;
Error err;
if (!m_bInit) {
err = InitDecoder();
if (err != kError_NoErr)
return err;
m_bInit = false;
}
m_pPmi->GetLength(filesize);
totalSeconds = (int)((float)filesize / ((float)vi.bitrate_nominal / 8.0));
if (!m_bInit)
ClrDecode();
return (totalSeconds);
}
void VorbisLMC::DecodeWorkerThreadFunc(void *pxlmc)
{
if (pxlmc) {
VorbisLMC *xlmc = (VorbisLMC *) pxlmc;
xlmc->DecodeWork();
}
}
Error VorbisLMC::VDecode(int &vout, void *rOutBuffer)
{
int ret, eos;
ogg_int16_t convbuffer[iMaxWriteSize + 4];
while (1) {
ret = ogg_sync_pageout(&oy, &og);
if (ret <= 0) {
// Need more data, breaking to read methods
return kError_NoErr;
} else {
ret = ogg_stream_pagein(&os, &og);
while (1) {
ret = ogg_stream_packetout(&os, &op);
if (ret <= 0) {
// Need more data, try breaking to read methods
return (kError_NoErr);
} else {
float **pcm;
int samples;
if ((samples = vorbis_synthesis(&vb, &op)) == 0)
vorbis_synthesis_blockin(&vd, &vb);
if (samples) {
return kError_NoErr;
}
while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0
&& !m_bExit) {
int j;
int clipflag = 0;
int bout = (samples < ((iMaxWriteSize - vout) / 4) ? samples : ((iMaxWriteSize - vout) / 4));
if (bout == 0)
return kError_NoErr;
for (int i = 0;i < vi.channels;i++) {
ogg_int16_t *ptr = convbuffer + i;
float *mono = pcm[i];
for (j = 0;j < bout;j++) {
int val = (int)((float)mono[j] * 32767.f);
if (val > 32767) {
val = 32767;
clipflag = 1;
}
if (val < -32768) {
val = -32768;
clipflag = 1;
}
*ptr = val;
ptr += vi.channels;
}
}
memcpy(((char*)rOutBuffer) + vout, convbuffer, bout*2*vi.channels);
vout += (bout * 2 * vi.channels);
vorbis_synthesis_read(&vd, bout);
}
}
}
if (ogg_page_eos(&og)) {
return kError_EndOfStream;
}
}
}
if (m_bExit) {
return kError_Interrupt;
}
return kError_NoErr;
}
Error VorbisLMC::BeginRead(void *&pBuf, unsigned int iBytesNeeded)
{
Error eRet = kError_NoErr;
for (;!m_bExit;) {
eRet = m_pInputBuffer->BeginRead(pBuf, iBytesNeeded);
if (eRet == kError_NoDataAvail) {
m_pPmi->Wake();
if (Sleep()) {
m_pInputBuffer->EndRead(0);
return kError_Interrupt;
}
}
break;
}
if (m_bExit)
return kError_Interrupt;
return eRet;
}
Error VorbisLMC::EndRead(size_t iBytesUsed)
{
return m_pInputBuffer->EndRead(iBytesUsed);
}
void VorbisLMC::DecodeWork()
{
Error Err, Err2;
int32_t iValue;
int32_t ret;
bool bRestart = false;
void *rOutBuffer = 0;
assert(m_pPmi);
assert(m_pPmo);
m_pSleepSem->Wait();
m_pPmi->Wake();
// We have to init the decoder before actually decoding, duh
if (!m_bInit) {
Err = InitDecoder();
if (Err == kError_Interrupt)
return ;
if (IsError(Err)) {
if (m_decodeInfo.sendInfo) {
ReportStatus(szCannotDecode);
m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
} else {
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
}
return ;
}
//cerr << "Initialization stuff here \n";
}
// We are an ogg vorbis file, so tell the player some info about it
Err = ExtractMediaInfo();
if (Err == kError_Interrupt)
return ;
if (IsError(Err)) {
m_pContext->log->Error("ExtractMediaInfo failed: %d\n", Err);
if (m_decodeInfo.sendInfo) {
ReportStatus(szCannotDecode);
m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
} else
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
return ;
}
// This is a non-functional feature. Yay. it looks good in theory
m_pContext->prefs->GetPrefInt32(kDecoderThreadPriorityPref, &iValue);
m_decoderThread->SetPriority(iValue);
// start at frame 0 with 8192 bytes to fill in write buffer.
vout = 0;
for (m_frameCounter = 0; !m_bExit;) {
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(
new PMOTimeInfoEvent(m_frameCounter));
for (;!m_bExit;) {
if (m_bPause) {
m_pPauseSem->Wait();
if (m_bExit)
return ;
continue;
}
if (iMaxFrameSize > (int)m_pInputBuffer->GetNumBytesInBuffer() &&
m_pInputBuffer->GetNumBytesInBuffer() > 0 &&
m_pInputBuffer->IsEndOfStream())
iReadSize = m_pInputBuffer->GetNumBytesInBuffer();
else
iReadSize = iMaxFrameSize;
//cerr << "end of stream " << (int)m_pInputBuffer->IsEndOfStream() << endl;
if (m_pInputBuffer->IsEndOfStream()) {
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
return ;
}
Err = BeginRead(pBuffer, iReadSize);
if (Err == kError_Interrupt) {
return ;
}
/*if (Err == kError_EndOfStream) {
m_bExit = true;
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
return ;
}*/
if (Err == kError_NoDataAvail) {
if (Sleep())
break;
continue;
}
if (Err != kError_NoErr) {
EndRead(0);
ReportError(szFailRead);
m_pContext->log->Error("LMC: Cannot read from pullbuffer: %s\n", m_szError);
return ;
}
Err = m_pOutputBuffer->BeginWrite(rOutBuffer, iMaxWriteSize);
if (Err == kError_Interrupt) {
EndRead(0);
break;
}
if (Err == kError_BufferTooSmall) {
EndRead(0);
m_pOutputBuffer->EndWrite(0);
if (Sleep())
break;
continue;
}
if (Err != kError_NoErr) {
EndRead(0);
if (m_decodeInfo.sendInfo)
ReportError(szFailWrite);
else
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
m_pContext->log->Error("LMC: Cannot write to eventbuffer: %s (%d)\n",
m_szError, Err);
return ;
}
(char*)pOutBuffer = ogg_sync_buffer(&oy, iReadSize);
memcpy(pOutBuffer, pBuffer, iReadSize);
ogg_sync_wrote(&oy, iReadSize);
Err2 = VDecode(vout, rOutBuffer);
if (Err2 != kError_NoErr) {
vout = 0;
m_pOutputBuffer->EndWrite(0);
m_pInputBuffer->EndRead(0);
return ;
}
break;
}
if (m_bExit || Err == kError_Interrupt || Err == kError_EndOfStream) {
return ;
}
if (Err == kError_NoErr) {
EndRead(iReadSize);
m_frameCounter++;
} else
EndRead(0);
m_pPmi->Wake();
if (Err == kError_NoErr && m_pOutputBuffer && vout == iMaxWriteSize) {
Err = m_pOutputBuffer->EndWrite(vout);
vout = 0;
if (Err == kError_Interrupt) {
break;
}
if (IsError(Err)) {
m_pContext->log->Error("lmc: EndWrite returned %d\n", Err);
if (m_decodeInfo.sendInfo)
ReportError(szFailWrite);
else
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
return ;
}
} else {
m_pOutputBuffer->EndWrite(0);
}
}
return ;
}
// This is the annoying bastard method that doesn't wanna work.
Error VorbisLMC::ChangePosition(int position)
{
int32_t dummy;
uint32_t ISeekTo;
cerr << "seeking from pos :" << m_frameCounter << " "
<< position << " " << (m_frameCounter * iMaxFrameSize) / 2
<< " to " << (position * iMaxFrameSize) / 2 << endl;
//m_frameCounter = (position/4 - vout/4);
m_frameCounter = position;
vout = 0;
ISeekTo = ((m_frameCounter) * (iMaxFrameSize));
m_pPmi->Seek(dummy, ISeekTo, SEEK_FROM_START);
return kError_NoErr;
}
// Not sure if this is used anywhere
Error VorbisLMC::SetDecodeInfo(DecodeInfo &info)
{
m_decodeInfo = info;
return kError_NoErr;
}
// Not sure if this is used anywhere.
const string VorbisLMC::ConvertToISO(const char *utf8)
{
unsigned char *in, *buf;
unsigned char *out, *end;
string ret;
in = (unsigned char *)utf8;
buf = out = new unsigned char[strlen(utf8) + 1];
end = in + strlen(utf8);
for (;*in != 0x00 && in <= end; in++, out++) {
if (*in < 0x80) {
/* lower 7-bits unchanged */
*out = *in;
} else if (*in > 0xC3) {
/* discard anything above 0xFF */
*out = '?';
} else if (*in & 0xC0) {
/* parse upper 7-bits */
if (in >= end)
*out = 0;
else {
*out = (((*in) & 0x1F) << 6) | (0x3F & (*(++in)));
}
} else {
*out = '?'; /* this should never happen */
}
}
*out = 0x00; /* append null */
ret = string((char *)buf);
delete[] buf;
return ret;
}
vorbislmc.h
(text/x-chdr, 3.4 KB)
/*____________________________________________________________________________
Zinf - Zinf Is Not FreeA*p (The Free MP3 Player)
Portions Copyright (C) 1998-1999 EMusic.com
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: vorbislmc.h,v 1.4 2003/02/09 19:28:03 rottmann Exp $
____________________________________________________________________________*/
#ifndef INCLUDED_VORBISLMC_H_
#define INCLUDED_VORBISLMC_H_
/* system headers */
#include <stdlib.h>
#include <time.h>
#include <vorbis/vorbisfile.h>
#include <string>
#include <vector>
/* project headers */
#include "config.h"
#include "pmi.h"
#include "pmo.h"
#include "mutex.h"
#include "event.h"
#include "lmc.h"
#include "thread.h"
#include "mutex.h"
#include "queue.h"
#include "semaphore.h"
class VorbisLMC : public LogicalMediaConverter
{
public:
VorbisLMC(FAContext *context);
virtual ~VorbisLMC();
virtual uint32_t CalculateSongLength(const char *url);
virtual Error ChangePosition(int position);
virtual Error CanDecode();
virtual Error ExtractMediaInfo();
virtual void SetPMI(PhysicalMediaInput *pmi)
{
m_pPmi = pmi;
};
virtual void SetPMO(PhysicalMediaOutput *pmo)
{
m_pPmo = pmo;
};
virtual Error Prepare(PullBuffer *pInputBuffer, PullBuffer *&pOutBuffer);
virtual Error InitDecoder();
virtual std::vector<std::string> *GetExtensions(void);
virtual Error SetEQData(float *f, float)
{
return kError_YouScrewedUp;
};
virtual Error SetEQData(bool b)
{
return kError_YouScrewedUp;
};
virtual Error SetDecodeInfo(DecodeInfo &info);
private:
const std::string ConvertToISO(const char *utf8);
Error BeginRead(void *&, unsigned int);
Error EndRead(size_t);
static void DecodeWorkerThreadFunc(void *);
void ClrDecode();
void DecodeWork();
Error VorbisInit();
Error VDecode(int&, void*);
PhysicalMediaInput *m_pPmi;
PhysicalMediaOutput *m_pPmo;
Thread *m_decoderThread;
char *m_szUrl;
const char *m_szError;
bool m_bInit;
int m_channels, m_rate;
long m_frameCounter;
void *pBuffer;
void *pOutBuffer;
int iMaxFrameSize;
int iMaxWriteSize;
int vout;
int iReadSize;
// Ogg vorbis related datastructures
ogg_sync_state oy;
ogg_stream_state os;
ogg_page og;
ogg_packet op;
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_info vi;
vorbis_comment vc;
long serialno;
};
#endif