diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /mpeglib_artsplug/decoderBaseObject_impl.cpp | |
download | tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'mpeglib_artsplug/decoderBaseObject_impl.cpp')
-rw-r--r-- | mpeglib_artsplug/decoderBaseObject_impl.cpp | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/mpeglib_artsplug/decoderBaseObject_impl.cpp b/mpeglib_artsplug/decoderBaseObject_impl.cpp new file mode 100644 index 00000000..bd69a901 --- /dev/null +++ b/mpeglib_artsplug/decoderBaseObject_impl.cpp @@ -0,0 +1,620 @@ +/* + 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; +} |