mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Added support for AC3 replay over the DVB device
This commit is contained in:
parent
69ecb6a4d8
commit
c77989ee70
@ -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
24
HISTORY
@ -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.
|
||||
|
62
PLUGINS.html
62
PLUGINS.html
@ -14,18 +14,18 @@ Copyright © 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> </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> </td><td width=100%>
|
||||
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%>
|
||||
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%>
|
||||
<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </td><td width=100%>
|
||||
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%>
|
||||
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </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 &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> </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> </td><td width=100%>
|
||||
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%>
|
||||
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%>
|
||||
<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </td><td width=100%>
|
||||
<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%>
|
||||
<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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>
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
10
channels.h
10
channels.h
@ -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
266
device.c
@ -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
136
device.h
@ -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);
|
||||
|
78
dvbdevice.c
78
dvbdevice.c
@ -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)
|
||||
|
17
dvbdevice.h
17
dvbdevice.h
@ -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
|
||||
|
||||
|
87
dvbplayer.c
87
dvbplayer.c
@ -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
15
menu.c
@ -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());
|
||||
|
17
player.c
17
player.c
@ -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)
|
||||
|
36
player.h
36
player.h
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
101
transfer.c
101
transfer.c
@ -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;
|
||||
|
@ -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
4
vdr.c
@ -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...
|
||||
|
Loading…
Reference in New Issue
Block a user