Index: keys.txt
===================================================================
--- keys.txt (.../version-0.14) (revision 19)
+++ keys.txt (.../last-0.14) (revision 19)
@@ -47,6 +47,7 @@
- W to cycle through 4:3 aspect ratio, 16:9, and 4:3 Zoom (like pan and scan)
- Left (if a jump amount is entered) to jump back that amount.
- Right (if a jump amount is entered) to jump ahead that amount.
+- L to cycle through available languages
Without the stickykeys option selected:
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h (.../version-0.14) (revision 19)
+++ libs/libmythtv/NuppelVideoPlayer.h (.../last-0.14) (revision 19)
@@ -155,6 +155,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);
@@ -177,6 +178,10 @@
void ToggleLetterbox(void);
int GetLetterbox(void);
+ bool CyclePreferredAudioStreamId(void);
+ int GetPreferredAudioStreamId(void);
+ void SetPreferredAudioStreamId(int id);
+
void ExposeEvent(void);
protected:
@@ -231,6 +236,7 @@
int UpdateDelay(struct timeval *nexttrigger);
void ShowText();
+ void DisplayAudioChange();
void UpdateTimeDisplay(void);
void UpdateSeekAmount(bool up);
@@ -372,6 +378,8 @@
long long bookmarkseek;
+ int preferredAudioStreamId;
+
int consecutive_blanks;
int skipcommercials;
int autocommercialskip;
@@ -382,8 +390,10 @@
int ccindent[4];
int lastccrow;
- DecoderBase *decoder;
+ DecoderBase *decoder;
+ int displayStreamCount;
+
/* avsync stuff */
int lastaudiotime;
int delay;
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp (.../version-0.14) (revision 19)
+++ libs/libmythtv/NuppelVideoPlayer.cpp (.../last-0.14) (revision 19)
@@ -178,6 +178,8 @@
warplbuff = NULL;
warprbuff = NULL;
warpbuffsize = 0;
+
+ preferredAudioStreamId=0;
}
NuppelVideoPlayer::~NuppelVideoPlayer(void)
@@ -1583,6 +1585,7 @@
VideoFrame *frame = videoOutput->GetLastShownFrame();
+ DisplayAudioChange();
if (cc)
ShowText();
@@ -1640,6 +1643,7 @@
}
else
{
+ DisplayAudioChange();
if (cc)
ShowText();
videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
@@ -1762,8 +1766,16 @@
if (gContext->GetNumSetting("ClearSavedPosition", 1))
m_playbackinfo->SetBookmark(0, m_db);
+
}
+ if(!livetv && m_db && m_playbackinfo)
+ preferredAudioStreamId=m_playbackinfo->GetPreferredLanguage(m_db);
+ if(preferredAudioStreamIdGetAudioStreamCount())
+ decoder->SetCurrentAudioStreamId(preferredAudioStreamId);
+ else
+ decoder->SetCurrentAudioStreamId(0);
+
LoadBlankList();
if (!blankMap.isEmpty())
{
@@ -1999,6 +2011,109 @@
}
}
+/**
+ * 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)
+ {
+ printf("DEBUG: preferredAudioStreamId reset to 0 was %d\n", preferredAudioStreamId);
+ preferredAudioStreamId=0;
+
+ }
+ decoder->SetCurrentAudioStreamId(preferredAudioStreamId);
+
+ if(!livetv && m_db && m_playbackinfo)
+ {
+ QMutexLocker lockit(&db_lock);
+ printf("DEBUG: set preferred language to %d\n", preferredAudioStreamId);
+ m_playbackinfo->SetPreferredLanguage(preferredAudioStreamId, m_db);
+ }
+ else
+ printf("DEBUG: do not set preferred language to %d\n", count);
+
+ 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();
+ printf("DEBUG: current audio stream: %d\n", streamId);
+ switch(streamId)
+ {
+ case 0: // tell the user about this capability
+ if(count==2)
+ text=QObject::tr("Language I/II");
+ else
+ text=QObject::tr("Multilingual");
+ break;
+
+ case 1: // display current setting; 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/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp (.../version-0.14) (revision 19)
+++ libs/libmythtv/avformatdecoder.cpp (.../last-0.14) (revision 19)
@@ -64,6 +64,8 @@
memset(prvpkt, 0, 3);
do_ac3_passthru = gContext->GetNumSetting("AC3PassThru", false);
+
+ dual_language=true;
}
AvFormatDecoder::~AvFormatDecoder()
@@ -970,6 +972,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;
@@ -1120,19 +1131,34 @@
audio_sample_size = audio_channels * 2;
m_parent->SetEffDsp(audio_sampling_rate * 100);
- m_parent->SetAudioParams(16, audio_channels,
+ m_parent->SetAudioParams(16,
+ audio_channels,
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:
@@ -1266,6 +1292,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; sample=0 && idstart(kMuteTimeout, true);
}
+void TV::CycleLanguages(void)
+{
+ if(!activenvp)
+ return;
+ bool changed = activenvp->CyclePreferredAudioStreamId();
+ 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)
+{
+ printf("DEBUG: RecordPreferredLanguageForChannel(%s)\n", channel.ascii());
+ if(m_db && activenvp)
+ {
+ QString thequery = QString("REPLACE INTO channellanguage (language, channel) "
+ "VALUES (%1, '%2')")
+ .arg(activenvp->GetPreferredAudioStreamId())
+ .arg(channel);
+ printf("DEBUG: %s\n", thequery.ascii());
+ QSqlQuery query = m_db->exec(thequery);
+ if(!query.isActive())
+ MythContext::DBError("WriteChannelLanguage", thequery);
+ }
+}
+void TV::RestorePreferredLanguageForChannel(QString channel)
+{
+ printf("DEBUG: RestorePreferredLanguageForChannel(%s)\n", channel.ascii());
+
+ if(m_db && activenvp)
+ {
+ QString thequery = QString("SELECT language FROM channellanguage "
+ "WHERE channel = '%1'").arg(channel);
+ printf("DEBUG: %s\n", thequery.ascii());
+ QSqlQuery query = m_db->exec(thequery);
+ if(query.isActive() && query.numRowsAffected() > 0 )
+ {
+ query.next();
+ activenvp->SetPreferredAudioStreamId(query.value(0).toInt());
+ }
+ }
+}
+
void TV::ToggleInputs(void)
{
if (activenvp == nvp)
@@ -2156,14 +2226,18 @@
// all we care about is the ringbuffer being paused, here..
activerbuffer->WaitForPause();
+
+
// Save the current channel if this is the first time
if (channame_vector.size() == 0)
AddPreviousChannel();
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 (.../version-0.14) (revision 19)
+++ libs/libmythtv/tv_play.h (.../last-0.14) (revision 19)
@@ -139,6 +139,7 @@
void ChangeFFRew(int direction);
void RepeatFFRew(void);
void DoSkipCommercials(int direction);
+ void CycleLanguages(void);
void DoQueueTranscode(void);
@@ -170,6 +171,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 (.../version-0.14) (revision 19)
+++ libs/libmythtv/dbcheck.cpp (.../last-0.14) (revision 19)
@@ -681,6 +681,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)"
");",
@@ -769,6 +770,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 (.../version-0.14) (revision 19)
+++ libs/libmythtv/programinfo.h (.../last-0.14) (revision 19)
@@ -128,6 +128,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 (.../version-0.14) (revision 19)
+++ libs/libmythtv/avformatdecoder.h (.../last-0.14) (revision 19)
@@ -52,6 +52,8 @@
bool PosMapFromDb();
bool PosMapFromEnc();
+ virtual int GetAudioStreamCount(void);
+
QString GetEncodingType(void) { return QString("MPEG-2"); }
protected:
@@ -99,6 +101,7 @@
int &lower_bound, int &upper_bound);
void HandleGopStart(AVPacket *pkt);
+ void FilterAudioChannel(char *samples, int len);
RingBuffer *ringBuffer;
@@ -117,6 +120,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/mpegrecorder.cpp
===================================================================
--- libs/libmythtv/mpegrecorder.cpp (.../version-0.14) (revision 19)
+++ libs/libmythtv/mpegrecorder.cpp (.../last-0.14) (revision 19)
@@ -307,6 +307,7 @@
if (ioctl(chanfd, VIDIOC_S_CTRL, &ctrl) < 0)
{
cerr << "Error setting codec params\n";
+ printf("VIDIOC_S_CTRL failed (0x%lx)\n", VIDIOC_S_CTRL);
perror("VIDIOC_S_CTRL:");
return;
}
Index: libs/libmythtv/programinfo.cpp
===================================================================
--- libs/libmythtv/programinfo.cpp (.../version-0.14) (revision 19)
+++ libs/libmythtv/programinfo.cpp (.../last-0.14) (revision 19)
@@ -923,6 +923,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 (.../version-0.14) (revision 19)
+++ libs/libavcodec/mpegaudiodec.c (.../last-0.14) (revision 19)
@@ -1255,6 +1255,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 (.../version-0.14) (revision 19)
+++ libs/libavcodec/avcodec.h (.../last-0.14) (revision 19)
@@ -679,6 +679,7 @@
int sample_fmt; ///< sample format, currenly unused
/* the following data should not be initialized */
+
int frame_size; ///< in samples, initialized when calling 'init'
int frame_number; ///< audio or video frame number
int real_pict_num; ///< returns the real picture number of previous encoded frame
@@ -690,6 +691,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)