📄 vorbislmc.cpp
字号:
/*____________________________________________________________________________
FreeAmp - 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.17 2001/03/08 09:46:05 robert Exp $
____________________________________________________________________________*/
/* system headers */
#include <stdlib.h>
#include <stdio.h>
#include <iostream.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#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);
}
}
const int iDecodeBlockSize = 8192;
const int iFramesPerSecond = 10;
const int iBitrateLoopsPerUpdate = iFramesPerSecond * 3;
const int iInitialOutputBufferSize = 64512;
const char *szFailRead = "Cannot read vorbis data from input plugin.";
const char *szFailWrite = "Cannot write audio data to output buffer.";
const char *szCannotDecode = "Skipped corrupted file.";
VorbisLMC::VorbisLMC(FAContext *context) :
LogicalMediaConverter(context)
{
m_pContext = context;
m_decoderThread = NULL;
m_szUrl = NULL;
m_szError = NULL;
m_bInit = false;
m_newPos = -1;
m_decodeInfo.sendInfo = true;
}
VorbisLMC::~VorbisLMC()
{
if (m_decoderThread)
{
m_bExit = true;
m_pPauseSem->Signal();
m_pSleepSem->Signal();
m_decoderThread->Join();
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;
}
void VorbisLMC::Clear()
{
if (m_pOutputBuffer)
((EventBuffer *)m_pOutputBuffer)->Clear();
}
vector<string> *VorbisLMC::GetExtensions(void)
{
vector<string> *extList = new vector<string>;
extList->push_back("OGG");
return extList;
}
Error VorbisLMC::CanDecode()
{
Error err;
if (!m_bInit)
{
err = InitDecoder();
if (err != kError_NoErr)
return err;
}
return kError_NoErr;
}
Error VorbisLMC::InitDecoder()
{
ov_callbacks callbacks;
vorbis_info *vi;
Error result;
int iNewSize;
if (!m_pTarget || !m_pPmi || !m_pPmo || !m_pInputBuffer || !m_pOutputBuffer)
{
return kError_PluginNotInitialized;
}
memset(&m_vf, 0, sizeof(m_vf));
callbacks.read_func = VorbisLMC::ReadWrapper;
callbacks.seek_func = VorbisLMC::SeekWrapper;
callbacks.close_func = VorbisLMC::CloseWrapper;
callbacks.tell_func = VorbisLMC::TellWrapper;
if (ov_open_callbacks(this, &m_vf, NULL, 0, callbacks) < 0)
return kError_UnknownErr;
vi = ov_info(&m_vf, -1);
m_channels = vi->channels;
m_rate = vi->rate;
m_section = -1;
m_pContext->prefs->GetPrefInt32(kOutputBufferSizePref, &iNewSize);
iNewSize = max(iNewSize, iMinimumOutputBufferSize);
iNewSize *= 1024;
result = m_pOutputBuffer->Resize(iNewSize, iNewSize / 6);
if (IsError(result))
{
ReportError("Internal buffer sizing error occurred.");
m_pContext->log->Error("Resize output buffer failed.");
return result;
}
m_bInit = true;
return kError_NoErr;
}
Error VorbisLMC::ExtractMediaInfo()
{
Error err;
float totalSeconds;
MediaInfoEvent *pMIE;
vorbis_info *vi;
if (!m_bInit)
{
err = InitDecoder();
if (err != kError_NoErr)
return err;
}
totalSeconds = ov_time_total(&m_vf, 0);
vi = ov_info(&m_vf, -1);
pMIE = new MediaInfoEvent(m_pPmi->Url(), totalSeconds);
if (!pMIE)
return kError_OutOfMemory;
VorbisInfoEvent *mie = new VorbisInfoEvent(ov_bitrate(&m_vf, 0),
vi->channels,
vi->rate,
1. / (float)iFramesPerSecond);
if (mie)
{
pMIE->AddChildEvent((Event *) mie);
mie = NULL;
}
else
{
return kError_OutOfMemory;
}
if (m_pTarget)
m_pTarget->AcceptEvent(pMIE);
return kError_NoErr;
}
uint32 VorbisLMC::CalculateSongLength(const char *url)
{
char path[_MAX_PATH];
uint32 len = _MAX_PATH;
FILE *fpFile;
OggVorbis_File vf;
double dur;
URLToFilePath(url, path, &len);
fpFile = fopen(path, "rb");
if (fpFile == NULL)
return 0;
memset(&vf, 0, sizeof(vf));
if (ov_open(fpFile, &vf, NULL, 0) < 0)
{
fclose(fpFile);
return 0;
}
dur = ov_time_total(&vf, 0);
ov_clear(&vf);
return (int)dur;
}
void VorbisLMC::DecodeWorkerThreadFunc(void *pxlmc)
{
if (pxlmc)
{
VorbisLMC *xlmc = (VorbisLMC *) pxlmc;
xlmc->DecodeWork();
}
}
void VorbisLMC::DecodeWork()
{
void *pOutBuffer;
Error Err;
int32 iValue;
int32 section, ret;
OutputInfo *info;
vorbis_info *vi;
uint32 bytesCopied, bytesPerFrame;
int bitrateLoops = 0;
assert(m_pPmi);
assert(m_pPmo);
m_pSleepSem->Wait();
m_pPmi->Wake();
Err = CanDecode();
if (Err == kError_Interrupt)
return;
if (Err != kError_NoErr)
{
m_pContext->log->Error("CanDecode returned false.\n");
if (m_decodeInfo.sendInfo)
{
ReportStatus(szCannotDecode);
m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
}
else
((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
return;
}
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));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -