Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 6857)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -30,6 +30,7 @@
 class FilterManager;
 class FilterChain;
 class VideoSync;
+struct AVSubtitle;
 
 struct TextContainer
 {
@@ -223,6 +224,11 @@
         return 0; 
     }
 
+    void DisplaySubtitles();
+    void ClearSubtitles();
+    void AddSubtitles(const AVSubtitle& subtitle);
+    bool SubtitlesEnabled();
+    
  protected:
     void DisplayPauseFrame(void);
     void DisplayNormalFrame(void);
@@ -483,6 +489,12 @@
 
     bool errored;
 
+    // the key is the start display time in ms
+    typedef QMap<uint32_t, AVSubtitle> SubtitleMap;
+    SubtitleMap nondisplayedSubtitles;
+    QMutex subtitleLock;
+    bool osdHasSubtitles;
+
     int m_DeintSetting;
 };
 
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 6857)
+++ libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -207,6 +207,8 @@
 
     errored = false;
     audio_timecode_offset = 0;
+
+    osdHasSubtitles = false;
 }
 
 NuppelVideoPlayer::~NuppelVideoPlayer(void)
@@ -218,6 +220,9 @@
         delete m_playbackinfo;
     if (weMadeBuffer)
         delete ringBuffer;
+
+    ClearSubtitles();
+
     if (osd)
         delete osd;
     
@@ -241,6 +246,7 @@
 
     if (videosync)
         delete videosync;
+
 }
 
 void NuppelVideoPlayer::SetWatchingRecording(bool mode)
@@ -932,14 +938,17 @@
     QString msg;
 
     vbimode = mode;
-    if (cc && !arg)
+
+    if (cc && !arg) 
     {
         cc = false;
         ResetCC();
         if (mode == 1)
             msg = QObject::tr("TXT off");
-        else
+        else if (mode == 2)
             msg = QObject::tr("CC off");
+	else
+            msg = QObject::tr("Subtitles off");
     }
     else
     {
@@ -996,8 +1005,8 @@
                 break;
             }
         }
-        else
-            msg = QString(QObject::tr("CC/TXT disabled"));
+        else 
+	    msg = QString(QObject::tr("Subtitles on"));
     }
 
     if (osd)
@@ -1588,8 +1597,12 @@
 
     VideoFrame *frame = videoOutput->GetLastShownFrame();
 
-    if (cc)
+    if (cc) {
         ShowText();
+	DisplaySubtitles();
+    } else {
+	ClearSubtitles();
+    } 
 
     videofiltersLock.lock();
     videoOutput->ProcessFrame(frame, osd, videoFilters, pipplayer);
@@ -4102,7 +4115,6 @@
         return false;
 }
 
-
 int NuppelVideoPlayer::getCurrentAudioTrack()
 {
     if (decoder)
@@ -4111,3 +4123,143 @@
         return 0;
 }
 
+// updates new subtitles to the screen and clears old ones
+void NuppelVideoPlayer::DisplaySubtitles() 
+{
+    OSDSet *subtitleOSD = NULL;
+    VideoFrame* currentFrame = videoOutput->GetLastShownFrame();
+    subtitleLock.lock();
+    SubtitleMap::iterator i1 = nondisplayedSubtitles.begin();
+    bool setVisible = false;
+
+    // time when the last shown subtitles should be hidden
+    static long long expireAt = -1;
+    static long long lastSubShownAt = -1;
+    
+    subtitleOSD = osd->GetSet("subtitles");
+    
+    // hide the subs if they have been long enough in the screen without 
+    // new subtitles replacing them
+    if (osdHasSubtitles && currentFrame->timecode >= expireAt) {
+
+	osd->HideSet("subtitles"); 
+	VERBOSE(VB_PLAYBACK, 
+		QString("Clearing expired subtitles from OSD set. Age was %1").
+		arg(currentFrame->timecode - lastSubShownAt));
+	osd->ClearAll("subtitles");
+	osdHasSubtitles = false;
+    }
+
+    while ((i1 != nondisplayedSubtitles.end()) && 
+	   (i1.key() <= currentFrame->timecode))
+    {
+	const AVSubtitle& subtitlePage = i1.data();
+
+	// clear old subtitles
+	if (osdHasSubtitles) {
+	    osd->HideSet("subtitles"); 
+	    osd->ClearAll("subtitles");
+	    osdHasSubtitles = false;
+	}
+        // draw the subtitle bitmap(s) to the OSD
+        for (std::size_t i = 0; i < subtitlePage.num_rects; ++i) {
+	    AVSubtitleRect* rect = &subtitlePage.rects[i];
+	    // because the buffer from rect might not start at 4 byte boundary,
+	    // or at least each line won't, we'll have to create the QImage
+	    // this way, one pixel at a time, as we cannot feed in the buffer 
+	    // directly
+	    QImage qImage(rect->w, rect->h, 32);
+	    qImage.setAlphaBuffer(true);
+	    for (int y = 0; y < rect->h; ++y) {
+		for (int x = 0; x < rect->w; ++x) {
+		    const uint8_t color = rect->bitmap[y*rect->w + x];
+		    const uint32_t pixel = rect->rgba_palette[color];
+		    qImage.setPixel(x, y, pixel);
+		}
+	    }
+	    OSDTypeImage* image = new OSDTypeImage();
+	    QPoint position(rect->x, rect->y);
+	    image->SetPosition(position);
+	    image->LoadFromQImage(qImage);
+	    subtitleOSD->AddType(image);
+	    av_free(rect->rgba_palette);
+	    av_free(rect->bitmap);
+	    setVisible = true;
+	    osdHasSubtitles = true;
+	}
+	if (subtitlePage.num_rects > 0)
+	    av_free(subtitlePage.rects);
+	expireAt = subtitlePage.end_display_time;
+	lastSubShownAt = currentFrame->timecode;
+
+	// the iterator is not guaranteed to point to anything sensible
+	// after the erase() call, thus we have to save the next iterator
+	// before erasing 
+	SubtitleMap::iterator nextSub = i1;
+	++nextSub;
+        nondisplayedSubtitles.erase(i1);        
+	i1 = nextSub; 
+    }
+    subtitleLock.unlock();
+
+    if (setVisible)
+    {
+        VERBOSE(
+	    VB_PLAYBACK, 
+	    QString("Setting subtitles visible, frame_timecode=%1")
+	    .arg(currentFrame->timecode));
+	osd->SetVisible(subtitleOSD, 0);
+    }
+}
+
+// hide subtitles and free the undisplayed subtitles
+void NuppelVideoPlayer::ClearSubtitles() {
+    OSDSet* osdSet = 0;
+
+    subtitleLock.lock();
+    if (nondisplayedSubtitles.size() > 0) {
+
+	// Delete all non-displayed held subtitles
+	for (SubtitleMap::iterator i1 = nondisplayedSubtitles.begin(); 
+	     i1 != nondisplayedSubtitles.end(); )
+	{
+
+	    AVSubtitle& subtitle = i1.data();
+    
+	    // Because the subtitles were not displayed, OSDSet does not
+	    // free the OSDTypeImages in ClearAll(), so we have to free
+	    // the dynamic buffers here
+	    for (std::size_t i = 0; i < subtitle.num_rects; ++i) {
+		AVSubtitleRect* rect = &subtitle.rects[i];
+		av_free(rect->rgba_palette);
+		av_free(rect->bitmap);
+	    }
+
+	    SubtitleMap::iterator nexti1 = i1;
+	    ++nexti1;
+
+	    nondisplayedSubtitles.erase( i1 );
+	    i1 = nexti1;
+	}
+    }
+    subtitleLock.unlock();
+
+    // Clear subtitles from OSD 
+    if (osdHasSubtitles && osd)
+	osdSet = osd->GetSet("subtitles");
+
+    if( osdSet )
+    {
+        osd->HideSet("subtitles");
+        osdSet->Clear();
+        osdHasSubtitles = false;
+    }
+}
+
+bool NuppelVideoPlayer::SubtitlesEnabled() {
+    return cc;
+}
+// adds a new subtitle to be shown
+void NuppelVideoPlayer::AddSubtitles(const AVSubtitle& subtitle) {
+    nondisplayedSubtitles[subtitle.start_display_time] = subtitle;
+}
Index: libs/libmythtv/osd.h
===================================================================
--- libs/libmythtv/osd.h	(revision 6857)
+++ libs/libmythtv/osd.h	(working copy)
@@ -48,6 +48,7 @@
 
     OSDSurface *Display(void);
 
+    void ClearAll(const QString &name);
     void ClearAllText(const QString &name);
     void SetText(const QString &name, QMap<QString, QString> &infoMap,
                          int length);
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 6857)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -167,6 +167,9 @@
     firstgoppos = 0;
     prevgoppos = 0;
 
+    subtitleLanguagePreferences = 
+	QStringList::split(",", gContext->GetSetting("PreferredLanguages", ""));
+
     fps = 29.97;
     maxkeyframedist = -1;
 
@@ -191,6 +194,56 @@
     delete d;
 }
 
+
+// evaluates whether the given language is the subtitle language that is
+// preferred
+//
+// in case there's only one subtitle language available, always returns true
+//
+// if more than one subtitle languages are found, the best one is
+// picked according to the PreferredLanguages setting
+//
+// in case there's no PreferredLanguages setting, or no preferred language
+// is found, the first found subtitle stream is preferred
+bool AvFormatDecoder::IsWantedSubtitleLanguage(QString language) {
+    
+    if (!ic) 
+	return true;
+
+    // count the subtitle streams
+    int subtitleStreams = 0;
+    AVStream *first = NULL;
+    for (int i = 0; i < ic->nb_streams; i++)
+    {
+	AVStream* st = ic->streams[i];
+	if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) {
+	    if (first == NULL)
+		first = ic->streams[i];
+	    ++subtitleStreams;
+	}
+    }
+    if (subtitleStreams == 1)
+	return true;
+    
+    // go through all preferred languages and pick the best found
+    for (QStringList::iterator l = subtitleLanguagePreferences.begin();
+	 l != subtitleLanguagePreferences.end(); ++l) {
+
+	for (int i = 0; i < ic->nb_streams; i++)
+	{	    
+	    AVStream* st = ic->streams[i];
+
+	    if (st->language == *l) {
+		return (language == st->language);
+	    }
+	}
+    }
+    // no preferences at all, or no language that is preferred found -- 
+    // let's pick the first subtitle stream
+    return language == first->language;
+}
+
+
 void AvFormatDecoder::CloseContext()
 {
     if (ic)
@@ -842,6 +895,14 @@
                 bitrate += enc->bit_rate;
                 break;
             }
+            case CODEC_TYPE_SUBTITLE:
+            {
+                bitrate += enc->bit_rate;
+                VERBOSE(VB_PLAYBACK,
+                        QString("AvFormatDecoder: subtitle codec")
+                        .arg(enc->codec_type));
+                break;
+            }
             case CODEC_TYPE_DATA:
             {
                 bitrate += enc->bit_rate;
@@ -861,7 +922,8 @@
         }
 
         if (enc->codec_type != CODEC_TYPE_AUDIO && 
-            enc->codec_type != CODEC_TYPE_VIDEO)
+            enc->codec_type != CODEC_TYPE_VIDEO &&
+	    enc->codec_type != CODEC_TYPE_SUBTITLE)
             continue;
 
         VERBOSE(VB_PLAYBACK, QString("AVFD: Looking for decoder for %1")
@@ -1675,7 +1737,7 @@
         if (!curstream->codec.codec)
         {
             VERBOSE(VB_PLAYBACK, QString("No codec for stream index %1").arg(pkt->stream_index));
-            av_free_packet(pkt);
+            av_free_packet(pkt);	    
             continue;
         }
 
@@ -1848,6 +1910,28 @@
                     lastvpts = temppts;
                     break;
                 }
+                case CODEC_TYPE_SUBTITLE:
+                {
+                    AVCodecContext *context = &(curstream->codec);
+		    int gotSubtitles = 0;
+		    AVSubtitle subtitle;
+		    
+		    if (m_parent->SubtitlesEnabled() && 
+			IsWantedSubtitleLanguage(curstream->language)) 
+			avcodec_decode_subtitle(
+			    context, &subtitle, &gotSubtitles, ptr, len);
+		    // the subtitle decoder always consumes the whole packet
+                    ptr += len;
+		    len = 0;
+
+		    if (gotSubtitles) {
+			subtitle.start_display_time += pts;
+			subtitle.end_display_time += pts;
+			m_parent->AddSubtitles(subtitle);
+		    }
+
+		    break;
+		}
                 default:
                     cerr << "error decoding - " << curstream->codec.codec_type
                          << endl;
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 6857)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -128,6 +128,8 @@
 
     // Update our position map, keyframe distance, and the like.  Called for key frame packets.
     void HandleGopStart(AVPacket *pkt);
+
+    bool IsWantedSubtitleLanguage(QString language);
     
     class AvFormatDecoderPrivate *d;
 
@@ -179,6 +181,8 @@
     MythCodecID video_codec_id;
 
     int maxkeyframedist;
+
+    QStringList subtitleLanguagePreferences;
 };
 
 #endif
Index: libs/libmythtv/osdtypes.cpp
===================================================================
--- libs/libmythtv/osdtypes.cpp	(revision 6857)
+++ libs/libmythtv/osdtypes.cpp	(working copy)
@@ -133,15 +133,22 @@
 
 OSDSet::~OSDSet()
 {
-    vector<OSDType *>::iterator i = allTypes->begin();
-    for (; i != allTypes->end(); i++)
-    {
-        OSDType *type = (*i);
-        if (type)
-            delete type;
-    }
+    this->Clear();
     delete allTypes;
 }
+
+void OSDSet::Clear()
+{
+     vector<OSDType *>::iterator i = allTypes->begin();
+     for (; i != allTypes->end(); i++)
+     {
+	 OSDType* type = *i;
+         if (type)
+             delete type;
+     }
+    allTypes->clear();
+
+}
  
 void OSDSet::AddType(OSDType *type)
 {
Index: libs/libmythtv/osdtypes.h
===================================================================
--- libs/libmythtv/osdtypes.h	(revision 6857)
+++ libs/libmythtv/osdtypes.h	(working copy)
@@ -24,6 +24,8 @@
            float wmult, float hmult, int frint);
     OSDSet(const OSDSet &other);
    ~OSDSet();
+   
+    void Clear();
 
     void SetCache(bool cache) { m_cache = cache; }
     bool GetCache() { return m_cache; }
Index: libs/libmythtv/osd.cpp
===================================================================
--- libs/libmythtv/osd.cpp	(revision 6857)
+++ libs/libmythtv/osd.cpp	(working copy)
@@ -196,6 +196,17 @@
 
         container->AddType(lb);
     }
+
+     
+    // Create container for subtitles
+    container = GetSet("subtitles");
+    if (!container)
+    {
+        QString name = "subtitles";
+        container = new OSDSet(name, true, vid_width, vid_height, wmult, hmult,
+                               frameint);
+        AddSet(container, name);
+    }    
 }
 
 void OSD::Reinit(int width, int height, int frint, int dispx, int dispy, 
@@ -1224,6 +1235,16 @@
     return retval;
 }
 
+void OSD::ClearAll(const QString &name)
+{
+    osdlock.lock();
+    OSDSet *container = GetSet(name);
+    if (container)
+        container->Clear();
+
+    osdlock.unlock();
+}
+
 void OSD::ClearAllText(const QString &name)
 {
     osdlock.lock();
