Implemented handling DVB subtitles

This commit is contained in:
Klaus Schmidinger 2007-10-12 14:52:30 +02:00
parent 3a4b7f065c
commit 0c8cda9bd0
26 changed files with 1774 additions and 85 deletions

View File

@ -1024,6 +1024,8 @@ Rolf Ahrenberg <rahrenbe@cc.hut.fi>
for improving cControl::Launch() to keep 'control' from pointing to uninitialized
memory
for adding internationalization to the "skincurses" plugin
for helping with adding compatibility mode for playback of recordings made with
the subtitles plugin
Ralf Klueber <ralf.klueber@vodafone.com>
for reporting a bug in cutting a recording if there is only a single editing mark
@ -1293,6 +1295,8 @@ Marcus M
Pekka Virtanen <pekka.virtanen@sci.fi>
for adding language code handling to the subtitling descriptor in 'libsi'
for adding missing NULL checks when accessing sectionHandler in device.c
for writing the subtitle plugin, which helped in implementing subtitle handling
in VDR
John Kennedy <rkennedy@ix.netcom.com>
for publishing "A Fast Bresenham Algorithm For Drawing Ellipses" (found at
@ -1419,6 +1423,8 @@ Marco Schl
for fixing a problem with characters >0x7F in the modified version of skipspace()
for reporting a faulty comment in Make.config.template
for fixing checking for ttDolbyLast in cDevice::SetCurrentAudioTrack()
for fixing selecting the audio track when pressing Ok in the Audio menu
for implementing handling DVB subtitles
Jürgen Schmitz <j.schmitz@web.de>
for reporting a bug in displaying the current channel when switching via the SVDRP

29
HISTORY
View File

@ -5416,3 +5416,32 @@ Video Disk Recorder Revision History
function to hand through the Level.
- Fixed checking for ttDolbyLast in cDevice::SetCurrentAudioTrack() (thanks
to Marco Schlüßler).
2007-10-12: Version 1.5.10
- Implemented handling DVB subtitles (thanks to Marco Schlüßler, and also to
Pekka Virtanen for writing the subtitle plugin, which helped in implementing
subtitle handling in VDR).
- The new remote control key "Subtitles" can be used to bring up the list
of available subtitles.
- The new setup option "DVB/Subtitle languages" can be used to define the
preferred languages for subtitles.
- Fixed selecting the audio track when pressing Ok in the Audio menu (thanks
to Marco Schlüßler).
- Implemented display of DVB subtitles in live viewing mode.
- Implemented subtitle track selection.
- Implemented bitmap color reduction and shrinking to display subtitles even
on devices that can't display the necessary number of colors.
- Added compatibility mode for playback of recordings made with the subtitles
plugin (with some help from Rolf Ahrenberg).
- The new setup option "DVB/Subtitle offset" can be used to shift the location
of the subtitles in the vertical direction.
- The new setup options "DVB/Subtitle foreground/background transparency"
define an additional level of transparency for the foreground and background
color of subtitles.
- Existing recordings made with the subtitle plugin can be given an 'X' record
in their info.vdr file, so that subtitles can be automatically selected upon
replay, according to the preferred language setup, as in
X 3 03 ger deutsch
(see vdr.5). Note that these entries need to be added in the proper sequence,
so that they correspond with the actual track languages in the recording.

26
MANUAL
View File

@ -59,6 +59,7 @@ Version 1.4
Mute mute
Audio select audio track
Subtitles select subtitles
Schedule \
Channels |
@ -675,6 +676,31 @@ Version 1.4
many "Audio language" options which allow you to select the
individual preferred languages.
Display subtitles = no If set to 'yes', the first available subtitles in the list
of preferred subtitle languages will be turned on when
switching to a channel that provides subtitles.
Subtitle languages = 0 Some tv stations broadcast various subtitle tracks in different
languages. This option allows you to define which language(s)
you prefer in such cases. By default, or if none of the
preferred languages is broadcast, no subtitles will
be selected when switching to such a channel. If this option
is set to a non-zero value, the menu page will contain that
many "Subtitle language" options which allow you to select the
individual preferred languages.
Subtitle offset = 0 Allows you to shift the location of the subtitles in the
vertical direction. The valid range is -50...50. This option
is only avialable if "Display subtitles" is set to 'yes'.
Subtitle foreground transparency = 0
Subtitle background transparency = 0
These define an additional level of transparency for the
foreground and background color of subtitles. Valid ranges
are 0...9 for foreground transparency, and 0...10 for
background transparency. By default the values as broadcast
are used.
LNB:
SLOF = 11700 The switching frequency (in MHz) between low and

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.106 2007/08/25 08:52:17 kls Exp $
# $Id: Makefile 1.107 2007/09/01 12:19:53 kls Exp $
.DELETE_ON_ERROR:
@ -37,7 +37,7 @@ DOXYFILE = Doxyfile
SILIB = $(LSIDIR)/libsi.a
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
dvbplayer.o dvbspu.o dvbsubtitle.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: channels.c 1.54 2007/07/21 14:55:01 kls Exp $
* $Id: channels.c 1.55 2007/10/12 14:40:53 kls Exp $
*/
#include "channels.h"
@ -440,12 +440,12 @@ static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[]
return q - s;
}
void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int Tpid)
void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
{
int mod = CHANNELMOD_NONE;
if (vpid != Vpid || ppid != Ppid || tpid != Tpid)
mod |= CHANNELMOD_PIDS;
int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs);
int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs);
if (m & STRDIFF)
mod |= CHANNELMOD_LANGS;
if (m & VALDIFF)
@ -468,7 +468,16 @@ void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE
q += IntArrayToString(q, Dpids, 10, DLangs);
}
*q = 0;
dsyslog("changing pids of channel %d from %d+%d:%s:%d to %d+%d:%s:%d", Number(), vpid, ppid, OldApidsBuf, tpid, Vpid, Ppid, NewApidsBuf, Tpid);
const int SBufferSize = MAXSPIDS * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia
char OldSpidsBuf[SBufferSize];
char NewSpidsBuf[SBufferSize];
q = OldSpidsBuf;
q += IntArrayToString(q, spids, 10, slangs);
*q = 0;
q = NewSpidsBuf;
q += IntArrayToString(q, Spids, 10, SLangs);
*q = 0;
dsyslog("changing pids of channel %d from %d+%d:%s:%s:%d to %d+%d:%s:%s:%d", Number(), vpid, ppid, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, NewApidsBuf, NewSpidsBuf, Tpid);
vpid = Vpid;
ppid = Ppid;
for (int i = 0; i < MAXAPIDS; i++) {
@ -481,6 +490,11 @@ void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE
strn0cpy(dlangs[i], DLangs[i], MAXLANGCODE2);
}
dpids[MAXDPIDS] = 0;
for (int i = 0; i < MAXSPIDS; i++) {
spids[i] = Spids[i];
strn0cpy(slangs[i], SLangs[i], MAXLANGCODE2);
}
spids[MAXSPIDS] = 0;
tpid = Tpid;
modification |= mod;
Channels.SetModified();

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.44 2007/07/21 14:58:36 kls Exp $
* $Id: channels.h 1.45 2007/09/02 10:23:11 kls Exp $
*/
#ifndef __CHANNELS_H
@ -33,7 +33,7 @@
#define MAXAPIDS 32 // audio
#define MAXDPIDS 16 // dolby (AC3 + DTS)
#define MAXSPIDS 8 // subtitles
#define MAXSPIDS 32 // subtitles
#define MAXCAIDS 8 // conditional access
#define MAXLANGCODE1 4 // a 3 letter language code, zero terminated
@ -212,7 +212,7 @@ public:
void SetId(int Nid, int Tid, int Sid, int Rid = 0);
void SetName(const char *Name, const char *ShortName, const char *Provider);
void SetPortalName(const char *PortalName);
void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int Tpid);
void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
void SetCaIds(const int *CaIds); // list must be zero-terminated
void SetCaDescriptors(int Level);
void SetLinkChannels(cLinkChannels *LinkChannels);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.156 2007/08/12 12:09:37 kls Exp $
* $Id: config.c 1.157 2007/10/06 14:28:58 kls Exp $
*/
#include "config.h"
@ -236,6 +236,11 @@ cSetup::cSetup(void)
MarginStart = 2;
MarginStop = 10;
AudioLanguages[0] = -1;
DisplaySubtitles = 0;
SubtitleLanguages[0] = -1;
SubtitleOffset = 0;
SubtitleFgTransparency = 0;
SubtitleBgTransparency = 0;
EPGLanguages[0] = -1;
EPGScanTimeout = 5;
EPGBugfixLevel = 3;
@ -406,6 +411,11 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
else if (!strcasecmp(Name, "AudioLanguages")) return ParseLanguages(Value, AudioLanguages);
else if (!strcasecmp(Name, "DisplaySubtitles")) DisplaySubtitles = atoi(Value);
else if (!strcasecmp(Name, "SubtitleLanguages")) return ParseLanguages(Value, SubtitleLanguages);
else if (!strcasecmp(Name, "SubtitleOffset")) SubtitleOffset = atoi(Value);
else if (!strcasecmp(Name, "SubtitleFgTransparency")) SubtitleFgTransparency = atoi(Value);
else if (!strcasecmp(Name, "SubtitleBgTransparency")) SubtitleBgTransparency = atoi(Value);
else if (!strcasecmp(Name, "EPGLanguages")) return ParseLanguages(Value, EPGLanguages);
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
@ -483,6 +493,11 @@ bool cSetup::Save(void)
Store("MarginStart", MarginStart);
Store("MarginStop", MarginStop);
StoreLanguages("AudioLanguages", AudioLanguages);
Store("DisplaySubtitles", DisplaySubtitles);
StoreLanguages("SubtitleLanguages", SubtitleLanguages);
Store("SubtitleOffset", SubtitleOffset);
Store("SubtitleFgTransparency", SubtitleFgTransparency);
Store("SubtitleBgTransparency", SubtitleBgTransparency);
StoreLanguages("EPGLanguages", EPGLanguages);
Store("EPGScanTimeout", EPGScanTimeout);
Store("EPGBugfixLevel", EPGBugfixLevel);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.298 2007/08/19 16:02:50 kls Exp $
* $Id: config.h 1.299 2007/10/06 14:27:18 kls Exp $
*/
#ifndef __CONFIG_H
@ -22,13 +22,13 @@
// VDR's own version number:
#define VDRVERSION "1.5.9"
#define VDRVERSNUM 10509 // Version * 10000 + Major * 100 + Minor
#define VDRVERSION "1.5.10"
#define VDRVERSNUM 10510 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number:
#define APIVERSION "1.5.9"
#define APIVERSNUM 10509 // Version * 10000 + Major * 100 + Minor
#define APIVERSION "1.5.10"
#define APIVERSNUM 10510 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to
@ -220,6 +220,10 @@ public:
int TimeTransponder;
int MarginStart, MarginStop;
int AudioLanguages[I18N_MAX_LANGUAGES + 1];
int DisplaySubtitles;
int SubtitleLanguages[I18N_MAX_LANGUAGES + 1];
int SubtitleOffset;
int SubtitleFgTransparency, SubtitleBgTransparency;
int EPGLanguages[I18N_MAX_LANGUAGES + 1];
int EPGScanTimeout;
int EPGBugfixLevel;

182
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.142 2007/08/26 11:11:42 kls Exp $
* $Id: device.c 1.143 2007/10/12 14:27:30 kls Exp $
*/
#include "device.h"
@ -19,6 +19,75 @@
#include "status.h"
#include "transfer.h"
// --- cLiveSubtitle ---------------------------------------------------------
#define LIVESUBTITLEBUFSIZE KILOBYTE(100)
class cLiveSubtitle : public cReceiver, public cThread {
private:
cRingBufferLinear *ringBuffer;
cRemux *remux;
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
cLiveSubtitle(int SPid);
virtual ~cLiveSubtitle();
};
cLiveSubtitle::cLiveSubtitle(int SPid)
:cReceiver(tChannelID(), -1, SPid)
,cThread("live subtitle")
{
ringBuffer = new cRingBufferLinear(LIVESUBTITLEBUFSIZE, TS_SIZE * 2, true, "Live Subtitle");
int NoPids = 0;
int SPids[] = { SPid, 0 };
remux = new cRemux(0, &NoPids, &NoPids, SPids);
}
cLiveSubtitle::~cLiveSubtitle()
{
cReceiver::Detach();
delete remux;
delete ringBuffer;
}
void cLiveSubtitle::Activate(bool On)
{
if (On)
Start();
else
Cancel(3);
}
void cLiveSubtitle::Receive(uchar *Data, int Length)
{
if (Running()) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p);
}
}
void cLiveSubtitle::Action(void)
{
while (Running()) {
int Count;
uchar *b = ringBuffer->Get(Count);
if (b) {
Count = remux->Put(b, Count);
if (Count)
ringBuffer->Del(Count);
}
b = remux->Get(Count);
if (b) {
Count = cDevice::PrimaryDevice()->PlaySubtitle(b, Count);
remux->Del(Count);
}
}
}
// --- cPesAssembler ---------------------------------------------------------
class cPesAssembler {
@ -172,6 +241,10 @@ cDevice::cDevice(void)
ClrAvailableTracks();
currentAudioTrack = ttNone;
currentAudioTrackMissingCount = 0;
currentSubtitleTrack = ttNone;
liveSubtitle = NULL;
dvbSubtitleConverter = NULL;
autoSelectPreferredSubtitleLanguage = true;
for (int i = 0; i < MAXRECEIVERS; i++)
receiver[i] = NULL;
@ -186,6 +259,8 @@ cDevice::~cDevice()
{
Detach(player);
DetachAllReceivers();
delete liveSubtitle;
delete dvbSubtitleConverter;
delete nitFilter;
delete sdtFilter;
delete patFilter;
@ -674,8 +749,11 @@ bool cDevice::SwitchChannel(int Direction)
eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
{
if (LiveView)
if (LiveView) {
StopReplay();
DELETENULL(liveSubtitle);
DELETENULL(dvbSubtitleConverter);
}
cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
@ -735,8 +813,11 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
for (int i = 0; i < MAXDPIDS; i++)
SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
}
for (int i = 0; i < MAXSPIDS; i++)
SetAvailableTrack(ttSubtitle, i, Channel->Spid(i), Channel->Slang(i));
if (!NeedsTransferMode)
EnsureAudioTrack(true);
EnsureSubtitleTrack();
}
cStatus::MsgChannelSwitch(this, Channel->Number()); // only report status if channel switch successfull
}
@ -848,6 +929,7 @@ void cDevice::ClrAvailableTracks(bool DescriptionsOnly, bool IdsOnly)
SetAudioChannel(0); // fall back to stereo
currentAudioTrackMissingCount = 0;
currentAudioTrack = ttNone;
currentSubtitleTrack = ttNone;
}
}
@ -855,18 +937,23 @@ bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const c
{
eTrackType t = eTrackType(Type + Index);
if (Type == ttAudio && IS_AUDIO_TRACK(t) ||
Type == ttDolby && IS_DOLBY_TRACK(t)) {
Type == ttDolby && IS_DOLBY_TRACK(t) ||
Type == ttSubtitle && IS_SUBTITLE_TRACK(t)) {
if (Language)
strn0cpy(availableTracks[t].language, Language, sizeof(availableTracks[t].language));
if (Description)
Utf8Strn0Cpy(availableTracks[t].description, Description, sizeof(availableTracks[t].description));
if (Id) {
availableTracks[t].id = Id; // setting 'id' last to avoid the need for extensive locking
int numAudioTracks = NumAudioTracks();
if (!availableTracks[currentAudioTrack].id && numAudioTracks && currentAudioTrackMissingCount++ > numAudioTracks * 10)
EnsureAudioTrack();
else if (t == currentAudioTrack)
currentAudioTrackMissingCount = 0;
if (Type == ttAudio || Type == ttDolby) {
int numAudioTracks = NumAudioTracks();
if (!availableTracks[currentAudioTrack].id && numAudioTracks && currentAudioTrackMissingCount++ > numAudioTracks * 10)
EnsureAudioTrack();
else if (t == currentAudioTrack)
currentAudioTrackMissingCount = 0;
}
else if (Type == ttSubtitle && autoSelectPreferredSubtitleLanguage)
EnsureSubtitleTrack();
}
return true;
}
@ -880,16 +967,26 @@ const tTrackId *cDevice::GetTrack(eTrackType Type)
return (ttNone < Type && Type < ttMaxTrackTypes) ? &availableTracks[Type] : NULL;
}
int cDevice::NumAudioTracks(void) const
int cDevice::NumTracks(eTrackType FirstTrack, eTrackType LastTrack) const
{
int n = 0;
for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
for (int i = FirstTrack; i <= LastTrack; i++) {
if (availableTracks[i].id)
n++;
}
return n;
}
int cDevice::NumAudioTracks(void) const
{
return NumTracks(ttAudioFirst, ttDolbyLast);
}
int cDevice::NumSubtitleTracks(void) const
{
return NumTracks(ttSubtitleFirst, ttSubtitleLast);
}
bool cDevice::SetCurrentAudioTrack(eTrackType Type)
{
if (ttNone < Type && Type <= ttDolbyLast) {
@ -908,6 +1005,30 @@ bool cDevice::SetCurrentAudioTrack(eTrackType Type)
return false;
}
bool cDevice::SetCurrentSubtitleTrack(eTrackType Type, bool Manual)
{
if (Type == ttNone || IS_SUBTITLE_TRACK(Type)) {
currentSubtitleTrack = Type;
autoSelectPreferredSubtitleLanguage = !Manual;
if (dvbSubtitleConverter)
dvbSubtitleConverter->Reset();
if (Type == ttNone && dvbSubtitleConverter) {
cMutexLock MutexLock(&mutexCurrentSubtitleTrack);
DELETENULL(dvbSubtitleConverter);
}
DELETENULL(liveSubtitle);
if (currentSubtitleTrack != ttNone && !Replaying() && !Transferring()) {
const tTrackId *TrackId = GetTrack(currentSubtitleTrack);
if (TrackId && TrackId->id) {
liveSubtitle = new cLiveSubtitle(TrackId->id);
AttachReceiver(liveSubtitle);
}
}
return true;
}
return false;
}
void cDevice::EnsureAudioTrack(bool Force)
{
if (Force || !availableTracks[currentAudioTrack].id) {
@ -939,6 +1060,25 @@ void cDevice::EnsureAudioTrack(bool Force)
}
}
void cDevice::EnsureSubtitleTrack(void)
{
if (Setup.DisplaySubtitles) {
eTrackType PreferredTrack = ttSubtitleFirst;
int LanguagePreference = -1;
for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
const tTrackId *TrackId = GetTrack(eTrackType(i));
if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference))
PreferredTrack = eTrackType(i);
}
// Make sure we're set to an available subtitle track:
const tTrackId *Track = GetTrack(GetCurrentSubtitleTrack());
if (!Track || !Track->id || PreferredTrack != GetCurrentSubtitleTrack())
SetCurrentSubtitleTrack(PreferredTrack);
}
else
SetCurrentSubtitleTrack(ttNone);
}
bool cDevice::CanReplay(void) const
{
return HasDecoder();
@ -961,6 +1101,8 @@ void cDevice::TrickSpeed(int Speed)
void cDevice::Clear(void)
{
Audios.ClearAudio();
if (dvbSubtitleConverter)
dvbSubtitleConverter->Reset();
}
void cDevice::Play(void)
@ -1016,6 +1158,9 @@ void cDevice::Detach(cPlayer *Player)
player = NULL; // avoids recursive calls to Detach()
p->Activate(false);
p->device = NULL;
cMutexLock MutexLock(&mutexCurrentSubtitleTrack);
delete dvbSubtitleConverter;
dvbSubtitleConverter = NULL;
SetPlayMode(pmNone);
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
Audios.ClearAudio();
@ -1051,6 +1196,13 @@ int cDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
return -1;
}
int cDevice::PlaySubtitle(const uchar *Data, int Length)
{
if (!dvbSubtitleConverter)
dvbSubtitleConverter = new cDvbSubtitleConverter;
return dvbSubtitleConverter->Convert(Data, Length);
}
int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
{
cMutexLock MutexLock(&mutexCurrentAudioTrack);
@ -1076,6 +1228,11 @@ int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
break;
case 0xBD: { // private stream 1
int PayloadOffset = Data[8] + 9;
// Compatibility mode for old subtitles plugin:
if ((Data[PayloadOffset - 3] & 0x81) == 1 && Data[PayloadOffset - 2] == 0x81)
PayloadOffset--;
uchar SubStreamId = Data[PayloadOffset];
uchar SubStreamType = SubStreamId & 0xF0;
uchar SubStreamIndex = SubStreamId & 0x1F;
@ -1090,6 +1247,9 @@ pre_1_3_19_PrivateStreamDeteced:
switch (SubStreamType) {
case 0x20: // SPU
case 0x30: // SPU
SetAvailableTrack(ttSubtitle, SubStreamIndex, SubStreamId);
if (!VideoOnly && currentSubtitleTrack != ttNone && SubStreamId == availableTracks[currentSubtitleTrack].id)
w = PlaySubtitle(Start, d);
break;
case 0x80: // AC3 & DTS
if (Setup.UseDolbyDigital) {
@ -1139,6 +1299,8 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
{
if (!Data) {
pesAssembler->Reset();
if (dvbSubtitleConverter)
dvbSubtitleConverter->Reset();
return 0;
}
int Result = 0;

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.82 2007/07/22 11:20:13 kls Exp $
* $Id: device.h 1.83 2007/10/12 13:53:50 kls Exp $
*/
#ifndef __DEVICE_H
@ -12,6 +12,7 @@
#include "channels.h"
#include "ci.h"
#include "dvbsubtitle.h"
#include "eit.h"
#include "filter.h"
#include "nit.h"
@ -70,16 +71,15 @@ enum eTrackType { ttNone,
ttDolby,
ttDolbyFirst = ttDolby,
ttDolbyLast = ttDolbyFirst + 15, // MAXDPIDS - 1
/* future...
ttSubtitle,
ttSubtitleFirst = ttSubtitle,
ttSubtitleLast = ttSubtitleFirst + 7, // MAXSPIDS - 1
*/
ttSubtitleLast = ttSubtitleFirst + 31, // MAXSPIDS - 1
ttMaxTrackTypes
};
#define IS_AUDIO_TRACK(t) (ttAudioFirst <= (t) && (t) <= ttAudioLast)
#define IS_DOLBY_TRACK(t) (ttDolbyFirst <= (t) && (t) <= ttDolbyLast)
#define IS_SUBTITLE_TRACK(t) (ttSubtitleFirst <= (t) && (t) <= ttSubtitleLast)
struct tTrackId {
uint16_t id; // The PES packet id or the PID.
@ -90,10 +90,12 @@ struct tTrackId {
class cPlayer;
class cReceiver;
class cPesAssembler;
class cLiveSubtitle;
/// The cDevice class is the base from which actual devices can be derived.
class cDevice : public cThread {
friend class cLiveSubtitle;
private:
static int numDevices;
static int useDevice;
@ -185,6 +187,9 @@ public:
// SPU facilities
private:
cLiveSubtitle *liveSubtitle;
cDvbSubtitleConverter *dvbSubtitleConverter;
public:
virtual cSpuDecoder *GetSpuDecoder(void);
///< Returns a pointer to the device's SPU decoder (or NULL, if this
@ -362,8 +367,11 @@ public:
private:
tTrackId availableTracks[ttMaxTrackTypes];
eTrackType currentAudioTrack;
eTrackType currentSubtitleTrack;
cMutex mutexCurrentAudioTrack;
cMutex mutexCurrentSubtitleTrack;
int currentAudioTrackMissingCount;
bool autoSelectPreferredSubtitleLanguage;
bool pre_1_3_19_PrivateStream;
protected:
virtual void SetAudioTrackDevice(eTrackType Type);
@ -384,18 +392,33 @@ public:
const tTrackId *GetTrack(eTrackType Type);
///< Returns a pointer to the given track id, or NULL if Type is not
///< less than ttMaxTrackTypes.
int NumTracks(eTrackType FirstTrack, eTrackType LastTrack) const;
///< Returns the number of tracks in the given range that are currently
///< available.
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.
int NumSubtitleTracks(void) const;
///< Returns the number of subtitle tracks that are currently available.
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.
eTrackType GetCurrentSubtitleTrack(void) { return currentSubtitleTrack; }
bool SetCurrentSubtitleTrack(eTrackType Type, bool Manual = false);
///< Sets the current subtitle track to the given Type.
///< IF Manual is true, no automatic preferred subtitle language selection
///< will be done for the rest of the current replay session, or until
///< the channel is changed.
///< \return Returns true if Type is a valid subtitle track, false otherwise.
void EnsureAudioTrack(bool Force = false);
///< Makes sure an audio track is selected that is actually available.
///< If Force is true, the language and Dolby Digital settings will
///< be verified even if the current audio track is available.
void EnsureSubtitleTrack(void);
///< Makes sure one of the preferred language subtitle tracks is selected.
///< Only has an effect if Setup.DisplaySubtitles is on.
// Audio facilities
@ -454,6 +477,13 @@ protected:
///< 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 PlaySubtitle(const uchar *Data, int Length);
///< Plays the given data block as a subtitle.
///< Data points to exactly one complete PES packet of the given Length.
///< PlaySubtitle() 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,

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbosd.c 1.31 2007/08/26 09:39:20 kls Exp $
* $Id: dvbosd.c 1.32 2007/09/16 08:55:54 kls Exp $
*/
#include "dvbosd.h"
@ -32,6 +32,7 @@ public:
cDvbOsd(int Left, int Top, int OsdDev, uint Level);
virtual ~cDvbOsd();
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas);
virtual void Flush(void);
};
@ -106,6 +107,19 @@ eOsdError cDvbOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
return Result;
}
eOsdError cDvbOsd::SetAreas(const tArea *Areas, int NumAreas)
{
if (shown) {
cBitmap *Bitmap;
for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
Cmd(OSD_SetWindow, 0, i + 1);
Cmd(OSD_Close);
}
shown = false;
}
return cOsd::SetAreas(Areas, NumAreas);
}
void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
{
if (osdDev >= 0) {

1029
dvbsubtitle.c Normal file

File diff suppressed because it is too large Load Diff

43
dvbsubtitle.h Normal file
View File

@ -0,0 +1,43 @@
/*
* dvbsubtitle.h: DVB subtitles
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* Original author: Marco Schlüßler <marco@lordzodiac.de>
*
* $Id: dvbsubtitle.h 1.1 2007/10/12 14:27:30 kls Exp $
*/
#ifndef __DVBSUBTITLE_H
#define __DVBSUBTITLE_H
#include "osd.h"
#include "thread.h"
#include "tools.h"
class cDvbSubtitlePage;
class cDvbSubtitleAssembler;
class cDvbSubtitleBitmaps;
class cDvbSubtitleConverter : public cThread {
private:
static int setupLevel;
cDvbSubtitleAssembler *dvbSubtitleAssembler;
cOsd *osd;
cList<cDvbSubtitlePage> *pages;
cList<cDvbSubtitleBitmaps> *bitmaps;
tColor yuv2rgb(int Y, int Cb, int Cr);
bool AssertOsd(void);
int ExtractSegment(const uchar *Data, int Length, int64_t Pts);
void FinishPage(cDvbSubtitlePage *Page);
public:
cDvbSubtitleConverter(void);
virtual ~cDvbSubtitleConverter();
void Action(void);
void Reset(void);
int Convert(const uchar *Data, int Length);
static void SetupChanged(void);
};
#endif //__DVBSUBTITLE_H

6
eit.c
View File

@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
* $Id: eit.c 1.125 2007/07/28 13:16:43 kls Exp $
* $Id: eit.c 1.126 2007/09/26 10:56:33 kls Exp $
*/
#include "eit.h"
@ -219,11 +219,11 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo
SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d;
uchar Stream = cd->getStreamContent();
uchar Type = cd->getComponentType();
if (1 <= Stream && Stream <= 2 && Type != 0) {
if (1 <= Stream && Stream <= 3 && Type != 0) { // 1=video, 2=audio, 3=subtitles
if (!Components)
Components = new cComponents;
char buffer[Utf8BufSize(256)];
Components->SetComponent(Components->NumComponents(), cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
Components->SetComponent(Components->NumComponents(), Stream, Type, I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
}
}
break;

3
keys.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: keys.c 1.15 2007/08/11 11:30:18 kls Exp $
* $Id: keys.c 1.16 2007/09/26 12:35:21 kls Exp $
*/
#include "keys.h"
@ -49,6 +49,7 @@ static tKey keyTable[] = { // "Up" and "Down" must be the first two keys!
{ kVolDn, trNOOP("Key$Volume-") },
{ kMute, trNOOP("Key$Mute") },
{ kAudio, trNOOP("Key$Audio") },
{ kSubtitles, trNOOP("Key$Subtitles") },
{ kSchedule, trNOOP("Key$Schedule") },
{ kChannels, trNOOP("Key$Channels") },
{ kTimers, trNOOP("Key$Timers") },

3
keys.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: keys.h 1.13 2007/08/24 13:15:48 kls Exp $
* $Id: keys.h 1.14 2007/09/26 12:34:50 kls Exp $
*/
#ifndef __KEYS_H
@ -43,6 +43,7 @@ enum eKeys { // "Up" and "Down" must be the first two keys!
kVolDn,
kMute,
kAudio,
kSubtitles,
kSchedule,
kChannels,
kTimers,

162
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.461 2007/08/24 13:15:48 kls Exp $
* $Id: menu.c 1.462 2007/10/12 14:30:29 kls Exp $
*/
#include "menu.h"
@ -260,6 +260,8 @@ void cMenuEditChannel::Setup(void)
Add(new cMenuEditIntItem( tr("Apid2"), &data.apids[1], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpids[0], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpids[1], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Spid1"), &data.spids[0], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Spid2"), &data.spids[1], 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
Add(new cMenuEditCaItem( tr("CA"), &data.caids[0]));
Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF));
@ -2400,6 +2402,8 @@ class cMenuSetupDVB : public cMenuSetupBase {
private:
int originalNumAudioLanguages;
int numAudioLanguages;
int originalNumSubtitleLanguages;
int numSubtitleLanguages;
void Setup(void);
const char *videoDisplayFormatTexts[3];
const char *updateChannelsTexts[6];
@ -2412,7 +2416,10 @@ cMenuSetupDVB::cMenuSetupDVB(void)
{
for (numAudioLanguages = 0; numAudioLanguages < I18nLanguages()->Size() && data.AudioLanguages[numAudioLanguages] >= 0; numAudioLanguages++)
;
for (numSubtitleLanguages = 0; numSubtitleLanguages < I18nLanguages()->Size() && data.SubtitleLanguages[numSubtitleLanguages] >= 0; numSubtitleLanguages++)
;
originalNumAudioLanguages = numAudioLanguages;
originalNumSubtitleLanguages = numSubtitleLanguages;
videoDisplayFormatTexts[0] = tr("pan&scan");
videoDisplayFormatTexts[1] = tr("letterbox");
videoDisplayFormatTexts[2] = tr("center cut out");
@ -2442,6 +2449,15 @@ void cMenuSetupDVB::Setup(void)
Add(new cMenuEditIntItem( tr("Setup.DVB$Audio languages"), &numAudioLanguages, 0, I18nLanguages()->Size()));
for (int i = 0; i < numAudioLanguages; i++)
Add(new cMenuEditStraItem(tr("Setup.DVB$Audio language"), &data.AudioLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
Add(new cMenuEditBoolItem(tr("Setup.DVB$Display subtitles"), &data.DisplaySubtitles));
if (data.DisplaySubtitles) {
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle languages"), &numSubtitleLanguages, 0, I18nLanguages()->Size()));
for (int i = 0; i < numSubtitleLanguages; i++)
Add(new cMenuEditStraItem(tr("Setup.DVB$Subtitle language"), &data.SubtitleLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle offset"), &data.SubtitleOffset, -50, 50));
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle foreground transparency"), &data.SubtitleFgTransparency, 0, 9));
Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle background transparency"), &data.SubtitleBgTransparency, 0, 10));
}
SetCurrent(Get(current));
Display();
@ -2453,11 +2469,15 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
int oldVideoDisplayFormat = ::Setup.VideoDisplayFormat;
bool oldVideoFormat = ::Setup.VideoFormat;
bool newVideoFormat = data.VideoFormat;
bool oldDisplaySubtitles = ::Setup.DisplaySubtitles;
bool newDisplaySubtitles = data.DisplaySubtitles;
int oldnumAudioLanguages = numAudioLanguages;
int oldnumSubtitleLanguages = numSubtitleLanguages;
eOSState state = cMenuSetupBase::ProcessKey(Key);
if (Key != kNone) {
bool DoSetup = data.VideoFormat != newVideoFormat;
DoSetup |= data.DisplaySubtitles != newDisplaySubtitles;
if (numAudioLanguages != oldnumAudioLanguages) {
for (int i = oldnumAudioLanguages; i < numAudioLanguages; i++) {
data.AudioLanguages[i] = 0;
@ -2476,6 +2496,24 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
data.AudioLanguages[numAudioLanguages] = -1;
DoSetup = true;
}
if (numSubtitleLanguages != oldnumSubtitleLanguages) {
for (int i = oldnumSubtitleLanguages; i < numSubtitleLanguages; i++) {
data.SubtitleLanguages[i] = 0;
for (int l = 0; l < I18nLanguages()->Size(); l++) {
int k;
for (k = 0; k < oldnumSubtitleLanguages; k++) {
if (data.SubtitleLanguages[k] == l)
break;
}
if (k >= oldnumSubtitleLanguages) {
data.SubtitleLanguages[i] = l;
break;
}
}
}
data.SubtitleLanguages[numSubtitleLanguages] = -1;
DoSetup = true;
}
if (DoSetup)
Setup();
}
@ -2486,6 +2524,9 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
cDevice::PrimaryDevice()->SetVideoDisplayFormat(eVideoDisplayFormat(::Setup.VideoDisplayFormat));
if (::Setup.VideoFormat != oldVideoFormat)
cDevice::PrimaryDevice()->SetVideoFormat(::Setup.VideoFormat);
if (::Setup.DisplaySubtitles != oldDisplaySubtitles)
cDevice::PrimaryDevice()->EnsureSubtitleTrack();
cDvbSubtitleConverter::SetupChanged();
}
return state;
}
@ -3128,14 +3169,18 @@ static void SetTrackDescriptions(int LiveChannel)
if (Components) {
int indexAudio = 0;
int indexDolby = 0;
int indexSubtitle = 0;
for (int i = 0; i < Components->NumComponents(); i++) {
const tComponent *p = Components->Component(i);
if (p->stream == 2) {
if (p->type == 0x05)
cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, LiveChannel ? NULL : p->language, p->description);
else
cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, LiveChannel ? NULL : p->language, p->description);
}
switch (p->stream) {
case 2: if (p->type == 0x05)
cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, LiveChannel ? NULL : p->language, p->description);
else
cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, LiveChannel ? NULL : p->language, p->description);
break;
case 3: cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, indexSubtitle++, 0, LiveChannel ? NULL : p->language, p->description);
break;
}
}
}
}
@ -3491,7 +3536,7 @@ cDisplayTracks::cDisplayTracks(void)
numTracks++;
}
}
descriptions[numTracks] = 0;
descriptions[numTracks] = NULL;
timeout.Set(TRACKTIMEOUT);
displayTracks = Skins.Current()->DisplayTracks(tr("Button$Audio"), numTracks, descriptions);
Show();
@ -3569,7 +3614,7 @@ eOSState cDisplayTracks::ProcessKey(eKeys Key)
timeout.Set(TRACKTIMEOUT);
break;
case kOk:
if (track != cDevice::PrimaryDevice()->GetCurrentAudioTrack())
if (types[track] != cDevice::PrimaryDevice()->GetCurrentAudioTrack())
oldTrack = -1; // make sure we explicitly switch to that track
timeout.Set();
break;
@ -3588,6 +3633,105 @@ eOSState cDisplayTracks::ProcessKey(eKeys Key)
return timeout.TimedOut() ? osEnd : osContinue;
}
// --- cDisplaySubtitleTracks ------------------------------------------------
cDisplaySubtitleTracks *cDisplaySubtitleTracks::currentDisplayTracks = NULL;
cDisplaySubtitleTracks::cDisplaySubtitleTracks(void)
:cOsdObject(true)
{
SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannel() : 0);
currentDisplayTracks = this;
numTracks = track = 0;
types[numTracks] = ttNone;
descriptions[numTracks] = strdup(tr("No subtitles"));
numTracks++;
eTrackType CurrentSubtitleTrack = cDevice::PrimaryDevice()->GetCurrentSubtitleTrack();
for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
if (TrackId && TrackId->id) {
types[numTracks] = eTrackType(i);
descriptions[numTracks] = strdup(*TrackId->description ? TrackId->description : *TrackId->language ? TrackId->language : *itoa(i));
if (i == CurrentSubtitleTrack)
track = numTracks;
numTracks++;
}
}
descriptions[numTracks] = NULL;
timeout.Set(TRACKTIMEOUT);
displayTracks = Skins.Current()->DisplayTracks(tr("Button$Subtitles"), numTracks, descriptions);
Show();
}
cDisplaySubtitleTracks::~cDisplaySubtitleTracks()
{
delete displayTracks;
currentDisplayTracks = NULL;
for (int i = 0; i < numTracks; i++)
free(descriptions[i]);
cStatus::MsgOsdClear();
}
void cDisplaySubtitleTracks::Show(void)
{
displayTracks->SetTrack(track, descriptions);
displayTracks->Flush();
//cStatus::MsgSetSubtitleTrack(track, descriptions); //TODO better make a more general cStatus::MsgSetTrack(tr("Subtitles"), track, descriptions)
}
cDisplaySubtitleTracks *cDisplaySubtitleTracks::Create(void)
{
if (cDevice::PrimaryDevice()->NumSubtitleTracks() > 0) {
if (!currentDisplayTracks)
new cDisplaySubtitleTracks;
return currentDisplayTracks;
}
Skins.Message(mtWarning, tr("No subtitles available!"));
return NULL;
}
void cDisplaySubtitleTracks::Process(eKeys Key)
{
if (currentDisplayTracks)
currentDisplayTracks->ProcessKey(Key);
}
eOSState cDisplaySubtitleTracks::ProcessKey(eKeys Key)
{
int oldTrack = track;
switch (Key) {
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
case kDown:
if (NORMALKEY(Key) == kUp && track > 0)
track--;
else if (NORMALKEY(Key) == kDown && track < numTracks - 1)
track++;
timeout.Set(TRACKTIMEOUT);
break;
case kSubtitles|k_Repeat:
case kSubtitles:
if (++track >= numTracks)
track = 0;
timeout.Set(TRACKTIMEOUT);
break;
case kOk:
if (types[track] != cDevice::PrimaryDevice()->GetCurrentSubtitleTrack())
oldTrack = -1; // make sure we explicitly switch to that track
timeout.Set();
break;
case kNone: break;
default: if ((Key & k_Release) == 0)
return osEnd;
}
if (track != oldTrack) {
Show();
cDevice::PrimaryDevice()->SetCurrentSubtitleTrack(types[track], true);
}
return timeout.TimedOut() ? osEnd : osContinue;
}
// --- cRecordControl --------------------------------------------------------
cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)

20
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.88 2007/08/12 10:35:42 kls Exp $
* $Id: menu.h 1.89 2007/09/26 14:35:57 kls Exp $
*/
#ifndef __MENU_H
@ -128,6 +128,24 @@ public:
eOSState ProcessKey(eKeys Key);
};
class cDisplaySubtitleTracks : public cOsdObject {
private:
cSkinDisplayTracks *displayTracks;
cTimeMs timeout;
eTrackType types[ttMaxTrackTypes];
char *descriptions[ttMaxTrackTypes + 1]; // list is NULL terminated
int numTracks, track;
static cDisplaySubtitleTracks *currentDisplayTracks;
virtual void Show(void);
cDisplaySubtitleTracks(void);
public:
virtual ~cDisplaySubtitleTracks();
static bool IsOpen(void) { return currentDisplayTracks != NULL; }
static cDisplaySubtitleTracks *Create(void);
static void Process(eKeys Key);
eOSState ProcessKey(eKeys Key);
};
cOsdObject *CamControl(void);
class cMenuRecordingItem;

106
osd.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.c 1.74 2007/08/26 09:44:50 kls Exp $
* $Id: osd.c 1.75 2007/10/12 12:38:36 kls Exp $
*/
#include "osd.h"
@ -82,7 +82,7 @@ void cPalette::SetColor(int Index, tColor Color)
}
}
const tColor *cPalette::Colors(int &NumColors)
const tColor *cPalette::Colors(int &NumColors) const
{
NumColors = numColors;
return numColors ? color : NULL;
@ -112,7 +112,7 @@ void cPalette::Replace(const cPalette &Palette)
antiAliasGranularity = Palette.antiAliasGranularity;
}
tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level)
tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const
{
if (antiAliasGranularity > 0)
Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity);
@ -131,7 +131,7 @@ tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level)
return (A << 24) | (R << 16) | (G << 8) | B;
}
int cPalette::ClosestColor(tColor Color, int MaxDiff)
int cPalette::ClosestColor(tColor Color, int MaxDiff) const
{
int n = 0;
int d = INT_MAX;
@ -640,6 +640,79 @@ const tIndex *cBitmap::Data(int x, int y)
return &bitmap[y * width + x];
}
void cBitmap::ReduceBpp(const cPalette &Palette)
{
int NewBpp = Palette.Bpp();
if (Bpp() == 4 && NewBpp == 2) {
for (int i = width * height; i--; ) {
tIndex p = bitmap[i];
bitmap[i] = (p >> 2) | ((p & 0x03) != 0);
}
}
else if (Bpp() == 8) {
if (NewBpp == 2) {
for (int i = width * height; i--; ) {
tIndex p = bitmap[i];
bitmap[i] = (p >> 6) | ((p & 0x30) != 0);
}
}
else if (NewBpp == 4) {
for (int i = width * height; i--; ) {
tIndex p = bitmap[i];
bitmap[i] = p >> 4;
}
}
else
return;
}
else
return;
SetBpp(NewBpp);
Replace(Palette);
}
void cBitmap::ShrinkBpp(int NewBpp)
{
int NumOldColors;
const tColor *Colors = this->Colors(NumOldColors);
if (Colors) {
// Find the most frequently used colors and create a map table:
int Used[MAXNUMCOLORS] = { 0 };
int Map[MAXNUMCOLORS] = { 0 };
for (int i = width * height; i--; )
Used[bitmap[i]]++;
int MaxNewColors = (NewBpp == 4) ? 16 : 4;
cPalette NewPalette(NewBpp);
for (int i = 0; i < MaxNewColors; i++) {
int Max = 0;
int Index = -1;
for (int n = 0; n < NumOldColors; n++) {
if (Used[n] > Max) {
Max = Used[n];
Index = n;
}
}
if (Index >= 0) {
Used[Index] = 0;
Map[Index] = i;
NewPalette.SetColor(i, Colors[Index]);
}
else
break;
}
// Complete the map table for all other colors (will be set to closest match):
for (int n = 0; n < NumOldColors; n++) {
if (Used[n])
Map[n] = NewPalette.Index(Colors[n]);
}
// Do the actual index mapping:
for (int i = width * height; i--; )
bitmap[i] = Map[bitmap[i]];
SetBpp(NewBpp);
Replace(NewPalette);
}
}
// --- cOsd ------------------------------------------------------------------
int cOsd::osdLeft = 0;
@ -720,19 +793,18 @@ eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
{
eOsdError Result = oeUnknown;
if (numBitmaps == 0) {
Result = CanHandleAreas(Areas, NumAreas);
if (Result == oeOk) {
width = height = 0;
for (int i = 0; i < NumAreas; i++) {
bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
width = max(width, Areas[i].x2 + 1);
height = max(height, Areas[i].y2 + 1);
}
}
eOsdError Result = CanHandleAreas(Areas, NumAreas);
if (Result == oeOk) {
while (numBitmaps)
delete bitmaps[--numBitmaps];
width = height = 0;
for (int i = 0; i < NumAreas; i++) {
bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
width = max(width, Areas[i].x2 + 1);
height = max(height, Areas[i].y2 + 1);
}
}
if (Result != oeOk)
else
esyslog("ERROR: cOsd::SetAreas returned %d", Result);
return Result;
}
@ -818,7 +890,7 @@ cOsdProvider::~cOsdProvider()
cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level)
{
if (Level == 0 && cOsd::IsOpen())
if (Level == OSD_LEVEL_DEFAULT && cOsd::IsOpen())
esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
else if (osdProvider) {
cOsd *ActiveOsd = cOsd::Osds.Size() ? cOsd::Osds[0] : NULL;

33
osd.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.h 1.57 2007/08/26 09:45:38 kls Exp $
* $Id: osd.h 1.58 2007/10/12 14:28:44 kls Exp $
*/
#ifndef __OSD_H
@ -17,6 +17,9 @@
#include "font.h"
#include "tools.h"
#define OSD_LEVEL_DEFAULT 0
#define OSD_LEVEL_SUBTITLES 10
#define MAXNUMCOLORS 256
enum {
@ -69,7 +72,7 @@ public:
///< a single color combination, and may not be able to serve all
///< requested colors. By default the palette assumes there will be
///< 10 fixed colors and 10 color combinations.
int Bpp(void) { return bpp; }
int Bpp(void) const { return bpp; }
void Reset(void);
///< Resets the palette, making it contain no colors.
int Index(tColor Color);
@ -77,7 +80,7 @@ public:
///< If Color is not yet contained in this palette, it will be added if
///< there is a free slot. If the color can't be added to this palette,
///< the closest existing color will be returned.
tColor Color(int Index) { return Index < maxColors ? color[Index] : 0; }
tColor Color(int Index) const { return Index < maxColors ? color[Index] : 0; }
///< Returns the color at the given Index. If Index is outside the valid
///< range, 0 will be returned.
void SetBpp(int Bpp);
@ -87,7 +90,7 @@ public:
///< Sets the palette entry at Index to Color. If Index is larger than
///< the number of currently used entries in this palette, the entries
///< in between will have undefined values.
const tColor *Colors(int &NumColors);
const tColor *Colors(int &NumColors) const;
///< Returns a pointer to the complete color table and stores the
///< number of valid entries in NumColors. If no colors have been
///< stored yet, NumColors will be set to 0 and the function will
@ -102,14 +105,14 @@ public:
void Replace(const cPalette &Palette);
///< Replaces the colors of this palette with the colors from the given
///< palette.
tColor Blend(tColor ColorFg, tColor ColorBg, uint8_t Level);
tColor Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const;
///< Determines a color that consists of a linear blend between ColorFg
///< and ColorBg. If Level is 0, the result is ColorBg, if it is 255,
///< the result is ColorFg. If SetAntiAliasGranularity() has been called previously,
///< Level will be mapped to a limited range of levels that allow to make best
///< use of the palette entries.
int ClosestColor(tColor Color, int MaxDiff = INT_MAX);
///< Returns the index of a color in this paltte that is closest to the given
int ClosestColor(tColor Color, int MaxDiff = INT_MAX) const;
///< Returns the index of a color in this palette that is closest to the given
///< Color. MaxDiff can be used to control the maximum allowed color difference.
///< If no color with a maximum difference of MaxDiff can be found, -1 will
///< be returned. With the default value of INT_MAX, there will always be
@ -232,6 +235,17 @@ public:
///< Returns the address of the index byte at the given coordinates.
tColor GetColor(int x, int y) { return Color(*Data(x, y)); }
///< Returns the color at the given coordinates.
void ReduceBpp(const cPalette &Palette);
///< Reduces the color depth of the bitmap to that of the given Palette.
///< If Palette's color depth is not smaller than the bitmap's current
///< color depth, or if it is not one of 4bpp or 2bpp, nothing happens. After
///< reducing the color depth the current palette is replaced with
///< the given one.
void ShrinkBpp(int NewBpp);
///< Shrinks the color depth of the bitmap to NewBpp by keeping only
///< the 2^NewBpp most frequently used colors as defined in the current palette.
///< If NewBpp is not smaller than the bitmap's current color depth,
///< or if it is not one of 4bpp or 2bpp, nothing happens.
};
struct tArea {
@ -292,7 +306,7 @@ public:
///< This may be useful for plugins that determine the scaling of the
///< video image and need to scale the OSD accordingly to fit on the
///< screen.
static int IsOpen(void) { return Osds.Size() && Osds[0]->level == 0; }
static int IsOpen(void) { return Osds.Size() && Osds[0]->level == OSD_LEVEL_DEFAULT; }
///< Returns true if there is currently a level 0 OSD open.
int Left(void) { return left; }
int Top(void) { return top; }
@ -327,6 +341,7 @@ public:
///< If the OSD has been divided into several sub-areas, all areas that
///< are part of the rectangle that surrounds a given drawing operation
///< will be drawn into, with the proper offsets.
///< A new call overwrites any previous settings
virtual void SaveRegion(int x1, int y1, int x2, int y2);
///< Saves the region defined by the given coordinates for later restoration
///< through RestoreRegion(). Only one saved region can be active at any
@ -398,7 +413,7 @@ public:
cOsdProvider(void);
//XXX maybe parameter to make this one "sticky"??? (frame-buffer etc.)
virtual ~cOsdProvider();
static cOsd *NewOsd(int Left, int Top, uint Level = 0);
static cOsd *NewOsd(int Left, int Top, uint Level = OSD_LEVEL_DEFAULT);
///< Returns a pointer to a newly created cOsd object, which will be located
///< at the given coordinates. When the cOsd object is no longer needed, the
///< caller must delete it. If the OSD is already in use, or there is no OSD

27
pat.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.c 1.17 2007/01/07 14:41:55 kls Exp $
* $Id: pat.c 1.18 2007/09/02 10:44:19 kls Exp $
*/
#include "pat.h"
@ -331,11 +331,14 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
int Ppid = pmt.getPCRPid();
int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
int Dpids[MAXDPIDS + 1] = { 0 };
int Spids[MAXSPIDS + 1] = { 0 };
char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" };
char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
int Tpid = 0;
int NumApids = 0;
int NumDpids = 0;
int NumSpids = 0;
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) {
switch (stream.getStreamType()) {
case 1: // STREAMTYPE_11172_VIDEO
@ -387,6 +390,26 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
case SI::AC3DescriptorTag:
dpid = stream.getPid();
break;
case SI::SubtitlingDescriptorTag:
if (NumSpids < MAXSPIDS) {
Spids[NumSpids] = stream.getPid();
SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
SI::SubtitlingDescriptor::Subtitling sub;
char *s = SLangs[NumSpids];
int n = 0;
for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
if (sub.languageCode[0]) {
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
NumSpids++;
}
break;
case SI::TeletextDescriptorTag:
Tpid = stream.getPid();
break;
@ -416,7 +439,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
}
}
if (Setup.UpdateChannels >= 2) {
Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Tpid);
Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
Channel->SetCaIds(CaDescriptors->CaIds());
}
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.c 1.154 2007/06/17 13:10:12 kls Exp $
* $Id: recording.c 1.155 2007/10/12 14:48:20 kls Exp $
*/
#include "recording.h"
@ -296,6 +296,17 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
strn0cpy(Component->language, s, sizeof(Component->language));
}
}
// The same applies to subtitles:
for (int i = 0; i < MAXSPIDS; i++) {
const char *s = Channel->Slang(i);
if (*s) {
tComponent *Component = Components->GetComponent(i, 3, 3);
if (!Component)
Components->SetComponent(Components->NumComponents(), 3, 3, s, NULL);
else if (strlen(s) > strlen(Component->language))
strn0cpy(Component->language, s, sizeof(Component->language));
}
}
if (Components != event->Components())
((cEvent *)event)->SetComponents(Components);
}

30
remux.c
View File

@ -11,7 +11,7 @@
* The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
*
* $Id: remux.c 1.58 2007/02/24 16:36:10 kls Exp $
* $Id: remux.c 1.59 2007/09/22 12:08:22 kls Exp $
*/
#include "remux.h"
@ -1555,6 +1555,13 @@ void cTS2PES::send_ipack(void)
buf[7] = 0x00;
buf[8] = 0x00;
count = 9;
if (!repacker && subStreamId) {
buf[9] = subStreamId;
buf[10] = 1;
buf[11] = 0;
buf[12] = 1;
count = 13;
}
break;
case 1:
buf[6] = 0x0F;
@ -1766,6 +1773,19 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
return;
}
if (!repacker && subStreamId) {
while (c < Count && found < (hlength + 9) && found < plength + 6) {
write_ipack(Buf + c, 1);
c++;
found++;
}
if (found == (hlength + 9)) {
uchar sbuf[] = { 0x01, 0x00, 0x00 };
write_ipack(&subStreamId, 1);
write_ipack(sbuf, 3);
}
}
while (c < Count && found < plength + 6) {
int l = Count - c;
if (l + found > plength + 6)
@ -1856,7 +1876,7 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure)
{
exitOnFailure = ExitOnFailure;
isRadio = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
noVideo = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
numUPTerrors = 0;
synced = false;
skipped = 0;
@ -1888,13 +1908,11 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS)
ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker);
}
/* future...
if (SPids) {
int n = 0;
while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x28 + n++);
ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x20 + n++);
}
*/
}
cRemux::~cRemux()
@ -2080,7 +2098,7 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
l = GetPacketLength(data, resultCount, i);
if (l < 0)
return resultData;
if (isRadio) {
if (noVideo) {
if (!synced) {
if (PictureType)
*PictureType = I_FRAME;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: remux.h 1.16 2006/03/25 12:27:30 kls Exp $
* $Id: remux.h 1.17 2007/09/02 10:19:06 kls Exp $
*/
#ifndef __REMUX_H
@ -37,7 +37,7 @@ class cTS2PES;
class cRemux {
private:
bool exitOnFailure;
bool isRadio;
bool noVideo;
int numUPTerrors;
bool synced;
int skipped;

6
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 1.63 2007/08/05 12:58:35 kls Exp $
.\" $Id: vdr.5 1.64 2007/09/26 10:57:08 kls Exp $
.\"
.TH vdr 5 "07 Jan 2007" "1.4.5" "Video Disk Recorder Files"
.SH NAME
@ -557,6 +557,8 @@ a recording is split into several files. The contents of these files is
0xC0...0xDF for audio 1...32 (up to 32 audio tracks may occur).
Dolby Digital data is stored in packets with ids 0xBD ("Private Stream 1")
and substream ids 0x80...0x87.
DVB subtitle data is stored in packets with ids 0xBD ("Private Stream 1")
and substream ids 0x20...0x27.
.SS INDEX
The file \fIindex.vdr\fR (if present in a recording directory) contains
the (binary) index data into each of the the recording files
@ -646,7 +648,7 @@ l l.
<title> @is the title of the event
<short text> @is the short text of the event (typically the name of the episode etc.)
<description> @is the description of the event (any '|' characters will be interpreted as newlines)
<stream> @is the stream content (1 = video, 2 = audio)
<stream> @is the stream content (1 = video, 2 = audio, 3 = subtitles)
<type> @is the stream type according to ETSI EN 300 468
<language> @is the three letter language code (optionally two codes, separated by '+')
<descr> @is the description of this stream component

14
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.299 2007/08/25 08:51:13 kls Exp $
* $Id: vdr.c 1.300 2007/09/26 14:36:48 kls Exp $
*/
#include <getopt.h>
@ -1009,6 +1009,18 @@ int main(int argc, char *argv[])
cDisplayTracks::Process(key);
key = kNone;
break;
// Subtitle track control:
case kSubtitles:
if (cControl::Control())
cControl::Control()->Hide();
if (!cDisplaySubtitleTracks::IsOpen()) {
DELETE_MENU;
Menu = cDisplaySubtitleTracks::Create();
}
else
cDisplaySubtitleTracks::Process(key);
key = kNone;
break;
// Pausing live video:
case kPause:
if (!cControl::Control()) {