Added support for AC3 replay over the DVB device

This commit is contained in:
Klaus Schmidinger 2004-12-17 14:55:49 +01:00
parent 69ecb6a4d8
commit c77989ee70
18 changed files with 517 additions and 364 deletions

View File

@ -263,6 +263,8 @@ Werner Fink <werner@suse.de>
for suggesting to add more checks and polling when getting frontend events
for setting the VPID before the APID in live mode to avoid unnecessary
overhead in the firmware
for a patch that was used as a base for implementing a modified PES packet
handling in order to play AC3 audio over full featured DVB cards
Rolf Hakenes <hakenes@hippomi.de>
for providing 'libdtv' and adapting the EIT mechanisms to it

24
HISTORY
View File

@ -3160,7 +3160,7 @@ Video Disk Recorder Revision History
right day of week for timers in the future.
- Some improvements to cPoller (thanks to Marco Schlüßler).
2004-11-27: Version 1.3.18
2004-12-17: Version 1.3.18
- Removed an unused variable from cTimer::GetWDayFromMDay() (thanks to Wayne Keer
for reporting this one).
@ -3171,3 +3171,25 @@ Video Disk Recorder Revision History
picture mode (thanks to Reinhard Nissl for reporting this one).
- Fixed a possible race condition in generating the DVB device names (thanks to
Rainer Zocholl for reporting this one).
- Changed the way PES packets are played to allow replay of AC3 sound over the
full featured DVB cards (partially based on a patch from Werner Fink).
+ The new function cDevice::PlayPes() is now called with the complete PES data
stream and calls PlayVideo() and PlayAudio() as necessary.
+ cDevice::PlayVideo() is now only called with actual video PES packets.
+ cDevice::PlayAudio() is now called with the actual audio PES packets, which
can be either "normal" audio or AC3 data. You need at least firmware version
0x261d to replay AC3 sound over a full featured DVB card. This function now
has an 'int' return value.
+ PlayAudio() of derived cDevice classes shall no longer call the base class
function. It shall just play the given data as audio.
+ cPlayer::PlayVideo() and cPlayer::PlayAudio() are now obsolete and have been
replaced with cPlayer::PlayPes().
+ All StripAudioPackets() functions are now obsolete. The functionality has been
moved into cDevice::PlayPes(), where only the video and audio packets that are
actually required will be processed.
+ All audio track handling is now done by cDevice; cTransfer and cDvbPlayer no
longer care about audio tracks. cPlayer, however, still has the virtual hooks
for audio track handling in order to allow plugins to implement players that
have their own idea about this.
+ cChannel::[AD]pid[12]() have been replaced with cChannel::[AD]pid(int i) to
allow access to all available PIDs.

View File

@ -14,18 +14,18 @@ Copyright &copy; 2004 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
<!--X1.2.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.2.6 are marked like this.
<!--X1.2.6--></td></tr></table>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.0 are marked like this.
<!--X1.3.0--></td></tr></table>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.7 are marked like this.
<!--X1.3.7--></td></tr></table>
<!--X1.3.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.8 are marked like this.
<!--X1.3.8--></td></tr></table>
<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.18 are marked like this.
<!--X1.3.18--></td></tr></table>
<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
@ -73,11 +73,11 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Status monitor">Status monitor</a>
<li><a href="#Players">Players</a>
<li><a href="#Receivers">Receivers</a>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<li><a href="#Filters">Filters</a>
<!--X1.3.0--></td></tr></table>
<li><a href="#The On Screen Display">The On Screen Display</a>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<li><a href="#Skins">Skins</a>
<li><a href="#Themes">Themes</a>
<!--X1.3.7--></td></tr></table>
@ -1023,17 +1023,21 @@ public:
Take a look at the files <tt>player.h</tt> and <tt>dvbplayer.c</tt> to see how VDR implements
its own player for the VDR recordings.
<p>
To play the video data, the player needs to call its member function
<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
To play the actual data, the player needs to call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre>
int PlayVideo(const uchar *Data, int Length);
int PlayPes(const uchar *Data, int Length, bool VideoOnly);
</pre></td></tr></table><p>
where <tt>Data</tt> points to a block of <tt>Length</tt> bytes of a PES data
stream. There are no prerequisites regarding the length or alignment of an
stream containing any combination of video, audio or dolby tracks. Which audio
or dolby track will actually be played is controlled by the device the player
is attached to. There are no prerequisites regarding the length or alignment of an
individual block of data. The sum of all blocks must simply result in the
desired video data stream, and it must be delivered fast enough so that the
desired data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
<!--X1.3.18--></td></tr></table>
To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre>
@ -1042,24 +1046,26 @@ bool DevicePoll(cPoller &amp;Poller, int TimeoutMs = 0);
to determine whether the device is ready for further data.
<p>
If the player can provide more than a single audio track, it can implement the
following functions to make them available:
<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
By default all audio track handling is done by the device a player is
attached to.
If the player can provide more than a single audio track, and has special
requirements in order to set a given track, it can implement the
following function to allow the device to set a specific track:
<p><table><tr><td bgcolor=#F0F0F0><pre>
virtual int NumAudioTracks(void) const;
virtual const char **GetAudioTracks(int *CurrentTrack = NULL);
virtual void SetAudioTrack(int Index);
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
</pre></td></tr></table><p>
<p>
If there is an additional audio track that has to be replayed with external hardware,
the player shall call its member function
A player that has special requirements about audio tracks should announce its
available audio tracks by calling
<p><table><tr><td bgcolor=#F0F0F0><pre>
void PlayAudio(const uchar *Data, int Length);
bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0)
</pre></td></tr></table><p>
where <tt>Data</tt> points to a complete audio PES packet of <tt>Length</tt> bytes.
See <tt>device.h</tt> for details about the parameters for track handling.
<!--X1.3.18--></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:
@ -1217,7 +1223,7 @@ Mode</i>).
If the <tt>cReceiver</tt> isn't needed any more, it may simply be <i>deleted</i>
and will automatically detach itself from the <tt>cDevice</tt>.
<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<a name="Filters"><hr><h2>Filters</h2>
<center><i><b>A Fistful of Datas</b></i></center><p>
@ -1263,7 +1269,7 @@ and will automatically detach itself from the <tt>cDevice</tt>.
See VDR/eit.c or VDR/pat.c to learn how to process filter data.
<!--X1.3.0--></td></tr></table>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<a name="The On Screen Display"><hr><h2>The On Screen Display</h2>
<center><i><b>Window to the world</b></i></center><p>
@ -1375,7 +1381,7 @@ new cMySkin;
in the <a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
Do not delete this object, it will be automatically deleted when the program ends.
<p>
<!--X1.3.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
In order to be able to easily identify plugins that implement a skin it is recommended
that the name of such a plugin should be
@ -1527,9 +1533,7 @@ The functions to implement replaying capabilites are
virtual bool HasDecoder(void) const;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
<!--X1.2.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
virtual int64_t GetSTC(void);
<!--X1.2.6--></td></tr></table>
virtual void TrickSpeed(int Speed);
virtual void Clear(void);
virtual void Play(void);
@ -1549,7 +1553,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<p>
<b>Section Filtering</b>
<p>
@ -1579,7 +1583,7 @@ handle section data.
<p>
<b>On Screen Display</b>
<p>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
If your device provides On Screen Display (OSD) capabilities (which every device
that is supposed to be used as a primary device should do), it shall implement
an "OSD provider" class, derived from <tt>cOsdProvider</tt>, which, when its <tt>CreateOsd()</tt>

View File

@ -28,3 +28,7 @@ VDR Plugin 'sky' Revision History
2004-10-16: Version 0.3.1
- Improved buffer handling.
2004-12-12: Version 0.3.2
- Changed Apid access in cChannel.

View File

@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
* $Id: sky.c 1.7 2004/10/16 09:10:06 kls Exp $
* $Id: sky.c 1.8 2004/12/12 14:27:33 kls Exp $
*/
#include <sys/socket.h>
@ -14,7 +14,7 @@
#include <vdr/plugin.h>
#include <vdr/sources.h>
static const char *VERSION = "0.3.1";
static const char *VERSION = "0.3.2";
static const char *DESCRIPTION = "Sky Digibox interface";
// --- cDigiboxDevice --------------------------------------------------------
@ -213,7 +213,7 @@ bool cDigiboxDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
cSkyChannel *SkyChannel = SkyChannels.GetSkyChannel(Channel);
if (SkyChannel) {
digiboxChannelNumber = SkyChannel->digiboxChannelNumber;
apid = Channel->Apid1();
apid = Channel->Apid(0);
vpid = Channel->Vpid();
//XXX only when recording??? -> faster channel switching!
LircSend("SKY"); // makes sure the Digibox is "on"

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: channels.h 1.22 2004/10/31 12:54:26 kls Exp $
* $Id: channels.h 1.23 2004/12/05 13:49:04 kls Exp $
*/
#ifndef __CHANNELS_H
@ -145,10 +145,10 @@ public:
int Srate(void) const { return srate; }
int Vpid(void) const { return vpid; }
int Ppid(void) const { return ppid; }
int Apid1(void) const { return apids[0]; }
int Apid2(void) const { return apids[1]; }
int Dpid1(void) const { return dpids[0]; }
int Dpid2(void) const { return dpids[1]; }
int Apid(int i) const { return (0 <= i && i < MAXAPIDS) ? apids[i] : 0; }
int Dpid(int i) const { return (0 <= i && i < MAXAPIDS) ? dpids[i] : 0; }
const char *Alang(int i) const { return (0 <= i && i < MAXAPIDS) ? alangs[i] : ""; }
const char *Dlang(int i) const { return (0 <= i && i < MAXAPIDS) ? dlangs[i] : ""; }
int Tpid(void) const { return tpid; }
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; }

266
device.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 1.62 2004/10/30 14:53:38 kls Exp $
* $Id: device.c 1.63 2004/12/17 13:51:44 kls Exp $
*/
#include "device.h"
@ -19,6 +19,87 @@
#include "status.h"
#include "transfer.h"
// --- cPesAssembler ---------------------------------------------------------
class cPesAssembler {
private:
uchar *data;
uint32_t tag;
int length;
int size;
bool Realloc(int Size);
public:
cPesAssembler(void);
~cPesAssembler();
int ExpectedLength(void) { return data[4] * 256 + data[5] + 6; }
int Length(void) { return length; }
const uchar *Data(void) { return data; }
void Reset(void);
void Put(uchar c);
void Put(const uchar *Data, int Length);
bool IsPes(void);
};
cPesAssembler::cPesAssembler(void)
{
data = NULL;
size = 0;
Reset();
}
cPesAssembler::~cPesAssembler()
{
free(data);
}
void cPesAssembler::Reset(void)
{
tag = 0xFFFFFFFF;
length = 0;
}
bool cPesAssembler::Realloc(int Size)
{
if (Size > size) {
size = max(Size, 2048);
data = (uchar *)realloc(data, size);
if (!data) {
esyslog("ERROR: can't allocate memory for PES assembler");
length = 0;
size = 0;
return false;
}
}
return true;
}
void cPesAssembler::Put(uchar c)
{
if (!length) {
tag = (tag << 8) | c;
if ((tag & 0xFFFFFF00) == 0x00000100) {
if (Realloc(4)) {
*(uint32_t *)data = htonl(tag);
length = 4;
}
}
}
else if (Realloc(length + 1))
data[length++] = c;
}
void cPesAssembler::Put(const uchar *Data, int Length)
{
while (!length && Length > 0) {
Put(*Data++);
Length--;
}
if (Length && Realloc(length + Length)) {
memcpy(data + length, Data, Length);
length += Length;
}
}
// --- cDevice ---------------------------------------------------------------
// The default priority for non-primary devices:
@ -53,6 +134,9 @@ cDevice::cDevice(void)
ciHandler = NULL;
player = NULL;
pesAssembler = new cPesAssembler;
ClrAvailableTracks();
currentAudioTrack = ttAudioFirst;
for (int i = 0; i < MAXRECEIVERS; i++)
receiver[i] = NULL;
@ -74,6 +158,7 @@ cDevice::~cDevice()
delete patFilter;
delete eitFilter;
delete sectionHandler;
delete pesAssembler;
}
void cDevice::SetUseDevice(int n)
@ -427,7 +512,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (CaDevice && CanReplay()) {
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
if (CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));//XXX+
cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
else
Result = scrNoTransfer;
}
@ -482,17 +567,7 @@ void cDevice::SetVolumeDevice(int Volume)
{
}
int cDevice::NumAudioTracksDevice(void) const
{
return 0;
}
const char **cDevice::GetAudioTracksDevice(int *CurrentTrack) const
{
return NULL;
}
void cDevice::SetAudioTrackDevice(int Index)
void cDevice::SetAudioTrackDevice(eTrackType Type)
{
}
@ -524,22 +599,72 @@ void cDevice::SetVolume(int Volume, bool Absolute)
}
}
void cDevice::ClrAvailableTracks(void)
{
memset(availableTracks, 0, sizeof(availableTracks));
}
bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language, uint32_t Flags)
{
eTrackType t = eTrackType(Type + Index);
if ((Type == ttAudio && IS_AUDIO_TRACK(t)) ||
(Type == ttDolby && IS_DOLBY_TRACK(t))) {
if (Language)
strn0cpy(availableTracks[t].language, Language, sizeof(availableTracks[t].language));
availableTracks[t].flags = Flags;
availableTracks[t].id = Id; // setting 'id' last to avoid the need for extensive locking
return true;
}
else
esyslog("ERROR: SetAvailableTrack called with invalid Type/Index (%d/%d)", Type, Index);
return false;
}
const tTrackId *cDevice::GetTrack(eTrackType Type)
{
return (ttNone < Type && Type < ttMaxTrackTypes) ? &availableTracks[Type] : NULL;
}
int cDevice::NumAudioTracks(void) const
{
return player ? player->NumAudioTracks() : NumAudioTracksDevice();
int n = 0;
for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
if (availableTracks[i].id)
n++;
}
return n;
}
const char **cDevice::GetAudioTracks(int *CurrentTrack) const
bool cDevice::SetCurrentAudioTrack(eTrackType Type)
{
return player ? player->GetAudioTracks(CurrentTrack) : GetAudioTracksDevice(CurrentTrack);
if (ttNone < Type && Type < ttDolbyLast) {
if (IS_DOLBY_TRACK(Type))
SetDigitalAudioDevice(true);
currentAudioTrack = Type;
if (player)
player->SetAudioTrack(currentAudioTrack, GetTrack(currentAudioTrack));
else
SetAudioTrackDevice(currentAudioTrack);
if (IS_AUDIO_TRACK(Type))
SetDigitalAudioDevice(false);
return true;
}
return false;
}
void cDevice::SetAudioTrack(int Index)
bool cDevice::IncCurrentAudioTrack(void)
{
if (player)
player->SetAudioTrack(Index);
else
SetAudioTrackDevice(Index);
int i = currentAudioTrack + 1;
for (;;) {
if (i > ttDolbyLast)
i = ttAudioFirst;
if (i == currentAudioTrack)
break;
if (availableTracks[i].id)
return SetCurrentAudioTrack(eTrackType(i));
i++;
}
return false;
}
bool cDevice::CanReplay(void) const
@ -595,6 +720,7 @@ bool cDevice::AttachPlayer(cPlayer *Player)
if (CanReplay()) {
if (player)
Detach(player);
ClrAvailableTracks();
player = Player;
SetPlayMode(player->playMode);
player->device = this;
@ -639,11 +765,105 @@ int cDevice::PlayVideo(const uchar *Data, int Length)
return -1;
}
void cDevice::PlayAudio(const uchar *Data, int Length)
int cDevice::PlayAudio(const uchar *Data, int Length)
{
Audios.PlayAudio(Data, Length);
return -1;
}
int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
{
bool FirstLoop = true;
uchar c = Data[3];
const uchar *Start = Data;
const uchar *End = Start + Length;
while (Start < End) {
int d = End - Start;
int w = d;
switch (c) {
case 0xE0 ... 0xEF: // video
w = PlayVideo(Start, d);
break;
case 0xC0 ... 0xDF: // audio
SetAvailableTrack(ttAudio, c - 0xC0, c);
if (!VideoOnly && c == availableTracks[currentAudioTrack].id)
w = PlayAudio(Start, d);
break;
case 0xBD: // dolby
SetAvailableTrack(ttDolby, 0, c);
if (!VideoOnly && c == availableTracks[currentAudioTrack].id) {
w = PlayAudio(Start, d);
if (FirstLoop)
Audios.PlayAudio(Data, Length);
}
break;
default:
;//esyslog("ERROR: unexpected packet id %02X", c);
}
if (w > 0)
Start += w;
else if (w <= 0) {
if (Start != Data)
esyslog("ERROR: incomplete PES packet write!");
return Start == Data ? w : Start - Data;
}
FirstLoop = false;
}
return Length;
}
int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
{
if (!Data) {
pesAssembler->Reset();
return 0;
}
int Result = 0;
if (pesAssembler->Length()) {
// Make sure we have a complete PES header:
while (pesAssembler->Length() < 6 && Length > 0) {
pesAssembler->Put(*Data++);
Length--;
Result++;
}
if (pesAssembler->Length() < 6)
return Result; // Still no complete PES header - wait for more
int l = pesAssembler->ExpectedLength();
int Rest = min(l - pesAssembler->Length(), Length);
pesAssembler->Put(Data, Rest);
Data += Rest;
Length -= Rest;
Result += Rest;
if (pesAssembler->Length() < l)
return Result; // Still no complete PES packet - wait for more
// Now pesAssembler contains one complete PES packet.
int w = PlayPesPacket(pesAssembler->Data(), pesAssembler->Length(), VideoOnly);
if (w > 0)
pesAssembler->Reset();
return Result > 0 ? Result : w < 0 ? w : 0;
}
int i = 0;
while (i <= Length - 6) {
if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
int l = Data[i + 4] * 256 + Data[i + 5] + 6;
if (i + l > Length) {
// Store incomplete PES packet for later completion:
pesAssembler->Put(Data + i, Length - i);
return Length;
}
int w = PlayPesPacket(Data + i, l, VideoOnly);
if (w > 0)
i += l;
else if (w < 0)
return i == 0 ? w : i;
}
else
i++;
}
if (i < Length)
pesAssembler->Put(Data + i, Length - i);
return Length;
}
int cDevice::Ca(void) const
{
int ca = 0;

136
device.h
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.46 2004/10/30 14:49:56 kls Exp $
* $Id: device.h 1.47 2004/12/17 13:44:34 kls Exp $
*/
#ifndef __DEVICE_H
@ -56,10 +56,36 @@ enum eVideoSystem { vsPAL,
vsNTSC
};
enum eTrackType { ttNone,
ttAudio,
ttAudioFirst = ttAudio,
ttAudioLast = ttAudioFirst + 31/*XXX MAXAPIDS - 1*/,
ttDolby,
ttDolbyFirst = ttDolby,
ttDolbyLast = ttDolbyFirst + 31/*XXX MAXAPIDS - 1*/,
/* future...
ttSubtitle,
ttSubtitleFirst = ttSubtitle,
ttSubtitleLast = ttSubtitleFirst + 31,
*/
ttMaxTrackTypes
};
#define IS_AUDIO_TRACK(t) (ttAudioFirst <= (t) && (t) <= ttAudioLast)
#define IS_DOLBY_TRACK(t) (ttDolbyFirst <= (t) && (t) <= ttDolbyLast)
struct tTrackId {
uint16_t id; // The PES packet id or the PID.
char language[8]; // something like either "eng" or "deu/eng"
// for future use:
uint32_t flags; // Used to further identify the actual track.
};
class cChannel;
class cPlayer;
class cReceiver;
class cSpuDecoder;
class cPesAssembler;
/// The cDevice class is the base from which actual devices can be derived.
@ -283,6 +309,37 @@ public:
///< Returns the video system of the currently displayed material
///< (default is PAL).
// Track facilities
private:
tTrackId availableTracks[ttMaxTrackTypes];
eTrackType currentAudioTrack;
protected:
virtual void SetAudioTrackDevice(eTrackType Type);
///< Sets the current audio track to the given value.
public:
void ClrAvailableTracks(void);
bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0);
///< Sets the track of the given Type and Index to the given values.
///< Type must be one of the basic eTrackType values, like ttAudio or ttDolby.
///< Index tells which track of the given basic type is meant.
///< \return Returns true if the track was set correctly, false otherwise.
const tTrackId *GetTrack(eTrackType Type);
///< Returns a pointer to the given track id, or NULL if Type is not
///< less than ttMaxTrackTypes.
int NumAudioTracks(void) const;
///< Returns the number of audio tracks that are currently available.
///< This is just for information, to quickly find out whether there
///< is more than one audio track.
eTrackType GetCurrentAudioTrack(void) { return currentAudioTrack; }
bool SetCurrentAudioTrack(eTrackType Type);
///< Sets the current audio track to the given Type.
///< \return Returns true if Type is a valid audio track, false otherwise.
bool IncCurrentAudioTrack(void);
///< Sets the current audio track to the next available track (wraps to
///< to the first one if necessary).
///< \return Returns true if the audio track has been changed, false otherwise.
// Audio facilities
private:
@ -291,27 +348,9 @@ private:
protected:
virtual void SetVolumeDevice(int Volume);
///< Sets the audio volume on this device (Volume = 0...255).
virtual int NumAudioTracksDevice(void) const;
///< Returns the number of audio tracks that are currently available on this
///< device. The default return value is 0, meaning that this device
///< doesn't have multiple audio track capabilities. The return value may
///< change with every call and need not necessarily be the number of list
///< entries returned by GetAudioTracksDevice(). This function is mainly called to
///< decide whether there should be an "Audio" button in a menu.
virtual const char **GetAudioTracksDevice(int *CurrentTrack = NULL) const;
///< Returns a list of currently available audio tracks. The last entry in the
///< list must be NULL. The number of entries does not necessarily have to be
///< the same as returned by a previous call to NumAudioTracksDevice().
///< If CurrentTrack is given, it will be set to the index of the current track
///< in the returned list. Note that the list must not be changed after it has
///< been returned by a call to GetAudioTracksDevice()! The only time the list may
///< change is *inside* the GetAudioTracksDevice() function.
///< By default the return value is NULL and CurrentTrack, if given, will not
///< have any meaning.
virtual void SetAudioTrackDevice(int Index);
///< Sets the current audio track to the given value, which should be within the
///< range of the list returned by a previous call to GetAudioTracksDevice()
///< (otherwise nothing will happen).
virtual void SetDigitalAudioDevice(bool On) {}
///< Tells the actual device that digital audio output shall be switched
///< on or off.
public:
bool IsMute(void) const { return mute; }
bool ToggleMute(void);
@ -320,32 +359,37 @@ public:
///< Sets the volume to the given value, either absolutely or relative to
///< the current volume.
static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???
int NumAudioTracks(void) const;
///< Returns the number of audio tracks that are currently available on this
///< device or a player attached to it.
const char **GetAudioTracks(int *CurrentTrack = NULL) const;
///< Returns a list of currently available audio tracks. The last entry in the
///< list is NULL. The number of entries does not necessarily have to be
///< the same as returned by a previous call to NumAudioTracks().
///< If CurrentTrack is given, it will be set to the index of the current track
///< in the returned list.
///< By default the return value is NULL and CurrentTrack, if given, will not
///< have any meaning.
void SetAudioTrack(int Index);
///< Sets the current audio track to the given value, which should be within the
///< range of the list returned by a previous call to GetAudioTracks() (otherwise
///< nothing will happen).
// Player facilities
private:
cPlayer *player;
cPesAssembler *pesAssembler;
protected:
virtual bool CanReplay(void) const;
///< Returns true if this device can currently start a replay session.
virtual bool SetPlayMode(ePlayMode PlayMode);
///< Sets the device into the given play mode.
///< \return true if the operation was successful.
virtual int PlayVideo(const uchar *Data, int Length);
///< Plays the given data block as video.
///< Data points to exactly one complete PES packet of the given Length.
///< PlayVideo() shall process the packet either as a whole (returning
///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
///< \return Returns the number of bytes actually taken from Data, or -1
///< in case of an error.
virtual int PlayAudio(const uchar *Data, int Length);
///< Plays the given data block as audio.
///< Data points to exactly one complete PES packet of the given Length.
///< PlayAudio() shall process the packet either as a whole (returning
///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
///< \return Returns the number of bytes actually taken from Data, or -1
///< in case of an error.
virtual int PlayPesPacket(const uchar *Data, int Length, bool VideoOnly = false);
///< Plays the single PES packet in Data with the given Length.
///< If VideoOnly is true, only the video will be displayed,
///< which is necessary for trick modes like 'fast forward'.
///< Data must point to one single, complete PES packet.
public:
virtual int64_t GetSTC(void);
///< Gets the current System Time Counter, which can be used to
@ -382,14 +426,16 @@ public:
///< If TimeoutMs is not zero, the device will wait up to the given
///< number of milliseconds before returning in case there is still
///< data in the buffers..
virtual int PlayVideo(const uchar *Data, int Length);
///< 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 stream.
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.
virtual int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
///< Plays all valid PES packets in Data with the given Length.
///< If Data is NULL any leftover data from a previous call will be
///< discarded. If VideoOnly is true, only the video will be displayed,
///< which is necessary for trick modes like 'fast forward'.
///< Data should point to a sequence of complete PES packets. If the
///< last packet in Data is not complete, it will be copied and combined
///< to a complete packet with data from the next call to PlayPes().
///< That way any functions called from within PlayPes() will be
///< guaranteed to always receive complete PES packets.
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.106 2004/11/27 10:24:47 kls Exp $
* $Id: dvbdevice.c 1.107 2004/12/17 14:19:48 kls Exp $
*/
#include "dvbdevice.h"
@ -322,9 +322,9 @@ void cDvbTuner::Action(void)
cCiCaPmt CaPmt(channel.Source(), channel.Transponder(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
if (CaPmt.Valid()) {
CaPmt.AddPid(channel.Vpid(), 2);
CaPmt.AddPid(channel.Apid1(), 4);
CaPmt.AddPid(channel.Apid2(), 4);
CaPmt.AddPid(channel.Dpid1(), 0);
CaPmt.AddPid(channel.Apid(0), 4);
CaPmt.AddPid(channel.Apid(1), 4);
CaPmt.AddPid(channel.Dpid(0), 0);
if (ciHandler->SetCaPmt(CaPmt, Slot)) {
tunerStatus = tsCam;
startTime = 0;
@ -352,8 +352,8 @@ cDvbDevice::cDvbDevice(int n)
dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
spuDecoder = NULL;
digitalAudio = false;
playMode = pmNone;
aPid1 = aPid2 = 0;
// Devices that are present on all card types:
@ -728,7 +728,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
result = hasPriority;
if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid1() && !HasPid(Channel->Apid1())) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS
if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE)
needsDetachReceivers = !ciHandler // only LL-firmware can do non-live CA channels
@ -801,9 +801,13 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
// PID settings:
if (TurnOnLivePIDs) {
aPid1 = Channel->Apid1();
aPid2 = Channel->Apid2();
if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Vpid(), ptVideo) && AddPid(Channel->Apid1(), ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
ClrAvailableTracks();
for (int i = 0; i < MAXAPIDS; i++) {
//XXX do this in cDevice???
SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
}
if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Vpid(), ptVideo) && AddPid(Channel->Apid(0), ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
return false;
}
@ -815,7 +819,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
}
else if (StartTransferMode)
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
return true;
}
@ -835,34 +839,32 @@ void cDvbDevice::SetVolumeDevice(int Volume)
}
}
int cDvbDevice::NumAudioTracksDevice(void) const
void cDvbDevice::SetDigitalAudioDevice(bool On)
{
int n = 0;
if (aPid1)
n++;
if (Ca() <= MAXDEVICES && aPid2 && aPid1 != aPid2) // a CA recording session blocks switching live audio tracks
n++;
return n;
}
const char **cDvbDevice::GetAudioTracksDevice(int *CurrentTrack) const
{
if (NumAudioTracksDevice()) {
if (CurrentTrack)
*CurrentTrack = (pidHandles[ptAudio].pid == aPid1) ? 0 : 1;
static const char *audioTracks1[] = { "Audio 1", NULL };
static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
return NumAudioTracksDevice() > 1 ? audioTracks2 : audioTracks1;
if (digitalAudio != On) {
if (digitalAudio)
cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed
SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume());
digitalAudio = On;
}
return NULL;
}
void cDvbDevice::SetAudioTrackDevice(int Index)
void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
{
if (0 <= Index && Index < NumAudioTracksDevice()) {
int Pid = Index ? aPid2 : aPid1;
pidHandles[ptAudio].pid = Pid;
SetPid(&pidHandles[ptAudio], ptAudio, true);
const tTrackId *TrackId = GetTrack(Type);
if (TrackId && TrackId->id) {
if (IS_AUDIO_TRACK(Type)) {
pidHandles[ptAudio].pid = TrackId->id;
SetPid(&pidHandles[ptAudio], ptAudio, true);
}
else if (IS_DOLBY_TRACK(Type)) {
// Currently this works only in Transfer Mode
cChannel *Channel = Channels.GetByNumber(CurrentChannel());
if (Channel) {
SetChannelDevice(Channel, false);
cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
}
}
}
}
@ -1120,16 +1122,12 @@ bool cDvbDevice::Flush(int TimeoutMs)
int cDvbDevice::PlayVideo(const uchar *Data, int Length)
{
int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
if (fd >= 0)
return write(fd, Data, Length);
return -1;
return write(fd_video, Data, Length);
}
void cDvbDevice::PlayAudio(const uchar *Data, int Length)
int cDvbDevice::PlayAudio(const uchar *Data, int Length)
{
//XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
cDevice::PlayAudio(Data, Length);
return write(fd_audio, 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.30 2004/11/07 10:25:16 kls Exp $
* $Id: dvbdevice.h 1.31 2004/12/17 14:01:31 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -90,15 +90,18 @@ public:
virtual void SetVideoFormat(bool VideoFormat16_9);
virtual eVideoSystem GetVideoSystem(void);
// Track facilities
protected:
virtual void SetAudioTrackDevice(eTrackType Type);
// Audio facilities
private:
int aPid1, aPid2;
bool digitalAudio;
protected:
virtual void SetVolumeDevice(int Volume);
virtual int NumAudioTracksDevice(void) const;
virtual const char **GetAudioTracksDevice(int *CurrentTrack = NULL) const;
virtual void SetAudioTrackDevice(int Index);
virtual void SetDigitalAudioDevice(bool On);
// Player facilities
@ -106,6 +109,8 @@ protected:
ePlayMode playMode;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
virtual int PlayVideo(const uchar *Data, int Length);
virtual int PlayAudio(const uchar *Data, int Length);
public:
virtual int64_t GetSTC(void);
virtual void TrickSpeed(int Speed);
@ -116,8 +121,6 @@ public:
virtual void StillPicture(const uchar *Data, int Length);
virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
virtual bool Flush(int TimeoutMs = 0);
virtual int PlayVideo(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.27 2004/11/27 10:07:05 kls Exp $
* $Id: dvbplayer.c 1.28 2004/12/11 17:02:40 kls Exp $
*/
#include "dvbplayer.h"
@ -194,13 +194,10 @@ private:
ePlayDirs playDir;
int trickSpeed;
int readIndex, writeIndex;
bool canToggleAudioTrack;
uchar audioTrack;
cFrame *readFrame;
cFrame *playFrame;
void TrickSpeed(int Increment);
void Empty(void);
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
int Resume(void);
bool Save(void);
@ -220,9 +217,6 @@ public:
void Goto(int Position, bool Still = false);
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
virtual int NumAudioTracks(void) const;
virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
virtual void SetAudioTrack(int Index);
};
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@ -245,8 +239,6 @@ cDvbPlayer::cDvbPlayer(const char *FileName)
playMode = pmPlay;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
canToggleAudioTrack = false;
audioTrack = 0xC0;
readIndex = writeIndex = -1;
readFrame = NULL;
playFrame = NULL;
@ -312,41 +304,6 @@ void cDvbPlayer::Empty(void)
firstPacket = true;
}
void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
{
if (index) {
for (int i = 0; i < Length - 6; i++) {
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
uchar c = b[i + 3];
int l = b[i + 4] * 256 + b[i + 5] + 6;
switch (c) {
case 0xBD: // dolby
if (Except)
PlayAudio(&b[i], l);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)
canToggleAudioTrack = true;
if (!Except || c != Except)
memset(&b[i], 0x00, min(l, Length-i));
break;
case 0xE0 ... 0xEF: // video
break;
default:
//esyslog("ERROR: unexpected packet id %02X", c);
l = 0;
}
if (l)
i += l - 1; // the loop increments, too!
}
/*XXX
else
esyslog("ERROR: broken packet header");
XXX*/
}
}
}
bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0)
@ -413,7 +370,6 @@ void cDvbPlayer::Action(void)
nonBlockingFileReader = new cNonBlockingFileReader;
int Length = 0;
int AudioTrack = 0; // -1 = any, 0 = none, >0 = audioTrack
running = true;
while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) {
@ -449,9 +405,6 @@ void cDvbPlayer::Action(void)
continue;
}
readIndex = Index;
AudioTrack = 0;
// must clear all audio packets because the buffer is not emptied
// when falling back from "fast forward" to "play" (see above)
}
else if (index) {
uchar FileNumber;
@ -462,12 +415,9 @@ void cDvbPlayer::Action(void)
eof = true;
continue;
}
AudioTrack = audioTrack;
}
else { // allows replay even if the index file is missing
else // allows replay even if the index file is missing
Length = MAXFRAMESIZE;
AudioTrack = -1;
}
if (Length == -1)
Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
else if (Length > MAXFRAMESIZE) {
@ -478,8 +428,6 @@ void cDvbPlayer::Action(void)
}
int r = nonBlockingFileReader->Read(replayFile, b, Length);
if (r > 0) {
if (AudioTrack == 0)
StripAudioPackets(b, r);
readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
b = NULL;
}
@ -517,15 +465,14 @@ void cDvbPlayer::Action(void)
pc = playFrame->Count();
if (p) {
if (firstPacket) {
PlayPes(NULL, 0);
cRemux::SetBrokenLink(p, pc);
firstPacket = false;
}
if (AudioTrack > 0)
StripAudioPackets(p, pc, AudioTrack);
}
}
if (p) {
int w = PlayVideo(p, pc);
int w = PlayPes(p, pc, playMode != pmPlay);
if (w > 0) {
p += w;
pc -= w;
@ -717,7 +664,6 @@ void cDvbPlayer::Goto(int Index, bool Still)
if (r > 0) {
if (playMode == pmPause)
DevicePlay();
StripAudioPackets(b, r);
DeviceStillPicture(b, r);
}
playMode = pmStill;
@ -757,31 +703,6 @@ bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
return true;
}
int cDvbPlayer::NumAudioTracks(void) const
{
return canToggleAudioTrack ? 2 : 1;
}
const char **cDvbPlayer::GetAudioTracks(int *CurrentTrack) const
{
if (NumAudioTracks()) {
if (CurrentTrack)
*CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
static const char *audioTracks1[] = { "Audio 1", NULL };
static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
}
return NULL;
}
void cDvbPlayer::SetAudioTrack(int Index)
{
if ((audioTrack == 0xC0) != (Index == 0)) {
audioTrack = (Index == 1) ? 0xC1 : 0xC0;
Empty();
}
}
// --- cDvbPlayerControl -----------------------------------------------------
cDvbPlayerControl::cDvbPlayerControl(const char *FileName)

15
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.320 2004/11/20 10:49:17 kls Exp $
* $Id: menu.c 1.321 2004/12/12 16:07:05 kls Exp $
*/
#include "menu.h"
@ -2471,15 +2471,8 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
state = replaying ? osContinue : osRecord;
break;
case kGreen: if (!HadSubMenu) {
int CurrentAudioTrack = -1;
const char **AudioTracks = cDevice::PrimaryDevice()->GetAudioTracks(&CurrentAudioTrack);
if (AudioTracks) {
const char **at = &AudioTracks[CurrentAudioTrack];
if (!*++at)
at = AudioTracks;
cDevice::PrimaryDevice()->SetAudioTrack(at - AudioTracks);
state = osEnd;
}
cDevice::PrimaryDevice()->IncCurrentAudioTrack();
state = osEnd;
}
break;
case kYellow: if (!HadSubMenu)
@ -2826,7 +2819,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid(0), ch->Apid(1), ch->Dpid(0), ch->Dpid(1));
if (device->AttachReceiver(recorder)) {
Recording.WriteSummary();
cStatus::MsgRecording(device, Recording.Name());

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.8 2004/11/20 11:33:08 kls Exp $
* $Id: player.c 1.9 2004/12/12 11:21:07 kls Exp $
*/
#include "player.h"
@ -23,23 +23,14 @@ cPlayer::~cPlayer()
Detach();
}
int cPlayer::PlayVideo(const uchar *Data, int Length)
int cPlayer::PlayPes(const uchar *Data, int Length, bool VideoOnly)
{
if (device)
return device->PlayVideo(Data, Length);
esyslog("ERROR: attempt to use cPlayer::PlayVideo() without attaching to a cDevice!");
return device->PlayPes(Data, Length, VideoOnly);
esyslog("ERROR: attempt to use cPlayer::PlayPes() without attaching to a cDevice!");
return -1;
}
void cPlayer::PlayAudio(const uchar *Data, int Length)
{
if (device) {
device->PlayAudio(Data, Length);
return;
}
esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!");
}
void cPlayer::Detach(void)
{
if (device)

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.12 2004/06/19 08:53:07 kls Exp $
* $Id: player.h 1.13 2004/12/12 11:20:19 kls Exp $
*/
#ifndef __PLAYER_H
@ -19,6 +19,7 @@ private:
cDevice *device;
ePlayMode playMode;
protected:
bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0) { return device ? device->SetAvailableTrack(Type, Index, Id, Language, Flags) : false; }
bool DevicePoll(cPoller &Poller, int TimeoutMs = 0) { return device ? device->Poll(Poller, TimeoutMs) : false; }
bool DeviceFlush(int TimeoutMs = 0) { return device ? device->Flush(TimeoutMs) : true; }
void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
@ -32,12 +33,10 @@ protected:
// This function is called right after the cPlayer has been attached to
// (On == true) or before it gets detached from (On == false) a cDevice.
// It can be used to do things like starting/stopping a thread.
int PlayVideo(const uchar *Data, int Length);
// 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
int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
// Sends the given PES Data to the device and returns the number of
// bytes that have actually been accepted by the device (or a
// negative value in case of an error).
void PlayAudio(const uchar *Data, int Length);
// Plays additional audio streams, like Dolby Digital.
public:
cPlayer(ePlayMode PlayMode = pmAudioVideo);
virtual ~cPlayer();
@ -51,27 +50,10 @@ public:
// we are going forward or backward and 'Speed' is -1 if this is normal
// play/pause mode, 0 if it is single speed fast/slow forward/back mode
// and >0 if this is multi speed mode.
virtual int NumAudioTracks(void) const { return 0; }
// Returns the number of audio tracks that are currently available on this
// player. The default return value is 0, meaning that this player
// doesn't have multiple audio track capabilities. The return value may
// change with every call and need not necessarily be the number of list
// entries returned by GetAudioTracks(). This function is mainly called to
// decide whether there should be an "Audio" button in a menu.
virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const { return NULL; }
// Returns a list of currently available audio tracks. The last entry in the
// list must be NULL. The number of entries does not necessarily have to be
// the same as returned by a previous call to NumAudioTracks().
// If CurrentTrack is given, it will be set to the index of the current track
// in the returned list. Note that the list must not be changed after it has
// been returned by a call to GetAudioTracks()! The only time the list may
// change is *inside* the GetAudioTracks() function.
// By default the return value is NULL and CurrentTrack, if given, will not
// have any meaning.
virtual void SetAudioTrack(int Index) {}
// Sets the current audio track to the given value, which should be within the
// range of the list returned by a previous call to GetAudioTracks()
// (otherwise nothing will happen).
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId) {}
// Sets the current audio track to the given value.
// This is just a virtual hook for players that need to do special things
// in order to switch audio tracks.
};
class cControl : public cOsdObject {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skinsttng.c 1.6 2004/07/18 11:32:42 kls Exp $
* $Id: skinsttng.c 1.7 2004/12/05 13:19:59 kls Exp $
*/
// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
@ -233,14 +233,14 @@ void cSkinSTTNGDisplayChannel::SetChannel(const cChannel *Channel, int Number)
x -= bmEncrypted.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmEncrypted.Height()) / 2, bmEncrypted, Theme.Color(Channel->Ca() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
x -= bmDolbyDigital.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmDolbyDigital.Height()) / 2, bmDolbyDigital, Theme.Color(Channel->Dpid1() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
osd->DrawBitmap(x, y0 + (y1 - y0 - bmDolbyDigital.Height()) / 2, bmDolbyDigital, Theme.Color(Channel->Dpid(0) ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
x -= bmAudio.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmAudio.Height()) / 2, bmAudio, Theme.Color(Channel->Apid2() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
osd->DrawBitmap(x, y0 + (y1 - y0 - bmAudio.Height()) / 2, bmAudio, Theme.Color(Channel->Apid(1) ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
if (Channel->Vpid()) {
x -= bmTeletext.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmTeletext.Height()) / 2, bmTeletext, Theme.Color(Channel->Tpid() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
}
else if (Channel->Apid1()) {
else if (Channel->Apid(0)) {
x -= bmRadio.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmRadio.Height()) / 2, bmRadio, Theme.Color(clrChannelSymbolOn), frameColor);
}

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.18 2004/10/23 13:35:08 kls Exp $
* $Id: transfer.c 1.19 2004/11/28 11:51:00 kls Exp $
*/
#include "transfer.h"
@ -20,8 +20,6 @@ cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2)
{
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
canToggleAudioTrack = false;
audioTrack = 0xC0;
active = false;
}
@ -60,8 +58,41 @@ void cTransfer::Action(void)
int PollTimeouts = 0;
uchar *p = NULL;
int Result = 0;
// XXX Apparently this isn't necessary with the new PES data handling that
// XXX was intorduced in VDR 1.3.18. If you do need this, enable the following
// XXX line and send an email to kls@cadsoft.de. If nobody requires this, it
// XXX will be removed later. kls 2004-12-27
//#define FW_NEEDS_BUFFER_RESERVE_FOR_AC3
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
bool Cleared = false;
bool GotBufferReserve = false;
#endif
active = true;
while (active) {
#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
#define HasDolby true
if (HasDolby) {
if (IsAttached() && !Cleared) {
PlayPes(NULL, 0);
Cleared = true;
}
//XXX For dolby we've to fill the buffer because the firmware does
//XXX not decode dolby but use a PCM stream for transport, therefore
//XXX the firmware has not enough buffer for noiseless skipping early
//XXX PCM samples (each dolby frame requires 6144 bytes in PCM and
//XXX audio is mostly to early in comparison to video).
//XXX To resolve this, the remuxer or PlayPes() should synchronize
//XXX audio with the video frames. 2004/09/09 Werner
if (!GotBufferReserve) {
if (ringBuffer->Available() < 3 * MAXFRAMESIZE / 2) {
cCondWait::SleepMs(20); // allow the buffer to collect some reserve
continue;
}
else
GotBufferReserve = true;
}
}
#endif
int Count;
uchar *b = ringBuffer->Get(Count);
if (b) {
@ -80,13 +111,13 @@ void cTransfer::Action(void)
if (Count)
ringBuffer->Del(Count);
}
if (!p && (p = remux->Get(Result)) != NULL)
StripAudioPackets(p, Result, audioTrack);
if (!p)
p = remux->Get(Result);
if (p) {
cPoller Poller;
if (DevicePoll(Poller, 100)) {
PollTimeouts = 0;
int w = PlayVideo(p, Result);
int w = PlayPes(p, Result);
if (w > 0) {
p += w;
Result -= w;
@ -112,64 +143,6 @@ void cTransfer::Action(void)
active = false;
}
void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
{
for (int i = 0; i < Length - 6; i++) {
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
uchar c = b[i + 3];
int l = b[i + 4] * 256 + b[i + 5] + 6;
switch (c) {
case 0xBD: // dolby
if (Except)
PlayAudio(&b[i], l);
// continue with deleting the data - otherwise it disturbs DVB replay
case 0xC0 ... 0xC1: // audio
if (c == 0xC1)
canToggleAudioTrack = true;
if (!Except || c != Except)
memset(&b[i], 0x00, min(l, Length-i));
break;
case 0xE0 ... 0xEF: // video
break;
default:
//esyslog("ERROR: unexpected packet id %02X", c);
l = 0;
}
if (l)
i += l - 1; // the loop increments, too!
}
/*XXX
else
esyslog("ERROR: broken packet header");
XXX*/
}
}
int cTransfer::NumAudioTracks(void) const
{
return canToggleAudioTrack ? 2 : 1;
}
const char **cTransfer::GetAudioTracks(int *CurrentTrack) const
{
if (NumAudioTracks()) {
if (CurrentTrack)
*CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
static const char *audioTracks1[] = { "Audio 1", NULL };
static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
}
return NULL;
}
void cTransfer::SetAudioTrack(int Index)
{
if ((audioTrack == 0xC0) != (Index == 0)) {
audioTrack = (Index == 1) ? 0xC1 : 0xC0;
DeviceClear();
}
}
// --- cTransferControl ------------------------------------------------------
cDevice *cTransferControl::receiverDevice = NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: transfer.h 1.5 2004/10/15 12:39:54 kls Exp $
* $Id: transfer.h 1.6 2004/11/28 11:51:37 kls Exp $
*/
#ifndef __TRANSFER_H
@ -20,10 +20,7 @@ class cTransfer : public cReceiver, public cPlayer, public cThread {
private:
cRingBufferLinear *ringBuffer;
cRemux *remux;
bool canToggleAudioTrack;
uchar audioTrack;
bool active;
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
@ -31,9 +28,6 @@ protected:
public:
cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2);
virtual ~cTransfer();
virtual int NumAudioTracks(void) const;
virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
virtual void SetAudioTrack(int Index);
};
class cTransferControl : public cControl {

4
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.193 2004/11/06 10:30:30 kls Exp $
* $Id: vdr.c 1.194 2004/12/05 13:20:29 kls Exp $
*/
#include <getopt.h>
@ -532,7 +532,7 @@ int main(int argc, char *argv[])
static time_t lastTime = 0;
if (time(NULL) - lastTime > MINCHANNELWAIT) {
cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (Channel && (Channel->Vpid() || Channel->Apid1())) {
if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
&& !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer...
&& !cDevice::SwitchChannel(1) // ...or the next higher available one...