📄 audiohandler.cc
字号:
// Copyright (c) 2005 by Istv醤 V醨adi// This file is part of dxr3Player, a DVD player written specifically // for the DXR3 (aka Hollywood+) decoder card.// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA//------------------------------------------------------------------------------#include "AudioHandler.h"#include "AudioDevice.h"#include "Timer.h"#include "dvd/packet/PacketQueue.h"#include "dvd/packet/DataBlockTypes.h"#include "sched/Scheduler.h"#include "util/Config.h"//------------------------------------------------------------------------------using output::AudioHandler;using dvd::packet::PacketQueue;using dvd::packet::TimedAudioPacket;using dvd::packet::DataBlockTypes;using sched::Scheduler;//------------------------------------------------------------------------------const unsigned char AudioHandler::gapBuffer[4096] = { 0 };//------------------------------------------------------------------------------inline audioMode_t AudioHandler::getAudioMode(audioEncoding_t encoding){ switch(encoding) { case AC3: case DTS: return AM_DIGITAL_AC3; default: return AM_ANALOGUE; }}//------------------------------------------------------------------------------inline size_t AudioHandler::getSampleSize() const{ return audioDevice.getSampleSize();}//------------------------------------------------------------------------------inline size_t AudioHandler::samples2Bytes(size_t numSamples) const{ return numSamples * getSampleSize();}//------------------------------------------------------------------------------inline size_t AudioHandler::pts2Samples(pts_t diff) const{ diff *= audioDevice.getSampleRate(); diff /= Util::ptsFrequency; return static_cast<size_t>(diff);}//------------------------------------------------------------------------------inline size_t AudioHandler::millis2Samples(millis_t millis) const{ millis *= audioDevice.getSampleRate(); millis /= 1000; return static_cast<size_t>(millis);}//------------------------------------------------------------------------------inline ptsdiff_t AudioHandler::getPTSWritten() const{ ptsdiff_t diff = samplesWritten; diff *= Util::ptsFrequency; diff /= audioDevice.getSampleRate(); return diff;}//------------------------------------------------------------------------------//------------------------------------------------------------------------------AudioHandler::AudioHandler(AudioDevice& audioDevice, Timer& timer, PacketQueue& inputQueue) : Schedulable("output::AudioHandler"), timer(timer), inputQueue(inputQueue), audioDevice(audioDevice), audioStopped("output::AudioHandler", "audioStopped"), startPTS(INVALID_PTS), samplesWritten(0), endPTS(INVALID_PTS), updateTimerPTS(INVALID_PTS), toFlush(false){ audioDevice.setup(Config::get().audioMode, 48000, true);}//------------------------------------------------------------------------------void AudioHandler::run(){ while(!shouldQuit()) { clearInterrupt(); toFlush = false; prebuffer(); play(); finish(); } samplesWritten = 0; audioDevice.stopPlayback(); audioStopped.set(); audioDevice.stop();}//------------------------------------------------------------------------------void AudioHandler::reset(){ currentPacket = 0; interrupt();}//------------------------------------------------------------------------------void AudioHandler::flush(){ size_t minPlayableSamples = audioDevice.isTriggerable() ? millis2Samples(prebufferMillis) : 1; if (samplesWritten<minPlayableSamples) { toFlush = true; interrupt(); }}//------------------------------------------------------------------------------void AudioHandler::waitStop(){ while(samplesWritten>0) { flush(); Scheduler::wait(audioStopped); }}//------------------------------------------------------------------------------void AudioHandler::setFormat(const AudioFormat& audioFormat){ Log::debug("output::AudioHandler::setFormat: encoding=%d, sampleRate=%u, # of channels=%u\n", (int)audioFormat.encoding, audioFormat.sampleRate, audioFormat.numberOfChannels); audioDevice.setup(getAudioMode(audioFormat.encoding), audioFormat.sampleRate, audioFormat.numberOfChannels>=2);}//------------------------------------------------------------------------------bool AudioHandler::isDifferent(const AudioFormat& audioFormat) const{ if (audioFormat.sampleRate!=audioDevice.getSampleRate()) return true; bool stereoNeeded = audioFormat.numberOfChannels >= 2 || audioFormat.encoding==AC3; if (stereoNeeded!=audioDevice.isStereo()) return true; if (getAudioMode(audioFormat.encoding)!=audioDevice.getAudioMode()) return true; return false;}//------------------------------------------------------------------------------void AudioHandler::prebuffer(){ audioDevice.stopPlayback(); Log::debug("output::AudioHandler::prebuffer\n"); startPTS = endPTS = updateTimerPTS = INVALID_PTS; samplesWritten = 0; audioStopped.set(); do { if (!currentPacket.isValid()) { currentPacket = TimedAudioPacket::convert(inputQueue.get()); } if (currentPacket.isValid()) { assert(currentPacket->getType()==DataBlockTypes::audio); const AudioFormat& format = currentPacket->getFormat(); if (isDifferent(format)) { if (samplesWritten==0) { setFormat(format); } else { Log::debug("output::dxr3::AudioHandler::prebuffer: audio format changed, flushing.\n"); toFlush = true; interrupt(); break; } } setupStartPTS(currentPacket); if (audioDevice.isTriggerable()) { writeCurrentPacket(); } } } while(!isInterrupted() && samplesWritten<millis2Samples(prebufferMillis) && audioDevice.isTriggerable()); if (audioDevice.isTriggerable() || currentPacket.isValid()) { startPlayback(); }}//------------------------------------------------------------------------------void AudioHandler::setupStartPTS(Reference<TimedAudioPacket> packet){ if (startPTS!=INVALID_PTS) return; pts_t dataPTS = packet->getPTS(); if (dataPTS==INVALID_PTS) return; startPTS = dataPTS; if (samplesWritten==0) return; ptsdiff_t ptsDiff = getPTSWritten(); if (ptsDiff<static_cast<ptsdiff_t>(startPTS)) startPTS -= ptsDiff; else startPTS = 0;}//------------------------------------------------------------------------------void AudioHandler::startPlayback(){ if (startPTS==INVALID_PTS || toFlush) { startPTS = timer.getCurrentSCR(); } else while(!isInterrupted()) { pts_t currentSCR = timer.getCurrentSCR(); pts_t compensatedStartPTS = startPTS + timer.getPTSCompensation(); if (currentSCR < compensatedStartPTS) { timer.sleepInterruptible(compensatedStartPTS); } else { ptsdiff_t diff = currentSCR - compensatedStartPTS; timer.advancePTSCompensation(diff); startPTS += timer.getPTSCompensation(); break; } } if ( (!isInterrupted() || toFlush) && (samplesWritten>0 || !audioDevice.isTriggerable()) ) { updateTimerPTS = startPTS + timerUpdateInterval; updateEndPTS(); if (audioDevice.isTriggerable()) { audioDevice.startPlayback(); } else { writeCurrentPacket(); } Log::debug("output::dxr3::AudioHandler::startPlayback: audio of PTS %llu (compensated with %lld) at %llu\n", startPTS, timer.getPTSCompensation(), timer.getCurrentSCR()); } else { samplesWritten = 0; }}//------------------------------------------------------------------------------void AudioHandler::play(){ while(!isInterrupted()) { if (!currentPacket.isValid()) { pts_t timeoutPTS = endPTS; if (!audioDevice.isRealTime()) timeoutPTS -= ptsTolerance; millis_t timeout = timer.getMillis(timeoutPTS); currentPacket = TimedAudioPacket::convert(inputQueue.get(timeout)); } if (currentPacket.isValid()) { assert(currentPacket->getType()==DataBlockTypes::audio); if (isDifferent(currentPacket->getFormat())) { Log::debug("output::AudioHandler::play: audio format changed during play\n"); break; } if (audioDevice.isRealTime()) { pts_t ptsBuffered = audioDevice.getPTSBuffered(); if ( ptsBuffered < millis2PTS(minPlayabaleMillis) ) { Log::debug("output::AudioHandler::play: " "too little (%llu) remained to be played, packet is probably late\n", ptsBuffered); break; } } size_t gapSize = calculateGapSize(currentPacket); if (gapSize>samples2Bytes(millis2Samples(minGapMillis))) { Log::debug("output::AudioHandler::play: PTS increased, adding gap of size %u\n", gapSize); if (audioDevice.getAudioMode()==AM_DIGITAL_AC3) { break; } else { writeGap(gapSize); } } if (!isInterrupted()) { writeCurrentPacket(); bool updateTimer = updateEndPTS(); if (audioDevice.isRealTime() && updateTimer) { pts_t ptsPlayed = audioDevice.getPTSPlayed(); timer.adjustFrequency(startPTS + ptsPlayed); updateTimerPTS += timerUpdateInterval; } } } else if (!isInterrupted()) { Log::debug("output::AudioHandler::play: timeout while waiting for packet\n"); break; } }}//------------------------------------------------------------------------------void AudioHandler::finish(){}//------------------------------------------------------------------------------size_t AudioHandler::calculateGapSize(Reference<TimedAudioPacket> packet){ pts_t dataPTS = packet->getPTS(); if (dataPTS==INVALID_PTS) { return 0; } dataPTS += timer.getPTSCompensation(); if (dataPTS<endPTS) { ptsdiff_t diff = endPTS - dataPTS; Log::debug("output::AudioHandler::calculateGapSize: PTS decrased by %lld, compensating\n", diff); timer.advancePTSCompensation(diff); dataPTS += diff; return 0; } else if (dataPTS>endPTS) { return samples2Bytes(pts2Samples(dataPTS - endPTS)); } else { return 0; }}//------------------------------------------------------------------------------bool AudioHandler::updateEndPTS(){ endPTS = startPTS + getPTSWritten(); return endPTS>=updateTimerPTS;}//------------------------------------------------------------------------------void AudioHandler::write(const unsigned char* buffer, size_t length, bool interruptible){ size_t written = audioDevice.play(buffer, length, interruptible); samplesWritten += written / getSampleSize();}//------------------------------------------------------------------------------void AudioHandler::writeGap(size_t gapSize, bool interruptible){ while((!isInterrupted() || !interruptible) && gapSize>0) { size_t toWrite = sizeof(gapBuffer); if (toWrite>gapSize) toWrite = gapSize; write(gapBuffer, toWrite, interruptible); gapSize -= toWrite; }}//------------------------------------------------------------------------------void AudioHandler::writeCurrentPacket(){ assert(currentPacket.isValid()); write(currentPacket->getData(), currentPacket->getLength()); currentPacket = 0;}//------------------------------------------------------------------------------// Local Variables:// mode: C++// c-basic-offset: 4// indent-tabs-mode: nil// End:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -