/* 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 #include #include #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*>; } 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 *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(m_inputStream); if (!stream) return; int length = stream->getByteLength(); int freeSpace = INPUT_BUFFER_SIZE - length; DataPacket *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;iwaitStreamState(_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;ifragmentSize(); 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:"<