Implemented audio plugin interface

This commit is contained in:
Klaus Schmidinger 2002-11-03 11:53:58 +01:00
parent 2e26a1d607
commit 0a517afdf8
13 changed files with 263 additions and 44 deletions

21
HISTORY
View File

@ -1671,7 +1671,7 @@ Video Disk Recorder Revision History
buttons of those that have already been learned (thanks to Oliver Endriss for
reporting this one).
2002-11-02: Version 1.1.15
2002-11-03: Version 1.1.15
- Completely switched to the new CVS HEAD version of the linux-dvb driver.
The NEWSTRUCT compile time switch is now obsolete. The required driver is now
@ -1697,3 +1697,22 @@ Video Disk Recorder Revision History
your existing plugin directory - make a backup copy first!).
- Fixed reading EPG data via the SVDRP command PUTE (it changed the current
service ID).
- Fixed closing all dup'ed file descriptors in cPipe.
- Implemented a plugin interface for additional audio processing (see PLUGINS.html
under "Dolby Digital").
The functionality of the '-a' command line option has been reactivated.
Since the author doesn't have any Dolby Digital equipment this may or may not
work as expected. There may still be some places where a call to cAudios::Clear()
or cAudios::Mute() is necessary. Those with Dolby Digital equipment should please
take a look at this and maybe send patches.
Replaying Dolby Digital in ways other than through VDR's '-a' option will have to
be implemented as plugins. Those who have written patches for VDR version 1.0.x
should convert their work into the proper plugins for version 1.1.x.
Note to authors of cPlayer derived plugins: please read the modified comments
in device.h regarding the member functions cDevice::Clear(), cDevice::Mute() and
cDevice::PlayAudio(). Derived classes must call these base class member functions
to make sure all registered cAudio objects are properly handled. Also note that
the return type of cDevice::PlayAudio() has been changed to 'void', since this
function always has to accept the entire data block immediately and there is
nothing that could be reasonably done in case an error occurs in one of the
cAudio objects.

View File

@ -21,18 +21,18 @@ VDR program and present itself to the user.
The <i>inside</i> interface provides the plugin code access to VDR's internal data
structures and allows it to hook itself into specific areas to perform special actions.
<p>
<!--X1.1.11--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.11 are marked like this.
<!--X1.1.11--></td></tr></table>
<!--X1.1.12--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.12--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.12 are marked like this.
<!--X1.1.12--></td></tr></table>
<!--X1.1.13--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.13--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.13 are marked like this.
<!--X1.1.13--></td></tr></table>
<!--X1.1.14--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.14--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.14 are marked like this.
<!--X1.1.14--></td></tr></table>
<!--X1.1.15--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.15 are marked like this.
<!--X1.1.15--></td></tr></table>
<a name="Part I - The Outside Interface"><hr><center><h1>Part I - The Outside Interface</h1></center>
@ -964,7 +964,7 @@ bool DevicePoll(cPoller &amp;Poller, int TimeoutMs = 0);
</pre></td></tr></table><p>
to determine whether the device is ready for further data.
<!--X1.1.13--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.13--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<p>
If the player can provide more than a single audio track, it can implement the
following functions to make them available:
@ -977,7 +977,16 @@ virtual void SetAudioTrack(int Index);
<!--X1.1.13--></td></tr></table>
<p>
TODO: PlayAudio()???
<!--X1.1.15--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
If there is an additional audio track that has to be replayed with external hardware,
the player shall call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
void PlayAudio(const uchar *Data, int Length);
</pre></td></tr></table><p>
where <tt>Data</tt> points to a complete audio PES packet of <tt>Length</tt> bytes.
<!--X1.1.15--></td></tr></table>
<p>
The second part needed here is a control object that receives user input from the main
program loop and reacts on this by telling the player what to do:
@ -1196,7 +1205,7 @@ If the new device can receive, it most likely needs to provide a way of
selecting which channel it shall tune to:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
<!--X1.1.12--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.12--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
virtual bool ProvidesSource(int Source) const;
<!--X1.1.12--></td></tr></table>
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
@ -1206,7 +1215,7 @@ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
These functions will be called with the desired source or channel and shall return whether
this device can provide the requested source or channel and whether tuning to it was successful,
repectively.
<!--X1.1.13--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.13--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<p>
<b>Audio selection</b>
<p>
@ -1253,7 +1262,7 @@ The functions to implement replaying capabilites are
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool HasDecoder(void) const;
<!--X1.1.14--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.14--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
virtual bool CanReplay(void) const;
<!--X1.1.14--></td></tr></table>
virtual bool SetPlayMode(ePlayMode PlayMode);
@ -1315,7 +1324,51 @@ shut down (delete) all devices when the program terminates. It is therefore
important that the devices are created on the heap, using the <tt>new</tt>
operator!
<!--X1.1.11--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<!--X1.1.15--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<hr><h2>Dolby Digital</h2>
<center><i><b>"The stereo effect may only be experienced if stereo equipment is used!"</b></i></center><p>
There are many different ways to replay additional audio tracks, like Dolby Digital.
So VDR offers a plugin interface that allows for the implementation of any kind of
audio replay facility.
<p>
To implement a new audio output facility, simply derive a class from <tt>cAudio</tt>,
as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
#include &lt;vdr/audio.h&gt;
#include &lt;vdr/thread.h&gt;
class cMyAudio : public cAudio, private cThread {
private:
virtual void Action(void);
public:
cMyAudio(void);
virtual void Play(const uchar *Data, int Length);
virtual void Mute(bool On);
virtual void Clear(void);
};
</pre></td></tr></table><p>
You should create your derived audio object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).
<p>
The <tt>Play()</tt> function will be offered complete audio PES packets
and has to accept each packet immediately. It must return as soon as possible,
in order to not delay the overall replay process. Therefore you may want to
also derive your class from <tt>cThread</tt> and run the actual audio processing
as a separate thread. Note that the offered data is only valid within the call
to <tt>Play()</tt>, so if you can't process the entire block immediately, you
will need to copy it for later processing in your thread.
<p>
The <tt>Mute()</tt> and <tt>Clear()</tt> functions will be called whenever the audio shall
be muted, or any buffered data shall be cleared, respectively.
<!--X1.1.15--></td></tr></table>
<hr><h2>Remote Control</h2>
<center><i><b>The joy of zapping!</b></i></center><p>
@ -1427,7 +1480,6 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
</pre></td></tr></table><p>
The other parameters have the same meaning as in the first version of this function.
<!--X1.1.11--></td></tr></table>
</body>
</html>

89
audio.c
View File

@ -4,8 +4,95 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: audio.c 1.1 2002/06/10 16:30:00 kls Exp $
* $Id: audio.c 1.2 2002/11/03 11:53:34 kls Exp $
*/
#include "audio.h"
#include "stdlib.h"
// --- cAudio ----------------------------------------------------------------
cAudio::cAudio(void)
{
Audios.Add(this);
}
cAudio::~cAudio()
{
}
// --- cAudios ---------------------------------------------------------------
cAudios Audios;
void cAudios::PlayAudio(const uchar *Data, int Length)
{
for (cAudio *audio = First(); audio; audio = Next(audio))
audio->Play(Data, Length);
}
void cAudios::MuteAudio(bool On)
{
for (cAudio *audio = First(); audio; audio = Next(audio))
audio->Mute(On);
}
void cAudios::ClearAudio(void)
{
for (cAudio *audio = First(); audio; audio = Next(audio))
audio->Clear();
}
// --- cExternalAudio --------------------------------------------------------
cExternalAudio::cExternalAudio(const char *Command)
{
command = strdup(Command);
mute = false;
}
cExternalAudio::~cExternalAudio()
{
free(command);
}
void cExternalAudio::Play(const uchar *Data, int Length)
{
if (command && !mute) {
if (pipe || pipe.Open(command, "w")) {
if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) {
if (Data[3] == 0xBD) { // dolby
//XXX??? int written = Data[8] + (skipAC3bytes ? 13 : 9); // skips the PES header
int written = Data[8] + 9; // skips the PES header
Length -= written;
while (Length > 0) {
int w = fwrite(Data + written, 1, Length, pipe);
if (w < 0) {
LOG_ERROR;
break;
}
Length -= w;
written += w;
}
}
}
}
else {
esyslog("ERROR: can't open pipe to audio command '%s'", command);
free(command);
command = NULL;
}
}
}
void cExternalAudio::Mute(bool On)
{
mute = On;
if (mute)
Clear();
}
void cExternalAudio::Clear(void)
{
pipe.Close();
}

44
audio.h
View File

@ -4,10 +4,52 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: audio.h 1.1 2002/06/10 16:30:00 kls Exp $
* $Id: audio.h 1.2 2002/11/03 11:50:02 kls Exp $
*/
#ifndef __AUDIO_H
#define __AUDIO_H
#include "thread.h"
#include "tools.h"
class cAudio : public cListObject {
protected:
cAudio(void);
public:
virtual ~cAudio();
virtual void Play(const uchar *Data, int Length) = 0;
// Plays the given block of audio Data. Must return as soon as possible.
// If the entire block of data can't be processed immediately, it must
// be copied and processed in a separate thread. The Data is always a
// complete PES audio packet.
virtual void Mute(bool On) = 0;
// Immediately sets the audio device to be silent (On==true) or to
// normal replay (On==false).
virtual void Clear(void) = 0;
// Clears all data that might still be awaiting processing.
};
class cAudios : public cList<cAudio> {
public:
void PlayAudio(const uchar *Data, int Length);
void MuteAudio(bool On);
void ClearAudio(void);
};
extern cAudios Audios;
class cExternalAudio : public cAudio {
private:
char *command;
cPipe pipe;
bool mute;
public:
cExternalAudio(const char *Command);
virtual ~cExternalAudio();
virtual void Play(const uchar *Data, int Length);
virtual void Mute(bool On);
virtual void Clear(void);
};
#endif //__AUDIO_H

View File

@ -4,13 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 1.33 2002/11/01 11:08:57 kls Exp $
* $Id: device.c 1.34 2002/11/03 11:51:24 kls Exp $
*/
#include "device.h"
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "audio.h"
#include "channels.h"
#include "eit.h"
#include "i18n.h"
@ -416,6 +417,7 @@ bool cDevice::ToggleMute(void)
mute = !mute;
SetVolume(0, mute);
volume = OldVolume;
Audios.MuteAudio(mute);
return mute;
}
@ -424,8 +426,10 @@ void cDevice::SetVolume(int Volume, bool Absolute)
volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
SetVolumeDevice(volume);
cStatus::MsgSetVolume(volume, Absolute);
if (volume > 0)
if (volume > 0) {
mute = false;
Audios.MuteAudio(mute);
}
}
int cDevice::NumAudioTracks(void) const
@ -462,6 +466,7 @@ void cDevice::TrickSpeed(int Speed)
void cDevice::Clear(void)
{
Audios.ClearAudio();
}
void cDevice::Play(void)
@ -474,6 +479,7 @@ void cDevice::Freeze(void)
void cDevice::Mute(void)
{
Audios.MuteAudio(true);
}
void cDevice::StillPicture(const uchar *Data, int Length)
@ -529,9 +535,9 @@ int cDevice::PlayVideo(const uchar *Data, int Length)
return -1;
}
int cDevice::PlayAudio(const uchar *Data, int Length)
void cDevice::PlayAudio(const uchar *Data, int Length)
{
return -1;
Audios.PlayAudio(Data, Length);
}
int cDevice::Ca(void) const

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.26 2002/11/01 11:03:56 kls Exp $
* $Id: device.h 1.27 2002/11/03 11:16:11 kls Exp $
*/
#ifndef __DEVICE_H
@ -290,6 +290,8 @@ public:
// times.
virtual void Clear(void);
// Clears all video and audio data from the device.
// A derived class must call the base class function to make sure
// all registered cAudio objects are notified.
virtual void Play(void);
// Sets the device into play mode (after a previous trick
// mode).
@ -297,6 +299,8 @@ public:
// Puts the device into "freeze frame" mode.
virtual void Mute(void);
// Turns off audio while replaying.
// A derived class must call the base class function to make sure
// all registered cAudio objects are notified.
virtual void StillPicture(const uchar *Data, int Length);
// Displays the given I-frame as a still picture.
virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
@ -309,8 +313,10 @@ public:
// Actually plays the given data block as video. The data must be
// part of a PES (Packetized Elementary Stream) which can contain
// one video and one audio strem.
virtual int PlayAudio(const uchar *Data, int Length);
virtual void PlayAudio(const uchar *Data, int Length);
// Plays additional audio streams, like Dolby Digital.
// A derived class must call the base class function to make sure data
// is distributed to all registered cAudio objects.
bool Replaying(void) const;
// Returns true if we are currently replaying.
void StopReplay(void);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.32 2002/11/01 11:24:47 kls Exp $
* $Id: dvbdevice.c 1.33 2002/11/03 11:51:29 kls Exp $
*/
#include "dvbdevice.h"
@ -792,10 +792,10 @@ int cDvbDevice::PlayVideo(const uchar *Data, int Length)
return -1;
}
int cDvbDevice::PlayAudio(const uchar *Data, int Length)
void cDvbDevice::PlayAudio(const uchar *Data, int Length)
{
//XXX+
return -1;
//XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
cDevice::PlayAudio(Data, Length);
}
bool cDvbDevice::OpenDvr(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.16 2002/11/01 10:05:41 kls Exp $
* $Id: dvbdevice.h 1.17 2002/11/02 14:32:25 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -103,7 +103,7 @@ public:
virtual void StillPicture(const uchar *Data, int Length);
virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
virtual int PlayVideo(const uchar *Data, int Length);
virtual int PlayAudio(const uchar *Data, int Length);
virtual void PlayAudio(const uchar *Data, int Length);
// Receiver facilities

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbplayer.c 1.15 2002/10/13 16:08:40 kls Exp $
* $Id: dvbplayer.c 1.16 2002/11/03 11:23:47 kls Exp $
*/
#include "dvbplayer.h"
@ -219,7 +219,7 @@ void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
switch (c) {
case 0xBD: // dolby
if (Except)
;//XXX+ PlayExternalDolby(&b[i], Length - i);
PlayAudio(&b[i], l);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: player.c 1.5 2002/08/15 10:29:17 kls Exp $
* $Id: player.c 1.6 2002/11/02 14:55:37 kls Exp $
*/
#include "player.h"
@ -31,12 +31,13 @@ int cPlayer::PlayVideo(const uchar *Data, int Length)
return -1;
}
int cPlayer::PlayAudio(const uchar *Data, int Length)
void cPlayer::PlayAudio(const uchar *Data, int Length)
{
if (device)
return device->PlayAudio(Data, Length);
if (device) {
device->PlayAudio(Data, Length);
return;
}
esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!");
return -1;
}
void cPlayer::Detach(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: player.h 1.9 2002/10/12 11:17:02 kls Exp $
* $Id: player.h 1.10 2002/11/03 11:27:30 kls Exp $
*/
#ifndef __PLAYER_H
@ -35,8 +35,8 @@ protected:
// Sends the given Data to the video device and returns the number of
// bytes that have actually been accepted by the video device (or a
// negative value in case of an error).
int PlayAudio(const uchar *Data, int Length);
// XXX+ TODO
void PlayAudio(const uchar *Data, int Length);
// Plays additional audio streams, like Dolby Digital.
public:
cPlayer(ePlayMode PlayMode = pmAudioVideo);
virtual ~cPlayer();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: transfer.c 1.6 2002/10/26 10:17:17 kls Exp $
* $Id: transfer.c 1.7 2002/11/03 11:24:02 kls Exp $
*/
#include "transfer.h"
@ -123,7 +123,7 @@ void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
switch (c) {
case 0xBD: // dolby
if (Except)
;//XXX+ PlayExternalDolby(&b[i], Length - i);
PlayAudio(&b[i], l);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)

14
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.130 2002/10/27 15:20:56 kls Exp $
* $Id: vdr.c 1.131 2002/11/03 11:44:17 kls Exp $
*/
#include <getopt.h>
@ -30,6 +30,7 @@
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include "audio.h"
#include "channels.h"
#include "config.h"
#include "cutter.h"
@ -87,6 +88,7 @@ int main(int argc, char *argv[])
#define DEFAULTPLUGINDIR "./PLUGINS/lib"
int SVDRPport = DEFAULTSVDRPPORT;
const char *AudioCommand = NULL;
const char *ConfigDirectory = NULL;
bool DisplayHelp = false;
bool DisplayVersion = false;
@ -121,10 +123,8 @@ int main(int argc, char *argv[])
int c;
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
switch (c) {
/*XXX+
case 'a': cDevice::SetAudioCommand(optarg);
case 'a': AudioCommand = optarg;
break;
XXX*/
case 'c': ConfigDirectory = optarg;
break;
case 'd': DaemonMode = true; break;
@ -365,6 +365,11 @@ int main(int argc, char *argv[])
#endif
Interface->LearnKeys();
// External audio:
if (AudioCommand)
new cExternalAudio(AudioCommand);
// Channel:
Channels.SwitchTo(Setup.CurrentChannel);
@ -676,6 +681,7 @@ int main(int argc, char *argv[])
delete Interface;
cOsd::Shutdown();
Remotes.Clear();
Audios.Clear();
Setup.CurrentChannel = cDevice::CurrentChannel();
Setup.CurrentVolume = cDevice::CurrentVolume();
Setup.Save();