Index: keys.txt
===================================================================
--- keys.txt (.../branches/version-0.15.1) (revision 19)
+++ keys.txt (.../trunk) (revision 19)
@@ -52,6 +52,7 @@
- Left (if a jump amount is entered) to jump back that amount.
- Right (if a jump amount is entered) to jump ahead that amount.
- F8 to toggle the sleep timer 30m->1hr->1hr30m->2hr->Off
+- L to cycle through available languages
Without the stickykeys option selected:
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/NuppelVideoPlayer.h (.../trunk) (revision 19)
@@ -157,6 +157,7 @@
void DrawSlice(VideoFrame *frame, int x, int y, int w, int h);
bool GetRawAudioState(void);
+ void AudioStreamCountChanged(int oldcount, int newcount);
void AddAudioData(char *buffer, int len, long long timecode);
void AddAudioData(short int *lbuffer, short int *rbuffer, int samples,
long long timecode);
@@ -180,6 +181,10 @@
void Zoom(int direction);
int GetLetterbox(void);
+ bool CyclePreferredAudioStreamId(void);
+ int GetPreferredAudioStreamId(void);
+ void SetPreferredAudioStreamId(int id);
+
void ExposeEvent(void);
protected:
@@ -237,6 +242,8 @@
void ResetCC(void);
void UpdateCC(unsigned char *inpos);
+ void DisplayAudioChange();
+
void UpdateTimeDisplay(void);
void UpdateSeekAmount(bool up);
void UpdateEditSlider(void);
@@ -381,6 +388,8 @@
long long bookmarkseek;
+ int preferredAudioStreamId;
+
int consecutive_blanks;
int skipcommercials;
int autocommercialskip;
@@ -393,6 +402,8 @@
DecoderBase *decoder;
+ int displayStreamCount;
+
/* avsync stuff */
int lastaudiotime;
int delay;
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/NuppelVideoPlayer.cpp (.../trunk) (revision 19)
@@ -187,6 +187,9 @@
warplbuff = NULL;
warprbuff = NULL;
warpbuffsize = 0;
+
+ preferredAudioStreamId=0;
+ displayStreamCount=0;
}
NuppelVideoPlayer::~NuppelVideoPlayer(void)
@@ -1765,6 +1768,7 @@
VideoFrame *frame = videoOutput->GetLastShownFrame();
+ DisplayAudioChange();
if (cc)
ShowText();
@@ -1822,6 +1826,7 @@
}
else
{
+ DisplayAudioChange();
if (cc)
ShowText();
videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
@@ -1980,6 +1985,13 @@
m_playbackinfo->SetBookmark(0, m_db->db());
}
+ if(!livetv && m_db && m_playbackinfo)
+ preferredAudioStreamId=m_playbackinfo->GetPreferredLanguage(m_db->db());
+ if(preferredAudioStreamIdGetAudioStreamCount())
+ decoder->SetCurrentAudioStreamId(preferredAudioStreamId);
+ else
+ decoder->SetCurrentAudioStreamId(0);
+
LoadBlankList();
if (!blankMap.isEmpty())
{
@@ -2214,6 +2226,102 @@
}
}
+/**
+ * Cycle through all currently available streams.
+ * Returns true if something was changed.
+ */
+bool NuppelVideoPlayer::CyclePreferredAudioStreamId()
+{
+ int count = decoder->GetAudioStreamCount();
+ if(count<=1)
+ return false;
+
+ preferredAudioStreamId++;
+ if(preferredAudioStreamId>=count || preferredAudioStreamId<0)
+ preferredAudioStreamId=0;
+
+ decoder->SetCurrentAudioStreamId(preferredAudioStreamId);
+
+ if(!livetv && m_db && m_playbackinfo)
+ {
+ QMutexLocker lockit(m_db->mutex());
+ m_playbackinfo->SetPreferredLanguage(preferredAudioStreamId, m_db->db());
+ }
+
+ return true;
+}
+
+int NuppelVideoPlayer::GetPreferredAudioStreamId()
+{
+ return preferredAudioStreamId;
+}
+void NuppelVideoPlayer::SetPreferredAudioStreamId(int id)
+{
+ preferredAudioStreamId=id;
+ if(preferredAudioStreamIdGetAudioStreamCount())
+ decoder->SetCurrentAudioStreamId(preferredAudioStreamId);
+ else
+ decoder->SetCurrentAudioStreamId(0);
+}
+/**
+ * This function is for the audio decoder to tell the player
+ * the number of streams has changed. The player can then
+ * choose to make the user aware of this fact and/or fallback
+ * to a default stream. This is run on the decoder thread.
+ * See also DisplayAudioChange()
+ */
+void NuppelVideoPlayer::AudioStreamCountChanged(int oldcount, int newcount)
+{
+ if(!decoder)
+ return;
+ int current = decoder->GetCurrentAudioStreamId();
+ if(current>=newcount)
+ decoder->SetCurrentAudioStreamId(0);
+ if(preferredAudioStreamIdSetCurrentAudioStreamId(preferredAudioStreamId);
+
+ if(newcount>1 && newcount>oldcount)
+ displayStreamCount=newcount;
+ else
+ displayStreamCount=0;
+}
+
+/**
+ * Tell the user about an increase in the number
+ * of audio streams. This is run on the display thread.
+ */
+void NuppelVideoPlayer::DisplayAudioChange()
+{
+ int count = displayStreamCount;
+ if(count!=0)
+ {
+ displayStreamCount=0;
+ if(!disableaudio && osd)
+ {
+ QString text;
+ int streamId = decoder->GetCurrentAudioStreamId();
+ switch(streamId)
+ {
+ case 0: // default setting; tell the user about this capability
+ if(count>2)
+ text=QObject::tr("Multilingual");
+ else
+ text=QObject::tr("Language I/II");
+ break;
+
+ case 1: // display current setting, because language has changed
+ text=QObject::tr("Language II");
+ break;
+
+ default:
+ text=QObject::tr("Language %1 %").arg(streamId+1);
+ break;
+ }
+ osd->SetSettingsText(text, 4);
+ }
+ }
+}
+
void NuppelVideoPlayer::AddAudioData(short int *lbuffer, short int *rbuffer,
int samples, long long timecode)
{
Index: libs/libmythtv/decoderbase.h
===================================================================
--- libs/libmythtv/decoderbase.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/decoderbase.h (.../trunk) (revision 19)
@@ -22,6 +22,7 @@
watchingrecording = false;
nvr_enc = NULL;
lowbuffers = false;
+ currentAudioStreamId=0;
}
virtual ~DecoderBase() { }
@@ -59,6 +60,23 @@
virtual void SetPixelFormat(const int) { }
+ virtual int GetAudioStreamCount(void) { return 1; }
+ int GetCurrentAudioStreamId()
+ {
+ return ValidAudioStreamId(currentAudioStreamId) ? currentAudioStreamId:0;
+ }
+ virtual void SetCurrentAudioStreamId(int audioStreamId)
+ {
+ if(ValidAudioStreamId(audioStreamId))
+ currentAudioStreamId=audioStreamId;
+ }
+
+ private:
+ bool ValidAudioStreamId(int id)
+ {
+ return id>=0 && idGetNumSetting("AC3PassThru", false);
+
+ dual_language=false;
}
AvFormatDecoder::~AvFormatDecoder()
@@ -1021,6 +1023,15 @@
return retval;
}
+int AvFormatDecoder::GetAudioStreamCount(void)
+{
+ if(dual_language && audio_channels==2)
+ return 2;
+ else if(audio_channels>0)
+ return 1;
+ return 0;
+}
+
void AvFormatDecoder::GetFrame(int onlyvideo)
{
AVPacket *pkt = NULL;
@@ -1175,15 +1186,29 @@
audio_sampling_rate);
m_parent->ReinitAudio();
}
+ if(curstream->codec.avcodec_dual_language!=dual_language)
+ {
+ if(dual_language)
+ {
+ dual_language=false;
+ m_parent->AudioStreamCountChanged(2, 1);
+ }
+ else
+ {
+ dual_language=true;
+ m_parent->AudioStreamCountChanged(1, 2);
+ }
+ }
}
long long temppts = lastapts;
// calc for next frame
lastapts += (long long)((double)(data_size * 1000) /
audio_sample_size / audio_sampling_rate);
-
- m_parent->AddAudioData((char *)samples, data_size,
- temppts);
+ if(dual_language & audio_channels==2)
+ FilterAudioChannel((char *)samples, data_size);
+ m_parent->AddAudioData((char *)samples, data_size,
+ temppts);
break;
}
case CODEC_TYPE_VIDEO:
@@ -1317,6 +1342,38 @@
m_parent->SetFramesPlayed(framesPlayed);
}
+/**
+ * Filter out either the right or the left channel, based
+ * on currentAudioStreamId (0=left, 1=right)
+ */
+void AvFormatDecoder::FilterAudioChannel(char *buffer, int samples)
+{
+ const int channel = currentAudioStreamId==0 ? 0:1;
+ const int bits = 16;
+ int bit;
+ const char *from;
+ char *to;
+ const int samplesize = (bits/8)*2;
+ const int halfsample = bits/8;
+ int sample;
+ if(channel==0)
+ {
+ from=buffer;
+ to=buffer+halfsample;
+ }
+ else
+ {
+ from=buffer+halfsample;
+ to=buffer;
+ }
+
+ for(sample=0; sampleCyclePreferredAudioStreamId();
+ if(osd && !browsemode)
+ {
+ QString text;
+ if(changed)
+ {
+ int current = activenvp->GetPreferredAudioStreamId();
+ switch(current)
+ {
+ case 0:
+ text=tr("Language I");
+ break;
+
+ case 1:
+ text=tr("Language II");
+ break;
+
+ default:
+ text=QString( tr("Language %1 %") ).arg(current+1);
+ break;
+ }
+ }
+ else
+ {
+ text=tr("Monolingual");
+ }
+ osd->SetSettingsText(text, 3);
+ }
+}
+
+void TV::RecordPreferredLanguageForChannel(QString channel)
+{
+ if(m_db && activenvp)
+ {
+ QString thequery = QString("REPLACE INTO channellanguage (language, channel) "
+ "VALUES (%1, '%2')")
+ .arg(activenvp->GetPreferredAudioStreamId())
+ .arg(channel);
+ QSqlQuery query = m_db->db()->exec(thequery);
+ if(!query.isActive())
+ MythContext::DBError("WriteChannelLanguage", thequery);
+ }
+}
+void TV::RestorePreferredLanguageForChannel(QString channel)
+{
+ if(m_db && activenvp)
+ {
+ QString thequery = QString("SELECT language FROM channellanguage "
+ "WHERE channel = '%1'").arg(channel);
+ QSqlQuery query = m_db->db()->exec(thequery);
+ if(query.isActive() && query.numRowsAffected() > 0 )
+ {
+ query.next();
+ activenvp->SetPreferredAudioStreamId(query.value(0).toInt());
+ }
+ }
+}
+
void TV::ToggleInputs(void)
{
if (activenvp == nvp)
@@ -2429,8 +2494,10 @@
activerecorder->Pause();
activerbuffer->Reset();
+ RecordPreferredLanguageForChannel(activerecorder->GetCurrentChannel());
activerecorder->SetChannel(name);
+ RestorePreferredLanguageForChannel(name);
activenvp->ResetPlaying();
activenvp->Play(1.0, true, false);
Index: libs/libmythtv/tv_play.h
===================================================================
--- libs/libmythtv/tv_play.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/tv_play.h (.../trunk) (revision 19)
@@ -154,6 +154,7 @@
void DoToggleCC(int mode);
void DoSkipCommercials(int direction);
void DoEditMode(void);
+ void CycleLanguages(void);
void DoQueueTranscode(void);
@@ -190,6 +191,9 @@
void ToggleRecord(void);
void BrowseChannel(QString &chan);
+ void RecordPreferredLanguageForChannel(QString channel);
+ void RestorePreferredLanguageForChannel(QString channel);
+
void DoTogglePictureAttribute(void);
void DoToggleRecPictureAttribute(void);
void DoChangePictureAttribute(int control, bool up, bool rec);
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/dbcheck.cpp (.../trunk) (revision 19)
@@ -1040,6 +1040,7 @@
" editing INT UNSIGNED NOT NULL DEFAULT 0,"
" cutlist TEXT NULL,"
" autoexpire INT DEFAULT 0 NOT NULL,"
+" language INT UNSIGNED NOT NULL DEFAULT 0,"
" PRIMARY KEY (chanid, starttime),"
" INDEX (endtime)"
");",
@@ -1128,6 +1129,10 @@
" chanid int(11) unsigned NOT NULL default '0',"
" PRIMARY KEY (favid)"
");",
+"CREATE TABLE IF NOT EXISTS channellanguage ("
+" channel VARCHAR(20) NOT NULL PRIMARY KEY,"
+" language INT unsigned NOT NULL"
+");",
"CREATE TABLE IF NOT EXISTS recordedmarkup"
"("
" chanid INT UNSIGNED NOT NULL,"
Index: libs/libmythtv/programinfo.h
===================================================================
--- libs/libmythtv/programinfo.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/programinfo.h (.../trunk) (revision 19)
@@ -136,6 +136,8 @@
void SetBookmark(long long pos, QSqlDatabase *db);
long long GetBookmark(QSqlDatabase *db);
+ void SetPreferredLanguage(int language, QSqlDatabase *db);
+ int GetPreferredLanguage(QSqlDatabase *db);
bool IsEditing(QSqlDatabase *db);
void SetEditing(bool edit, QSqlDatabase *db);
bool IsCommFlagged(QSqlDatabase *db);
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/avformatdecoder.h (.../trunk) (revision 19)
@@ -52,6 +52,8 @@
bool PosMapFromDb();
bool PosMapFromEnc();
+ virtual int GetAudioStreamCount(void);
+
QString GetEncodingType(void) { return QString("MPEG-2"); }
void SetPixelFormat(const int);
@@ -101,6 +103,7 @@
int &lower_bound, int &upper_bound);
void HandleGopStart(AVPacket *pkt);
+ void FilterAudioChannel(char *samples, int len);
RingBuffer *ringBuffer;
@@ -119,6 +122,7 @@
int audio_sample_size;
int audio_sampling_rate;
int audio_channels;
+ bool dual_language;
int audio_check_1st;
int audio_sampling_rate_2nd;
Index: libs/libmythtv/programinfo.cpp
===================================================================
--- libs/libmythtv/programinfo.cpp (.../branches/version-0.15.1) (revision 19)
+++ libs/libmythtv/programinfo.cpp (.../trunk) (revision 19)
@@ -1017,6 +1017,48 @@
return pos;
}
+void ProgramInfo::SetPreferredLanguage(int language, QSqlDatabase *db)
+{
+ MythContext::KickDatabase(db);
+
+ QString starts = recstartts.toString("yyyyMMddhhmm");
+ starts += "00";
+
+ QString querystr;
+
+ querystr = QString("UPDATE recorded SET language = %1, starttime = '%2' "
+ "WHERE chanid = '%3' AND "
+ "starttime = '%4';").arg(language).arg(starts).arg(chanid).arg(starts);
+
+ QSqlQuery query = db->exec(querystr);
+ if (!query.isActive())
+ MythContext::DBError("Save language update", querystr);
+}
+
+int ProgramInfo::GetPreferredLanguage(QSqlDatabase *db)
+{
+ MythContext::KickDatabase(db);
+
+ QString starts = recstartts.toString("yyyyMMddhhmm");
+ starts += "00";
+
+ QString querystr = QString("SELECT language FROM recorded WHERE "
+ "chanid = '%1' AND starttime = '%2';")
+ .arg(chanid).arg(starts);
+
+ QSqlQuery query = db->exec(querystr);
+ if (query.isActive() && query.numRowsAffected() > 0)
+ {
+ query.next();
+
+ QVariant value = query.value(0);
+ if(!value.isNull())
+ return value.toInt();
+ }
+ return 0;
+}
+
+
bool ProgramInfo::IsEditing(QSqlDatabase *db)
{
MythContext::KickDatabase(db);
Index: libs/libavcodec/mpegaudiodec.c
===================================================================
--- libs/libavcodec/mpegaudiodec.c (.../branches/version-0.15.1) (revision 19)
+++ libs/libavcodec/mpegaudiodec.c (.../trunk) (revision 19)
@@ -1256,6 +1256,7 @@
break;
}
+ avctx->avcodec_dual_language = s->mode==MPA_DUAL ? 1:0;
avctx->sample_rate = s->sample_rate;
avctx->channels = s->nb_channels;
avctx->bit_rate = s->bit_rate;
Index: libs/libavcodec/avcodec.h
===================================================================
--- libs/libavcodec/avcodec.h (.../branches/version-0.15.1) (revision 19)
+++ libs/libavcodec/avcodec.h (.../trunk) (revision 19)
@@ -722,6 +722,21 @@
* - decoding: unused
*/
int delay;
+
+ /**
+ * set when bilingual audio data has been detected.
+ * 0 normally, 1 if dual language flag is set
+ *
+ * this is a hack to keep binary compatibility with
+ * old versions of the library. where else could I
+ * put this flag ? it's such a little flag... one
+ * miserable, orphaned bit rejected by everyone...
+ * it's so unfair!
+ *
+ * - encoding: unused (called delay in this case...)
+ * - decoding: set by lavc
+ */
+#define avcodec_dual_language delay
/* - encoding parameters */
float qcompress; ///< amount of qscale change between easy & hard scenes (0.0-1.0)