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.
tdemultimedia/mpeglib_artsplug/decoderBaseObject_impl.cpp

621 lines
15 KiB

/*
base class for all mpeglib decoders
Copyright (C) 2000 Martin Vogt
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation.
For more information look at the file COPYRIGHT in this package
*/
#include <queue>
#include <iostream>
#include <connect.h>
#include "decoderBaseObject_impl.h"
#include "../mpeglib/lib/decoder/decoderPlugin.h"
#include "debug.h"
// define this to run the playobject without the
// arts backend. (useful to check if a bug is in arts or mpeglib)
//#define _STRIP_ZERO
static int instanceCnt=0;
DecoderBaseObject_impl::DecoderBaseObject_impl()
: _speed(1.0f)
{
flpos=0.0;
_blocking = false;
#ifdef _STRIP_ZERO
outputStream=NULL;
#else
m_outputStream=new ArtsOutputStream(NULL);
arts_debug("outputStream created");
decoderPlugin=NULL;
#endif
startTime=0.0;
m_inputStream=NULL;
setStreamState(_THREADSTATE_INIT);
_state=Arts::posIdle;
instance=instanceCnt;
instanceCnt++;
m_packetQueue = new std::queue<DataPacket<mcopbyte>*>;
}
DecoderBaseObject_impl::~DecoderBaseObject_impl() {
arts_debug("~DecoderBaseObject_impl -s");
shudownPlugins();
if (decoderPlugin != NULL) {
arts_debug("delete decoderPlugin");
delete decoderPlugin;
decoderPlugin=NULL;
}
if (m_outputStream != NULL) {
arts_debug("delete outputStream");
delete m_outputStream;
m_outputStream=NULL;
}
if (m_streaming)
m_artsInputStream.streamEnd();
delete m_packetQueue;
}
DecoderPlugin* DecoderBaseObject_impl::createPlugin() {
arts_fatal("direct virtual call DecoderBaseObject_impl::getPlugin");
return NULL;
}
InputStream* DecoderBaseObject_impl::createInputStream(const char* url) {
InputStream* back = InputPlugin::createInputStream(url,true);
return back;
}
bool DecoderBaseObject_impl::loadMedia(const string &filename) {
arts_debug("loadMedia");
int back=true;
m_streaming = false;
if ( m_inputStream != NULL ) {
arts_fatal("remove resources first with a call to: halt()");
}
if (decoderPlugin == NULL) {
decoderPlugin=createPlugin();
if(doFloat()) decoderPlugin->config("dofloat",0,0);
}
flpos=0.0;
startTime=0.0;
lastAudioBufferSize=-1;
/**
Note: you can only play one file with a PlayObject !!
Then you must destroy it.
A StreamEnd call should do the job.
*/
#ifdef _STRIP_ZERO
return true;
#endif
m_inputStream=createInputStream(filename.c_str());
// the plugin does not open the stream!
// we do it.
back=m_inputStream->open((char*)filename.c_str());
setStreamState(_THREADSTATE_OPENED);
// we are still in posIdle here
m_outputStream->audioOpen();
// watch the order!
decoderPlugin->setOutputPlugin(m_outputStream);
decoderPlugin->setInputPlugin(m_inputStream);
return back;
}
#define INPUT_BUFFER_SIZE 32768
bool DecoderBaseObject_impl::streamMedia(Arts::InputStream instream) {
arts_debug("DecoderBaseObject_impl::streamMedia -s");
bool back = true;
if (m_inputStream != NULL) {
arts_fatal("resource in use, call halt() first");
}
if (decoderPlugin == NULL) {
decoderPlugin = createPlugin();
if (doFloat())
decoderPlugin->config("dofloat", 0, 0);
// streaming, don't know the length
decoderPlugin->config("-c", 0, 0);
}
flpos = 0.0;
startTime = 0.0;
m_streaming = true;
lastAudioBufferSize = -1;
m_artsInputStream = instream;
m_inputStream = new BufferInputStream(INPUT_BUFFER_SIZE, 4096, (char*)"InputStream");
m_inputStream->open((char*)"InputStream");
// connect the stream now
Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy());
connect(m_artsInputStream, "outdata", self);
setStreamState(_THREADSTATE_OPENED);
m_outputStream->audioOpen();
decoderPlugin->setOutputPlugin(m_outputStream);
decoderPlugin->setInputPlugin(m_inputStream);
arts_debug("DecoderBaseObject_impl::streamMedia -e");
return back;
}
void DecoderBaseObject_impl::process_indata(DataPacket<mcopbyte> *inpacket) {
m_packetQueue->push(inpacket);
processQueue();
}
void DecoderBaseObject_impl::processQueue() {
// early exit if no packets in the queue
if (m_packetQueue->empty())
return;
// see how much space we have in the stream
BufferInputStream* stream = static_cast<BufferInputStream*>(m_inputStream);
if (!stream) return;
int length = stream->getByteLength();
int freeSpace = INPUT_BUFFER_SIZE - length;
DataPacket<mcopbyte> *inpacket = m_packetQueue->front();
if (!inpacket) return;
if (freeSpace >= inpacket->size) {
stream->write((char*)inpacket->contents, inpacket->size, 0);
m_packetQueue->pop();
inpacket->processed();
}
}
string DecoderBaseObject_impl::description() {
arts_debug("description");
string back;
#ifdef _STRIP_ZERO
return back;
#endif
PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
pluginInfo->print();
return back;
}
void DecoderBaseObject_impl::description(const string &) {
arts_debug("description");
// what should do this?
}
poTime DecoderBaseObject_impl::currentTime() {
poTime time;
#ifdef _STRIP_ZERO
return time;
#endif
AudioTime* audioTime=m_outputStream->getAudioTime();
float currentTime=audioTime->getTime()+(float)startTime;
time.seconds=(long)(currentTime);
time.ms=(long) (1000.0*(currentTime-(float)time.seconds));
return time;
}
poTime DecoderBaseObject_impl::overallTime() {
poTime time;
#ifdef _STRIP_ZERO
return time;
#endif
PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
time.seconds=pluginInfo->getLength();
time.ms=0;
return time;
}
poCapabilities DecoderBaseObject_impl::capabilities() {
arts_debug("capabilities");
#ifdef _STRIP_ZERO
return capSeek;
#endif
PluginInfo* pluginInfo=decoderPlugin->getPluginInfo();
long len=pluginInfo->getLength();
if (len == 0) {
return Arts::capPause; /* no seek supported */
}
// seek and pause supported
return (poCapabilities)(Arts::capSeek | Arts::capPause);
}
string DecoderBaseObject_impl::mediaName() {
arts_debug("mediaName");
string back;
// whats a mediaName?
return back;
}
poState DecoderBaseObject_impl::state() {
return _state;
}
void DecoderBaseObject_impl::play() {
arts_debug("play: %d", (int)streamState);
if (streamState == _THREADSTATE_OPENED) {
decoderPlugin->play();
} else {
Command cmd(_COMMAND_PLAY);
decoderPlugin->insertAsyncCommand(&cmd);
}
setStreamState(_THREADSTATE_PLAYING);
_state = Arts::posPlaying;
}
void DecoderBaseObject_impl::seek(const class poTime& seekTime) {
#ifdef _STRIP_ZERO
return;
#endif
long sec=seekTime.seconds;
arts_debug("sec in plugin is %d:", sec);
// we send an async command
Command cmd(_COMMAND_SEEK,sec);
decoderPlugin->insertAsyncCommand(&cmd);
// if the thread blocks on the artsOutputstream: kick him out
// the next command will the the seek command
m_outputStream->audioClose();
// thread blocking allowed
m_outputStream->audioOpen();
arts_debug("************ reopen");
// now set a new startTime
startTime=sec;
}
void DecoderBaseObject_impl::pause() {
arts_debug("pause");
_state = Arts::posPaused;
Command cmd(_COMMAND_PAUSE);
decoderPlugin->insertAsyncCommand(&cmd);
}
void DecoderBaseObject_impl::halt() {
/*
*
* halt() (which the normal programmer would probably refer to as stop())
* should seek to the beginning and go into the posIdle state, like a just
* opened PlayObject
*
*/
arts_debug("halt");
_state=Arts::posIdle;
shudownPlugins();
}
void DecoderBaseObject_impl::streamInit() {
#ifdef _STRIP_ZERO
return;
#endif
}
void DecoderBaseObject_impl::streamStart() {
arts_debug("DecoderBaseObject_impl::streamStart");
}
int DecoderBaseObject_impl::fillArts(unsigned long samples,
float* left , float* right) {
unsigned long haveSamples = 0;
AudioTime* audioTime=m_outputStream->getAudioTime();
int wav_samplingRate=audioTime->getSpeed();
int wav_sampleWidth=audioTime->getSampleSize();
int wav_channelCount=audioTime->getStereo()+1;
if(doFloat()) wav_sampleWidth = sizeof(float)*8;
// here seems to be an error, I have clicks sometimes in the stream
//int byteMultiplikator=(wav_sampleWidth/8)*wav_channelCount;
// maybe first multiply, then divide?
int byteMultiplikator = wav_channelCount * wav_sampleWidth / 8;
char* buffer;
int hasBytes = 0;
int wantBytes = 0;
int bufferSize=getBufferSize();
if (bufferSize != lastAudioBufferSize) {
lastAudioBufferSize=bufferSize;
m_outputStream->setAudioBufferSize(bufferSize);
}
/* difference between the sampling rates in percent */
float diff = fabs((double)wav_samplingRate - (double)(samplingRateFloat/_speed))
/ (double)samplingRateFloat;
/*
* efficient optimized case:
* 1. decoder -> float rendering
* 2. no resampling (i.e. artsd running @ 44100 Hz, playing an 44100 Hz mp3)
*/
if(_state == Arts::posPlaying && doFloat() && diff < 0.0005) {
wantBytes = sizeof(float) * wav_channelCount * samples;
hasBytes = m_outputStream->read(&buffer,wantBytes);
float *flptr = (float *)buffer;
if(wav_channelCount == 1)
{
while((int)(haveSamples * sizeof(float)) < hasBytes)
{
left[haveSamples] = right[haveSamples] = flptr[haveSamples];
haveSamples++;
}
}
else if(wav_channelCount == 2)
{
while((int)(haveSamples * 2 * sizeof(float)) < hasBytes)
{
left[haveSamples] = flptr[haveSamples*2];
right[haveSamples] = flptr[haveSamples*2+1];
haveSamples++;
}
}
m_outputStream->forwardReadPtr(haveSamples*sizeof(float)*wav_channelCount);
}
else if(_state == Arts::posPlaying) {
//
// since the samplingrate of the MP3 and the samplingrate of the output
// device (soundcard) are not necessarily the same, it's a bit tricky
//
// calculate "how fast" we consume input samples (2.0 means, we need 2
// input samples to generate one output sample)
double speed = (double)wav_samplingRate / (double)(samplingRateFloat/_speed);
// calculate how many input samples we need, then to satisfy the request
// use a larger amount than "really" required, to ensure that samples are
// available for rounding errors and interpolation
double wantWavSamples = (double)samples*speed+8.0;
// convert that into bytes and try to read that many bytes
wantBytes=(int) (wantWavSamples*byteMultiplikator);
hasBytes=m_outputStream->read(&buffer,wantBytes);
int format = doFloat()?Arts::uni_convert_float_ne:wav_sampleWidth;
// convert those bytes into the suitable output form
haveSamples = Arts::uni_convert_stereo_2float(samples, (unsigned char *)buffer,
hasBytes, wav_channelCount,
format,
left,right,speed,flpos);
// calculate where we are now (as floating point position) in our
// inputsample buffer
flpos += (double)haveSamples * speed;
// Good - so how many input samples we won't need anymore (for the
// next request)? Skip them.
int skip = (int)floor(flpos);
// we need to call this even on skip == 0
// because we must unlock the remoteBuffer
int forward=skip*byteMultiplikator;
flpos = flpos - floor(flpos);
m_outputStream->forwardReadPtr(forward);
}
if(haveSamples != samples) {
unsigned long i;
for(i=haveSamples;i<samples;i++)
left[i] = right[i] = 0.0;
}
return samples;
}
void DecoderBaseObject_impl::calculateBlock(unsigned long samples,
float* left , float* right) {
#ifndef _STRIP_ZERO
int audioState=m_outputStream->waitStreamState(_OUTPUT_WAIT_METHOD_POLL,
_STREAM_MASK_ALL,
_STREAMTYPE_AUDIO);
if (audioState & _STREAM_MASK_IS_INIT) {
// now check if we already have enough data
int lenough=false;
if (audioState & _STREAM_MASK_IS_EOF) {
if(_state == Arts::posPlaying) {
arts_debug("eof got in arts********** END");
_state = Arts::posIdle;
}
lenough=true;
}
if (m_outputStream->getBufferFillgrade() >= 4096) {
lenough=true;
}
if (_state == Arts::posPlaying) {
if (m_streaming) {
// produce more data
processQueue();
// check for stream end
if ( m_inputStream->getByteLength() == 0 ) {
if ( m_artsInputStream.eof() ) {
m_inputStream->close();
m_artsInputStream.streamEnd();
}
}
}
if (lenough || _blocking) {
fillArts(samples, left, right);
return;
}
}
}
#endif
// filling with zero , stream not ready(yet)
unsigned int i;
for(i=0;i<samples;i++)
left[i] = right[i] = 0.0;
}
void DecoderBaseObject_impl::streamEnd() {
arts_debug("streamEnd");
#ifdef _STRIP_ZERO
return;
#endif
halt();
}
int DecoderBaseObject_impl::getBufferSize() {
float hardwareBuffer;
float fragmentSize;
float fragmentCount;
float channels;
float sampleSize;
fragmentSize=Arts::AudioSubSystem::the()->fragmentSize();
fragmentCount=Arts::AudioSubSystem::the()->fragmentCount();
channels=Arts::AudioSubSystem::the()->channels();
sampleSize=16.0/8.0;
hardwareBuffer=fragmentSize*fragmentCount;
return (int)hardwareBuffer;
}
void DecoderBaseObject_impl::shudownPlugins() {
arts_debug("shudownPlugins -s");
/**
The order here is important.
First we close the audio so that the thread never blocks
on the ringbuffer.
Then we are sure thst we can safley call plugin->close,
because the thread does not block.
We then have the thread back in the decoder_loop of
the plugin.
*/
// this should theoretically be faster
if (decoderPlugin != NULL) {
Command cmd(_COMMAND_CLOSE);
decoderPlugin->insertAsyncCommand(&cmd);
}
if (m_outputStream != NULL) {
m_outputStream->audioClose();
}
// very likely the thread already is closed
// because of the asyncCommand above.
if (decoderPlugin) {
decoderPlugin->close();
}
delete m_inputStream;
m_inputStream=NULL;
if (m_streaming)
m_artsInputStream.streamEnd();
setStreamState(_THREADSTATE_CLOSED);
arts_debug("shudownPlugins -e");
}
void DecoderBaseObject_impl::setStreamState(int state) {
switch (state) {
case _THREADSTATE_INIT: {
streamState=_THREADSTATE_INIT;
break;
}
case _THREADSTATE_OPENED: {
streamState=_THREADSTATE_OPENED;
break;
}
case _THREADSTATE_PLAYING: {
streamState=_THREADSTATE_PLAYING;
break;
}
case _THREADSTATE_CLOSED: {
streamState=_THREADSTATE_INIT;
break;
}
default:
std::cerr << "unknown streamState:DecoderBaseObject_impl:"<<state<<std::endl;
}
}
/** internal stuff for testing **/
void DecoderBaseObject_impl::blocking(bool newvalue)
{
_blocking = newvalue;
}
bool DecoderBaseObject_impl::blocking()
{
return _blocking;
}
void DecoderBaseObject_impl::speed(float newValue)
{
_speed= newValue;
}
float DecoderBaseObject_impl::speed()
{
return _speed;
}