Ticket #1748: external_text_subtitle_file_support.v4.patch
File external_text_subtitle_file_support.v4.patch, 66.3 KB (added by , 18 years ago) |
---|
-
libs/libmythtv/NuppelVideoPlayer.cpp
old new 119 119 uint track_type_to_display_mode[kTrackTypeCount+2] = 120 120 { 121 121 kDisplayNone, 122 kDisplay Subtitle,122 kDisplayAVSubtitle, 123 123 kDisplayCC608, 124 124 kDisplayCC708, 125 125 kDisplayTeletextCaptions, … … 288 288 if (weMadeBuffer) 289 289 delete ringBuffer; 290 290 291 if (osdHasSubtitles || nonDisplayed Subtitles.size() > 0)291 if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0) 292 292 ClearSubtitles(); 293 293 294 294 if (osd) … … 1424 1424 1425 1425 DisableTeletext(); 1426 1426 } 1427 if (kDisplay Subtitle & mode)1427 if (kDisplayAVSubtitle & mode) 1428 1428 { 1429 1429 msg += decoder->GetTrackDesc(kTrackTypeSubtitle, 1430 1430 GetTrack(kTrackTypeSubtitle)); 1431 1431 } 1432 if (kDisplayTextSubtitle & mode) 1433 { 1434 msg += QObject::tr("Text subtitles"); 1435 } 1432 1436 if (kDisplayCC608 & mode) 1433 1437 { 1434 1438 msg += decoder->GetTrackDesc(kTrackTypeCC608, … … 1451 1455 void NuppelVideoPlayer::EnableCaptions(uint mode) 1452 1456 { 1453 1457 QString msg = ""; 1454 if (kDisplay Subtitle & mode)1458 if (kDisplayAVSubtitle & mode) 1455 1459 { 1456 1460 msg += decoder->GetTrackDesc(kTrackTypeSubtitle, 1457 1461 GetTrack(kTrackTypeSubtitle)); 1458 1462 } 1463 if (kDisplayTextSubtitle & mode) 1464 { 1465 msg += QObject::tr("Text Subtitles"); 1466 } 1459 1467 if (kDisplayNUVTeletextCaptions & mode) 1460 1468 msg += QObject::tr("TXT") + QString(" %1").arg(ttPageNum, 3, 16); 1461 1469 if (kDisplayCC608 & mode) … … 1565 1573 if (kDisplayCC708 & mode) 1566 1574 EnableCaptions(kDisplayCC708); 1567 1575 1568 if (kDisplaySubtitle & mode) 1569 EnableCaptions(kDisplaySubtitle); 1570 1576 if (kDisplayAVSubtitle & mode) 1577 EnableCaptions(kDisplayAVSubtitle); 1578 1579 if (kDisplayTextSubtitle & mode) 1580 EnableCaptions(kDisplayTextSubtitle); 1581 1571 1582 if (kDisplayTeletextCaptions & mode) 1572 1583 EnableCaptions(kDisplayTeletextCaptions); 1573 1584 1574 1585 return textDisplayMode; 1575 1586 } 1576 1587 … … 1589 1600 // figure out which text type to enable.. 1590 1601 bool captions_found = true; 1591 1602 if (decoder->GetTrackCount(kTrackTypeSubtitle)) 1592 EnableCaptions(kDisplaySubtitle); 1603 EnableCaptions(kDisplayAVSubtitle); 1604 else if (textSubtitles.GetSubtitleCount() > 0) 1605 EnableCaptions(kDisplayTextSubtitle); 1593 1606 else if (decoder->GetTrackCount(kTrackTypeCC708)) 1594 1607 EnableCaptions(kDisplayCC708); 1595 1608 else if (decoder->GetTrackCount(kTrackTypeTeletextCaptions)) … … 2390 2403 if (textDisplayMode & kDisplayNUVCaptions) 2391 2404 ShowText(); 2392 2405 2393 // handle with DVB/DVD subtitles 2394 if (textDisplayMode & kDisplaySubtitle) 2395 DisplaySubtitles(); 2396 else if (osdHasSubtitles) 2406 // handle DVB/DVD subtitles decoded by ffmpeg (in AVSubtitle format) 2407 if (textDisplayMode & kDisplayAVSubtitle) 2408 DisplayAVSubtitles(); 2409 else if (textDisplayMode & kDisplayTextSubtitle) 2410 DisplayTextSubtitles(); 2411 else if (osdHasSubtitles) 2397 2412 ClearSubtitles(); 2398 2413 else 2399 2414 ExpireSubtitles(); … … 2630 2645 if (fftime <= 0) 2631 2646 fftime = (int)(seconds * video_frame_rate); 2632 2647 2633 if (osdHasSubtitles || nonDisplayed Subtitles.size() > 0)2648 if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0) 2634 2649 ClearSubtitles(); 2635 2650 2636 2651 return fftime > CalcMaxFFTime(fftime, false); … … 2644 2659 if (rewindtime <= 0) 2645 2660 rewindtime = (int)(seconds * video_frame_rate); 2646 2661 2647 if (osdHasSubtitles || nonDisplayed Subtitles.size() > 0)2662 if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0) 2648 2663 ClearSubtitles(); 2649 2664 2650 2665 return rewindtime >= framesPlayed; … … 5582 5597 if (kTrackTypeSubtitle == type) 5583 5598 { 5584 5599 DisableCaptions(textDisplayMode, false); 5585 EnableCaptions(kDisplay Subtitle);5600 EnableCaptions(kDisplayAVSubtitle); 5586 5601 } 5587 5602 else if (kTrackTypeCC708 == type) 5588 5603 { … … 5738 5753 SetTrack(kTrackTypeSubtitle, 0); 5739 5754 } 5740 5755 } 5741 else if ((textDisplayMode & kDisplay Subtitle) &&5756 else if ((textDisplayMode & kDisplayAVSubtitle) && 5742 5757 (vbimode == VBIMode::PAL_TT)) 5743 5758 { 5744 5759 if ((uint)GetTrack(kTrackTypeSubtitle) + 1 < … … 5798 5813 else 5799 5814 SetCaptionsEnabled(false); 5800 5815 } 5801 else if ((textDisplayMode & kDisplay Subtitle) &&5816 else if ((textDisplayMode & kDisplayAVSubtitle) && 5802 5817 (vbimode == VBIMode::NTSC_CC)) 5803 5818 { 5804 5819 if ((uint)GetTrack(kTrackTypeSubtitle) + 1 < … … 5811 5826 } 5812 5827 } 5813 5828 5814 // updates new subtitles to the screen and clears old ones 5815 void NuppelVideoPlayer::DisplaySubtitles() 5829 #define MAX_SUBTITLE_DISPLAY_TIME_MS 4500 5830 /** \fn NuppelVideoPlayer::DisplayAVSubtitles(void) 5831 * \brief Displays new subtitles and removes old ones. 5832 * 5833 * This version is for AVSubtitles which are added with AddAVSubtitles() 5834 * when found in the input stream. 5835 */ 5836 void NuppelVideoPlayer::DisplayAVSubtitles() 5816 5837 { 5817 5838 OSDSet *subtitleOSD; 5818 5839 bool setVisible = false; … … 5836 5857 osdHasSubtitles = false; 5837 5858 } 5838 5859 5839 while (nonDisplayed Subtitles.size() > 0)5860 while (nonDisplayedAVSubtitles.size() > 0) 5840 5861 { 5841 const AVSubtitle subtitlePage = nonDisplayed Subtitles.front();5842 5862 const AVSubtitle subtitlePage = nonDisplayedAVSubtitles.front(); 5863 5843 5864 if (subtitlePage.start_display_time > currentFrame->timecode) 5844 5865 break; 5845 5846 nonDisplayed Subtitles.pop_front();5847 5866 5867 nonDisplayedAVSubtitles.pop_front(); 5868 5848 5869 // clear old subtitles 5849 5870 if (osdHasSubtitles) 5850 5871 { … … 5852 5873 osd->ClearAll("subtitles"); 5853 5874 osdHasSubtitles = false; 5854 5875 } 5855 5876 5856 5877 // draw the subtitle bitmap(s) to the OSD 5857 5878 for (std::size_t i = 0; i < subtitlePage.num_rects; ++i) 5858 5879 { 5859 5880 AVSubtitleRect* rect = &subtitlePage.rects[i]; 5860 5881 5861 5882 bool displaysub = true; 5862 if (nonDisplayed Subtitles.size() > 0 &&5863 nonDisplayed Subtitles.front().end_display_time <5883 if (nonDisplayedAVSubtitles.size() > 0 && 5884 nonDisplayedAVSubtitles.front().end_display_time < 5864 5885 currentFrame->timecode) 5865 5886 { 5866 5887 displaysub = false; … … 5881 5902 qImage.setPixel(x, y, pixel); 5882 5903 } 5883 5904 } 5884 5905 5885 5906 // scale the subtitle images which are scaled and positioned for 5886 5907 // a 720x576 video resolution to fit the current OSD resolution 5887 5908 float vsize = 576.0; 5888 5909 if (ringBuffer->isDVD()) 5889 5910 vsize = (float)video_height; 5890 5911 5891 5912 float hmult = osd->GetSubtitleBounds().width() / 720.0; 5892 5913 float vmult = osd->GetSubtitleBounds().height() / vsize; 5893 5914 5894 5915 rect->x = (int)(rect->x * hmult); 5895 5916 rect->y = (int)(rect->y * vmult); 5896 5917 rect->w = (int)(rect->w * hmult); … … 5898 5919 5899 5920 if (hmult < 0.98 || hmult > 1.02 || vmult < 0.98 || hmult > 1.02) 5900 5921 qImage = qImage.smoothScale(rect->w, rect->h); 5901 5922 5902 5923 OSDTypeImage* image = new OSDTypeImage(); 5903 5924 image->SetPosition(QPoint(rect->x, rect->y), hmult, vmult); 5904 5925 image->LoadFromQImage(qImage); 5905 5926 5906 5927 subtitleOSD->AddType(image); 5907 5928 5908 5929 osdSubtitlesExpireAt = subtitlePage.end_display_time; 5909 5930 // fix subtitles that don't display for very long (if at all). 5910 5931 if (subtitlePage.end_display_time <= 5911 5932 subtitlePage.start_display_time) 5912 5933 { 5913 if (nonDisplayedSubtitles.size() > 0) 5914 osdSubtitlesExpireAt = nonDisplayedSubtitles.front().start_display_time; 5934 if (nonDisplayedAVSubtitles.size() > 0) 5935 osdSubtitlesExpireAt = 5936 nonDisplayedAVSubtitles.front().start_display_time; 5915 5937 else 5916 osdSubtitlesExpireAt += 4500;5938 osdSubtitlesExpireAt += MAX_SUBTITLE_DISPLAY_TIME_MS; 5917 5939 } 5918 5940 5919 5941 setVisible = true; … … 5940 5962 } 5941 5963 } 5942 5964 5965 /** \fn NuppelVideoPlayer::DisplayTextSubtitles(void) 5966 * \brief Displays subtitles in textual format. 5967 * 5968 * This version is for subtitles that are loaded from an external subtitle 5969 * file by using the LoadExternalSubtitles() method. Subtitles are not 5970 * deleted after displaying so they can be displayed again after seeking. 5971 */ 5972 void NuppelVideoPlayer::DisplayTextSubtitles() 5973 { 5974 VideoFrame *currentFrame = videoOutput->GetLastShownFrame(); 5975 5976 if (!osd || !currentFrame) 5977 { 5978 VERBOSE(VB_PLAYBACK, "osd or current video frame not found"); 5979 return; 5980 } 5981 5982 QMutexLocker locker(&subtitleLock); 5983 5984 // frame time code in frames shown or millisecs from the start 5985 // depending on the timing type of the subtitles 5986 uint64_t playPos = 0; 5987 if (textSubtitles.IsFrameBasedTiming()) 5988 { 5989 // frame based subtitles get out of synch after running mythcommflag 5990 // for the file, i.e., the following number is wrong and does not 5991 // match the subtitle frame numbers: 5992 playPos = currentFrame->frameNumber; 5993 } 5994 else 5995 { 5996 playPos = currentFrame->timecode; 5997 } 5998 5999 if (!textSubtitles.SubtitleChanged(playPos)) 6000 return; 6001 6002 QStringList subtitlesToShow = textSubtitles.Subtitles(playPos); 6003 6004 if (subtitlesToShow.size() == 0) 6005 { 6006 osd->ClearTextSubtitles(); 6007 osdHasSubtitles = false; 6008 return; 6009 } 6010 6011 osd->SetTextSubtitles(subtitlesToShow); 6012 osdHasSubtitles = true; 6013 } 6014 5943 6015 /** \fn NuppelVideoPlayer::ExpireSubtitles(void) 5944 6016 * \brief Discard non-displayed subtitles. 5945 6017 */ … … 5952 6024 5953 6025 VideoFrame *currentFrame = videoOutput->GetLastShownFrame(); 5954 6026 5955 while (nonDisplayed Subtitles.size() > 0)6027 while (nonDisplayedAVSubtitles.size() > 0) 5956 6028 { 5957 const AVSubtitle subtitlePage = nonDisplayed Subtitles.front();6029 const AVSubtitle subtitlePage = nonDisplayedAVSubtitles.front(); 5958 6030 5959 6031 // Stop when we hit one old enough 5960 6032 if (subtitlePage.end_display_time > currentFrame->timecode) 5961 6033 break; 5962 6034 5963 nonDisplayed Subtitles.pop_front();6035 nonDisplayedAVSubtitles.pop_front(); 5964 6036 5965 6037 for (std::size_t i = 0; i < subtitlePage.num_rects; ++i) 5966 6038 { … … 5980 6052 { 5981 6053 subtitleLock.lock(); 5982 6054 5983 while (nonDisplayed Subtitles.size() > 0)6055 while (nonDisplayedAVSubtitles.size() > 0) 5984 6056 { 5985 AVSubtitle& subtitle = nonDisplayed Subtitles.front();6057 AVSubtitle& subtitle = nonDisplayedAVSubtitles.front(); 5986 6058 5987 6059 // Because the subtitles were not displayed, OSDSet does not 5988 6060 // free the OSDTypeImages in ClearAll(), so we have to free … … 5997 6069 if (subtitle.num_rects > 0) 5998 6070 av_free(subtitle.rects); 5999 6071 6000 nonDisplayed Subtitles.pop_front();6072 nonDisplayedAVSubtitles.pop_front(); 6001 6073 } 6002 6074 6003 6075 subtitleLock.unlock(); … … 6014 6086 } 6015 6087 } 6016 6088 6017 // adds a new subtitle to be shown 6089 // adds a new AVSubtitle to be shown, assumes the subtitles are pushed in 6090 // the order they should be shown 6018 6091 // FIXME: Need to fix subtitles to use a 64bit timestamp 6019 void NuppelVideoPlayer::Add Subtitle(const AVSubtitle &subtitle)6092 void NuppelVideoPlayer::AddAVSubtitle(const AVSubtitle &subtitle) 6020 6093 { 6021 6094 subtitleLock.lock(); 6022 nonDisplayed Subtitles.push_back(subtitle);6095 nonDisplayedAVSubtitles.push_back(subtitle); 6023 6096 subtitleLock.unlock(); 6024 6097 } 6025 6098 … … 6039 6112 } 6040 6113 } 6041 6114 6115 /** \fn NuppelVideoPlayer::LoadExternalSubtitles(QString) 6116 * \brief Loads subtitles from an external file. 6117 * 6118 * \return True in case the subtitle file format was detected and subtitles 6119 * were loaded successfully, false otherwise. 6120 */ 6121 bool NuppelVideoPlayer::LoadExternalSubtitles(QString subtitleFileName) 6122 { 6123 QMutexLocker locker(&subtitleLock); 6124 textSubtitles.Clear(); 6125 return TextSubtitleParser::LoadSubtitles(subtitleFileName, textSubtitles); 6126 } 6127 6042 6128 void NuppelVideoPlayer::ChangeDVDTrack(bool ffw) 6043 6129 { 6044 6130 if (!ringBuffer->isDVD()) -
libs/libmythtv/avformatdecoder.cpp
old new 3061 3061 { 3062 3062 subtitle.start_display_time += pts; 3063 3063 subtitle.end_display_time += pts; 3064 GetNVP()->Add Subtitle(subtitle);3064 GetNVP()->AddAVSubtitle(subtitle); 3065 3065 } 3066 3066 3067 3067 break; -
libs/libmythtv/libmythtv.pro
old new 203 203 SOURCES += tv_play.cpp NuppelVideoPlayer.cpp 204 204 SOURCES += DVDRingBuffer.cpp 205 205 206 # Text subtitle parser 207 HEADERS += textsubtitleparser.h xine_demux_sputext.h 208 SOURCES += textsubtitleparser.cpp xine_demux_sputext.c 209 206 210 # A/V decoders 207 211 HEADERS += decoderbase.h 208 212 HEADERS += nuppeldecoder.h avformatdecoder.h -
libs/libmythtv/osdtypes.h
old new 285 285 mutable uint m_draw_info_len; 286 286 mutable vector<DrawInfo> m_draw_info; 287 287 }; 288 288 289 289 class OSDTypeImage : public OSDType 290 290 { 291 291 public: -
libs/libmythtv/tv_play.h
old new 347 347 348 348 void ITVRestart(bool isLive); 349 349 350 void FindAndLoadExternalSubs(NuppelVideoPlayer& target, QString videoFile); 351 350 352 static QStringList GetValidRecorderList(uint chanid); 351 353 static QStringList GetValidRecorderList(const QString &channum); 352 354 static QStringList GetValidRecorderList(uint, const QString&); -
libs/libmythtv/NuppelVideoPlayer.h
old new 14 14 #include "recordingprofile.h" 15 15 #include "videooutbase.h" 16 16 #include "teletextdecoder.h" 17 #include "textsubtitleparser.h" 17 18 #include "tv_play.h" 18 19 #include "yuv2rgb.h" 19 20 #include "cc608decoder.h" … … 70 71 enum 71 72 { 72 73 kTrackTypeAudio = 0, 73 kTrackTypeSubtitle, 74 kTrackTypeSubtitle, // subtitle track from ffmpeg 74 75 kTrackTypeCC608, 75 76 kTrackTypeCC708, 76 77 kTrackTypeTeletextCaptions, 77 78 kTrackTypeCount, 78 79 79 kTrackTypeTeletextMenu, 80 80 }; 81 81 QString track_type_to_string(uint type); … … 87 87 kDisplayNone = 0x00, 88 88 kDisplayNUVTeletextCaptions = 0x01, 89 89 kDisplayTeletextCaptions = 0x02, 90 kDisplay Subtitle= 0x04,90 kDisplayAVSubtitle = 0x04, 91 91 kDisplayCC608 = 0x08, 92 92 kDisplayCC708 = 0x10, 93 93 kDisplayNUVCaptions = kDisplayNUVTeletextCaptions | kDisplayCC608, 94 kDisplayAllCaptions = 0x1f, 95 kDisplayTeletextMenu = 0x20, 94 kDisplayTextSubtitle = 0x20, 95 kDisplayAllCaptions = 0x3f, 96 kDisplayTeletextMenu = 0x40, 96 97 }; 97 98 98 99 class NuppelVideoPlayer : public CC608Reader, public CC708Reader … … 278 279 long long timecode); 279 280 void AddTextData(unsigned char *buffer, int len, 280 281 long long timecode, char type); 281 void Add Subtitle(const AVSubtitle& subtitle);282 void AddAVSubtitle(const AVSubtitle& subtitle); 282 283 283 284 // Closed caption and teletext stuff 284 285 uint GetCaptionMode(void) const { return textDisplayMode; } … … 288 289 bool ToggleCaptions(void); 289 290 bool ToggleCaptions(uint mode); 290 291 void SetCaptionsEnabled(bool); 292 bool LoadExternalSubtitles(QString subtitleFileName); 291 293 292 294 // Teletext Menu and non-NUV teletext decoder 293 295 void EnableTeletext(void); … … 478 480 void UpdateCC(unsigned char *inpos); 479 481 480 482 // Private subtitle stuff 481 void DisplaySubtitles(void); 483 void DisplayAVSubtitles(void); 484 void DisplayTextSubtitles(void); 482 485 void ClearSubtitles(void); 483 486 void ExpireSubtitles(void); 484 487 … … 613 616 bool textDesired; 614 617 bool osdHasSubtitles; 615 618 long long osdSubtitlesExpireAt; 616 MythDeque<AVSubtitle> nonDisplayedSubtitles; 619 620 /// Subtitles loaded from the video stream by libavcodec. 621 /// This should contain only undisplayed subtitles, old 622 /// ones are deleted after displayed. 623 MythDeque<AVSubtitle> nonDisplayedAVSubtitles; 624 625 /// Subtitles loaded from an external subtitle file. 626 /// This contains all subtitles in textual format. No 627 /// subtitles are deleted after displaying (so they can 628 /// be displayed again after seeking). The list is ordered 629 /// by the subtitle display start time. 630 TextSubtitles textSubtitles; 617 631 618 632 CC708Service CC708services[64]; 619 633 QString osdfontname; -
new file libs/libmythtv/textsubtitleparser.h
- + 1 // -*- Mode: c++ -*- 2 /** TextSubtitles 3 * Copyright (c) 2006 by Pekka Jääskeläinen 4 * Distributed as part of MythTV under GPL v2 and later. 5 */ 6 7 #ifndef TEXT_SUBTITLE_PARSER_H 8 #define TEXT_SUBTITLE_PARSER_H 9 10 #include <vector> 11 using namespace std; 12 13 #include <qstring.h> 14 #include <qstringlist.h> 15 #include <qdeepcopy.h> 16 17 #define MAX_TEXT_SUBTITLE_LINES 5 18 19 class text_subtitle_t 20 { 21 public: 22 text_subtitle_t(long start_, long end_) : start(start_), end(end_) {} 23 text_subtitle_t() : start(0), end(0) {} 24 text_subtitle_t(const text_subtitle_t &other) : 25 start(other.start), end(other.end), 26 textLines(QDeepCopy<QStringList>(other.textLines)) {} 27 28 public: 29 uint64_t start; ///< Starting time in msec or starting frame 30 uint64_t end; ///< Ending time in msec or ending frame 31 QStringList textLines; 32 }; 33 34 typedef std::vector<text_subtitle_t> TextSubtitleList; 35 36 class TextSubtitles 37 { 38 public: 39 TextSubtitles() : m_frameBasedTiming(false) {} 40 41 virtual ~TextSubtitles() {} 42 43 bool SubtitleChanged(uint64_t timecode) const; 44 QStringList Subtitles(uint64_t timecode) const; 45 46 /** \fn TextSubtitles::IsFrameBasedTiming(void) const 47 * \brief Returns true in case the subtitle timing data is frame-based. 48 * 49 * If the timing is frame-based, the client should use frame counts as 50 * timecodes for the SubtitleChanged() and Subtitles() methods, otherwise 51 * the timecode is milliseconds from the video start. 52 */ 53 bool IsFrameBasedTiming(void) const 54 { return m_frameBasedTiming; } 55 56 void SetFrameBasedTiming(bool frameBasedTiming) 57 { m_frameBasedTiming = frameBasedTiming; } 58 59 void AddSubtitle(const text_subtitle_t& newSub); 60 void Clear(void); 61 62 uint GetSubtitleCount(void) const 63 { return m_subtitles.size(); } 64 65 private: 66 TextSubtitleList m_subtitles; 67 mutable text_subtitle_t m_lastReturnedSubtitle; 68 bool m_frameBasedTiming; 69 }; 70 71 class TextSubtitleParser 72 { 73 public: 74 static bool LoadSubtitles(QString fileName, TextSubtitles& target); 75 }; 76 77 #endif -
libs/libmythtv/osd.h
old new 40 40 class OSDTypeImage; 41 41 class OSDTypePositionIndicator; 42 42 class OSDSurface; 43 class OSDTypeText; 43 44 class TV; 44 45 class UDPNotifyOSDSet; 45 46 class OSDListTreeType; … … 151 152 bool HasSet(const QString &name); 152 153 QRect GetSubtitleBounds(); 153 154 155 void SetTextSubtitles(const QStringList& lines); 156 void ClearTextSubtitles(); 154 157 private: 155 158 bool InitDefaults(void); 156 159 bool InitCC608(void); 157 160 bool InitCC708(void); 158 161 bool InitTeletext(void); 159 bool Init DVBSub(void);162 bool InitSubtitles(void); 160 163 bool InitMenu(void); 161 164 bool InitInteractiveTV(void); 162 165 -
new file libs/libmythtv/xine_demux_sputext.c
- + 1 /* 2 * Copyright (C) 2000-2003 the xine project 3 * 4 * This file is part of xine, a free video player. 5 * 6 * xine is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * xine is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 19 * 20 * $Id: demux_sputext.c,v 1.47 2006/02/14 18:44:08 dsalt Exp $ 21 * 22 * code based on old libsputext/xine_decoder.c 23 * 24 * code based on mplayer module: 25 * 26 * Subtitle reader with format autodetection 27 * 28 * Written by laaz 29 * Some code cleanup & realloc() by A'rpi/ESP-team 30 * dunnowhat sub format by szabi 31 */ 32 33 #ifdef HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <fcntl.h> 44 #include <ctype.h> 45 #include "xine_demux_sputext.h" 46 47 #define LOG_MODULE "demux_sputext" 48 #define LOG_VERBOSE 49 /* 50 #define LOG 51 */ 52 53 #define ERR (void *)-1 54 #define LINE_LEN 1000 55 #define LINE_LEN_QUOT "1000" 56 57 /* 58 * Demuxer code start 59 */ 60 61 #define FORMAT_UNKNOWN -1 62 #define FORMAT_MICRODVD 0 63 #define FORMAT_SUBRIP 1 64 #define FORMAT_SUBVIEWER 2 65 #define FORMAT_SAMI 3 66 #define FORMAT_VPLAYER 4 67 #define FORMAT_RT 5 68 #define FORMAT_SSA 6 /* Sub Station Alpha */ 69 #define FORMAT_PJS 7 70 #define FORMAT_MPSUB 8 71 #define FORMAT_AQTITLE 9 72 #define FORMAT_JACOBSUB 10 73 #define FORMAT_SUBVIEWER2 11 74 #define FORMAT_SUBRIP09 12 75 #define FORMAT_MPL2 13 /*Mplayer sub 2 ?*/ 76 77 static int eol(char p) { 78 return (p=='\r' || p=='\n' || p=='\0'); 79 } 80 81 static inline void trail_space(char *s) { 82 int i; 83 while (isspace(*s)) { 84 char *copy = s; 85 do { 86 copy[0] = copy[1]; 87 copy++; 88 } while(*copy); 89 } 90 i = strlen(s) - 1; 91 while (i > 0 && isspace(s[i])) 92 s[i--] = '\0'; 93 } 94 95 /* 96 * Reimplementation of fgets() using the input->read() method. 97 */ 98 static char *read_line_from_input(demux_sputext_t *this, char *line, off_t len) { 99 off_t nread = 0; 100 char *s; 101 int linelen; 102 103 if ((len - this->buflen) > 512) { 104 if((nread = fread( 105 &this->buf[this->buflen], 1, 106 len - this->buflen, this->file_ptr)) < 0) { 107 printf("read failed.\n"); 108 return NULL; 109 } 110 } 111 112 this->buflen += nread; 113 this->buf[this->buflen] = '\0'; 114 115 s = strchr(this->buf, '\n'); 116 117 if (line && (s || this->buflen)) { 118 119 linelen = s ? (s - this->buf) + 1 : this->buflen; 120 121 memcpy(line, this->buf, linelen); 122 line[linelen] = '\0'; 123 124 memmove(this->buf, &this->buf[linelen], SUB_BUFSIZE - linelen); 125 this->buflen -= linelen; 126 127 return line; 128 } 129 130 return NULL; 131 } 132 133 134 static subtitle_t *sub_read_line_sami(demux_sputext_t *this, subtitle_t *current) { 135 136 static char line[LINE_LEN + 1]; 137 static char *s = NULL; 138 char text[LINE_LEN + 1], *p, *q; 139 int state; 140 141 p = NULL; 142 current->lines = current->start = 0; 143 current->end = -1; 144 state = 0; 145 146 /* read the first line */ 147 if (!s) 148 if (!(s = read_line_from_input(this, line, LINE_LEN))) return 0; 149 150 do { 151 switch (state) { 152 153 case 0: /* find "START=" */ 154 s = strstr (s, "Start="); 155 if (s) { 156 current->start = strtol (s + 6, &s, 0) / 10; 157 state = 1; continue; 158 } 159 break; 160 161 case 1: /* find "<P" */ 162 if ((s = strstr (s, "<P"))) { s += 2; state = 2; continue; } 163 break; 164 165 case 2: /* find ">" */ 166 if ((s = strchr (s, '>'))) { s++; state = 3; p = text; continue; } 167 break; 168 169 case 3: /* get all text until '<' appears */ 170 if (*s == '\0') { break; } 171 else if (*s == '<') { state = 4; } 172 else if (!strncasecmp (s, " ", 6)) { *p++ = ' '; s += 6; } 173 else if (*s == '\r') { s++; } 174 else if (!strncasecmp (s, "<br>", 4) || *s == '\n') { 175 *p = '\0'; p = text; trail_space (text); 176 if (text[0] != '\0') 177 current->text[current->lines++] = strdup (text); 178 if (*s == '\n') s++; else s += 4; 179 } 180 else *p++ = *s++; 181 continue; 182 183 case 4: /* get current->end or skip <TAG> */ 184 q = strstr (s, "Start="); 185 if (q) { 186 current->end = strtol (q + 6, &q, 0) / 10 - 1; 187 *p = '\0'; trail_space (text); 188 if (text[0] != '\0') 189 current->text[current->lines++] = strdup (text); 190 if (current->lines > 0) { state = 99; break; } 191 state = 0; continue; 192 } 193 s = strchr (s, '>'); 194 if (s) { s++; state = 3; continue; } 195 break; 196 } 197 198 /* read next line */ 199 if (state != 99 && !(s = read_line_from_input (this, line, LINE_LEN))) 200 return 0; 201 202 } while (state != 99); 203 204 return current; 205 } 206 207 208 static char *sub_readtext(char *source, char **dest) { 209 int len=0; 210 char *p=source; 211 212 while ( !eol(*p) && *p!= '|' ) { 213 p++,len++; 214 } 215 216 *dest= (char *)malloc (len+1); 217 if (!dest) 218 return ERR; 219 220 strncpy(*dest, source, len); 221 (*dest)[len]=0; 222 223 while (*p=='\r' || *p=='\n' || *p=='|') 224 p++; 225 226 if (*p) return p; /* not-last text field */ 227 else return NULL; /* last text field */ 228 } 229 230 static subtitle_t *sub_read_line_microdvd(demux_sputext_t *this, subtitle_t *current) { 231 232 char line[LINE_LEN + 1]; 233 char line2[LINE_LEN + 1]; 234 char *p, *next; 235 int i; 236 237 memset (current, 0, sizeof(subtitle_t)); 238 239 current->end=-1; 240 do { 241 if (!read_line_from_input (this, line, LINE_LEN)) return NULL; 242 } while ((sscanf (line, "{%ld}{}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), line2) !=2) && 243 (sscanf (line, "{%ld}{%ld}%" LINE_LEN_QUOT "[^\r\n]", &(current->start), &(current->end),line2) !=3) 244 ); 245 246 p=line2; 247 248 next=p, i=0; 249 while ((next =sub_readtext (next, &(current->text[i])))) { 250 if (current->text[i]==ERR) return ERR; 251 i++; 252 if (i>=SUB_MAX_TEXT) { 253 printf ("Too many lines in a subtitle\n"); 254 current->lines=i; 255 return current; 256 } 257 } 258 current->lines= ++i; 259 260 return current; 261 } 262 263 static subtitle_t *sub_read_line_subviewer(demux_sputext_t *this, subtitle_t *current) { 264 265 char line[LINE_LEN + 1]; 266 int a1,a2,a3,a4,b1,b2,b3,b4; 267 char *p=NULL, *q=NULL; 268 int len; 269 270 memset (current, 0, sizeof(subtitle_t)); 271 272 while (1) { 273 if (!read_line_from_input(this, line, LINE_LEN)) return NULL; 274 if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) { 275 if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4) < 8) 276 continue; 277 } 278 current->start = a1*360000+a2*6000+a3*100+a4; 279 current->end = b1*360000+b2*6000+b3*100+b4; 280 281 if (!read_line_from_input(this, line, LINE_LEN)) 282 return NULL; 283 284 p=q=line; 285 for (current->lines=1; current->lines <= SUB_MAX_TEXT; current->lines++) { 286 for (q=p,len=0; *p && *p!='\r' && *p!='\n' && *p!='|' && strncasecmp(p,"[br]",4); p++,len++); 287 current->text[current->lines-1]=(char *)malloc (len+1); 288 if (!current->text[current->lines-1]) return ERR; 289 strncpy (current->text[current->lines-1], q, len); 290 current->text[current->lines-1][len]='\0'; 291 if (!*p || *p=='\r' || *p=='\n') break; 292 if (*p=='[') while (*p++!=']'); 293 if (*p=='|') p++; 294 } 295 if (current->lines > SUB_MAX_TEXT) current->lines = SUB_MAX_TEXT; 296 break; 297 } 298 return current; 299 } 300 301 static subtitle_t *sub_read_line_subrip(demux_sputext_t *this,subtitle_t *current) { 302 char line[LINE_LEN + 1]; 303 int a1,a2,a3,a4,b1,b2,b3,b4; 304 int i,end_sub; 305 306 memset(current,0,sizeof(subtitle_t)); 307 do { 308 if(!read_line_from_input(this,line,LINE_LEN)) 309 return NULL; 310 i = sscanf(line,"%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4); 311 } while(i < 8); 312 current->start = a1*360000+a2*6000+a3*100+a4/10; 313 current->end = b1*360000+b2*6000+b3*100+b4/10; 314 i=0; 315 end_sub=0; 316 do { 317 char *p; /* pointer to the curently read char */ 318 char temp_line[SUB_BUFSIZE]; /* subtitle line that will be transfered to current->text[i] */ 319 int temp_index; /* ... and its index wich 'points' to the first EMPTY place -> last read char is at temp_index-1 if temp_index>0 */ 320 temp_line[SUB_BUFSIZE-1]='\0'; /* just in case... */ 321 if(!read_line_from_input(this,line,LINE_LEN)) { 322 if(i) 323 break; /* if something was read, transmit it */ 324 else 325 return NULL; /* if not, repport EOF */ 326 } 327 for(temp_index=0,p=line;*p!='\0' && !end_sub && temp_index<SUB_BUFSIZE && i<SUB_MAX_TEXT;p++) { 328 switch(*p) { 329 case '\\': 330 if(*(p+1)=='N' || *(p+1)=='n') { 331 temp_line[temp_index++]='\0'; /* end of curent line */ 332 p++; 333 } else 334 temp_line[temp_index++]=*p; 335 break; 336 case '{': 337 #if 0 /* italic not implemented in renderer, ignore them for now */ 338 if(!strncmp(p,"{\\i1}",5) && temp_index+3<SUB_BUFSIZE) { 339 temp_line[temp_index++]='<'; 340 temp_line[temp_index++]='i'; 341 temp_line[temp_index++]='>'; 342 #else 343 if(!strncmp(p,"{\\i1}",5)) { 344 #endif 345 p+=4; 346 } 347 #if 0 /* italic not implemented in renderer, ignore them for now */ 348 else if(!strncmp(p,"{\\i0}",5) && temp_index+4<SUB_BUFSIZE) { 349 temp_line[temp_index++]='<'; 350 temp_line[temp_index++]='/'; 351 temp_line[temp_index++]='i'; 352 temp_line[temp_index++]='>'; 353 #else 354 else if(!strncmp(p,"{\\i0}",5)) { 355 #endif 356 p+=4; 357 } 358 else 359 temp_line[temp_index++]=*p; 360 break; 361 case '\r': /* just ignore '\r's */ 362 break; 363 case '\n': 364 temp_line[temp_index++]='\0'; 365 break; 366 default: 367 temp_line[temp_index++]=*p; 368 break; 369 } 370 if(temp_index>0) { 371 if(temp_index==SUB_BUFSIZE) 372 printf("Too many characters in a subtitle line\n"); 373 if(temp_line[temp_index-1]=='\0' || temp_index==SUB_BUFSIZE) { 374 if(temp_index>1) { /* more than 1 char (including '\0') -> that is a valid one */ 375 current->text[i]=(char *)malloc(temp_index); 376 if(!current->text[i]) 377 return ERR; 378 strncpy(current->text[i],temp_line,temp_index); /* temp_index<=SUB_BUFSIZE is always true here */ 379 i++; 380 temp_index=0; 381 } else 382 end_sub=1; 383 } 384 } 385 } 386 } while(i<SUB_MAX_TEXT && !end_sub); 387 if(i>=SUB_MAX_TEXT) 388 printf("Too many lines in a subtitle\n"); 389 current->lines=i; 390 return current; 391 } 392 393 static subtitle_t *sub_read_line_vplayer(demux_sputext_t *this,subtitle_t *current) { 394 char line[LINE_LEN + 1]; 395 int a1,a2,a3,b1,b2,b3; 396 char *p=NULL, *next, *p2; 397 int i; 398 399 memset (current, 0, sizeof(subtitle_t)); 400 401 while (!current->text[0]) { 402 if( this->next_line[0] == '\0' ) { /* if the buffer is empty.... */ 403 if( !read_line_from_input(this, line, LINE_LEN) ) return NULL; 404 } else { 405 /* ... get the current line from buffer. */ 406 strncpy( line, this->next_line, LINE_LEN); 407 line[LINE_LEN] = '\0'; /* I'm scared. This makes me feel better. */ 408 this->next_line[0] = '\0'; /* mark the buffer as empty. */ 409 } 410 /* Initialize buffer with next line */ 411 if( ! read_line_from_input( this, this->next_line, LINE_LEN) ) { 412 this->next_line[0] = '\0'; 413 return NULL; 414 } 415 if( (sscanf( line, "%d:%d:%d:", &a1, &a2, &a3) < 3) || 416 (sscanf( this->next_line, "%d:%d:%d:", &b1, &b2, &b3) < 3) ) 417 continue; 418 current->start = a1*360000+a2*6000+a3*100; 419 current->end = b1*360000+b2*6000+b3*100; 420 if ((current->end - current->start) > LINE_LEN) 421 current->end = current->start + LINE_LEN; /* not too long though. */ 422 /* teraz czas na wkopiowanie stringu */ 423 p=line; 424 /* finds the body of the subtitle_t */ 425 for (i=0; i<3; i++){ 426 p2=strchr( p, ':'); 427 if( p2 == NULL ) break; 428 p=p2+1; 429 } 430 431 next=p; 432 i=0; 433 while( (next = sub_readtext( next, &(current->text[i]))) ) { 434 if (current->text[i]==ERR) 435 return ERR; 436 i++; 437 if (i>=SUB_MAX_TEXT) { 438 printf("Too many lines in a subtitle\n"); 439 current->lines=i; 440 return current; 441 } 442 } 443 current->lines=++i; 444 } 445 return current; 446 } 447 448 static subtitle_t *sub_read_line_rt(demux_sputext_t *this,subtitle_t *current) { 449 /* 450 * TODO: This format uses quite rich (sub/super)set of xhtml 451 * I couldn't check it since DTD is not included. 452 * WARNING: full XML parses can be required for proper parsing 453 */ 454 char line[LINE_LEN + 1]; 455 int a1,a2,a3,a4,b1,b2,b3,b4; 456 char *p=NULL,*next=NULL; 457 int i,len,plen; 458 459 memset (current, 0, sizeof(subtitle_t)); 460 461 while (!current->text[0]) { 462 if (!read_line_from_input(this, line, LINE_LEN)) return NULL; 463 /* 464 * TODO: it seems that format of time is not easily determined, it may be 1:12, 1:12.0 or 0:1:12.0 465 * to describe the same moment in time. Maybe there are even more formats in use. 466 */ 467 if ((len=sscanf (line, "<Time Begin=\"%d:%d:%d.%d\" End=\"%d:%d:%d.%d\"",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8) 468 469 plen=a1=a2=a3=a4=b1=b2=b3=b4=0; 470 if ( 471 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&plen)) < 4) && 472 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&b2,&b3,&b4,&plen)) < 5) && 473 /* ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&plen)) < 5) && */ 474 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d.%d\" %*[Ee]nd=\"%d:%d.%d\"%*[^<]<clear/>%n",&a2,&a3,&a4,&b2,&b3,&b4,&plen)) < 6) && 475 ((len=sscanf (line, "<%*[tT]ime %*[bB]egin=\"%d:%d:%d.%d\" %*[Ee]nd=\"%d:%d:%d.%d\"%*[^<]<clear/>%n",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4,&plen)) < 8) 476 ) 477 continue; 478 current->start = a1*360000+a2*6000+a3*100+a4/10; 479 current->end = b1*360000+b2*6000+b3*100+b4/10; 480 p=line; p+=plen;i=0; 481 /* TODO: I don't know what kind of convention is here for marking multiline subs, maybe <br/> like in xml? */ 482 next = strstr(line,"<clear/>")+8;i=0; 483 while ((next =sub_readtext (next, &(current->text[i])))) { 484 if (current->text[i]==ERR) 485 return ERR; 486 i++; 487 if (i>=SUB_MAX_TEXT) { 488 printf("Too many lines in a subtitle\n"); 489 current->lines=i; 490 return current; 491 } 492 } 493 current->lines=i+1; 494 } 495 return current; 496 } 497 498 static subtitle_t *sub_read_line_ssa(demux_sputext_t *this,subtitle_t *current) { 499 int comma; 500 static int max_comma = 32; /* let's use 32 for the case that the */ 501 /* amount of commas increase with newer SSA versions */ 502 503 int hour1, min1, sec1, hunsec1, hour2, min2, sec2, hunsec2, nothing; 504 int num; 505 char line[LINE_LEN + 1], line3[LINE_LEN + 1], *line2; 506 char *tmp; 507 508 do { 509 if (!read_line_from_input(this, line, LINE_LEN)) return NULL; 510 } while (sscanf (line, "Dialogue: Marked=%d,%d:%d:%d.%d,%d:%d:%d.%d," 511 "%[^\n\r]", ¬hing, 512 &hour1, &min1, &sec1, &hunsec1, 513 &hour2, &min2, &sec2, &hunsec2, 514 line3) < 9 515 && 516 sscanf (line, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d," 517 "%[^\n\r]", ¬hing, 518 &hour1, &min1, &sec1, &hunsec1, 519 &hour2, &min2, &sec2, &hunsec2, 520 line3) < 9 ); 521 522 line2=strchr(line3, ','); 523 524 for (comma = 4; comma < max_comma; comma ++) 525 { 526 tmp = line2; 527 if(!(tmp=strchr(++tmp, ','))) break; 528 if(*(++tmp) == ' ') break; 529 /* a space after a comma means we're already in a sentence */ 530 line2 = tmp; 531 } 532 533 if(comma < max_comma)max_comma = comma; 534 /* eliminate the trailing comma */ 535 if(*line2 == ',') line2++; 536 537 current->lines=0;num=0; 538 current->start = 360000*hour1 + 6000*min1 + 100*sec1 + hunsec1; 539 current->end = 360000*hour2 + 6000*min2 + 100*sec2 + hunsec2; 540 541 while (((tmp=strstr(line2, "\\n")) != NULL) || ((tmp=strstr(line2, "\\N")) != NULL) ){ 542 current->text[num]=(char *)malloc(tmp-line2+1); 543 strncpy (current->text[num], line2, tmp-line2); 544 current->text[num][tmp-line2]='\0'; 545 line2=tmp+2; 546 num++; 547 current->lines++; 548 if (current->lines >= SUB_MAX_TEXT) return current; 549 } 550 551 current->text[num]=strdup(line2); 552 current->lines++; 553 554 return current; 555 } 556 557 /* Sylvain "Skarsnik" Colinet <scolinet@gmail.com> 558 * From MPlayer subreader.c : 559 * 560 * PJS subtitles reader. 561 * That's the "Phoenix Japanimation Society" format. 562 * I found some of them in http://www.scriptsclub.org/ (used for anime). 563 * The time is in tenths of second. 564 * 565 * by set, based on code by szabi (dunnowhat sub format ;-) 566 */ 567 568 static subtitle_t *sub_read_line_pjs (demux_sputext_t *this, subtitle_t *current) { 569 char line[LINE_LEN + 1]; 570 char text[LINE_LEN + 1]; 571 char *s, *d; 572 573 memset (current, 0, sizeof(subtitle_t)); 574 575 if (!read_line_from_input(this, line, LINE_LEN)) 576 return NULL; 577 for (s = line; *s && isspace(*s); s++); 578 if (*s == 0) 579 return NULL; 580 if (sscanf (line, "%ld,%ld,", &(current->start), 581 &(current->end)) <2) 582 return ERR; 583 /* the files I have are in tenths of second */ 584 current->start *= 10; 585 current->end *= 10; 586 587 /* walk to the beggining of the string */ 588 for (; *s; s++) if (*s==',') break; 589 if (*s) { 590 for (s++; *s; s++) if (*s==',') break; 591 if (*s) s++; 592 } 593 if (*s!='"') { 594 return ERR; 595 } 596 /* copy the string to the text buffer */ 597 for (s++, d=text; *s && *s!='"'; s++, d++) 598 *d=*s; 599 *d=0; 600 current->text[0] = strdup(text); 601 current->lines = 1; 602 603 return current; 604 } 605 606 static subtitle_t *sub_read_line_mpsub (demux_sputext_t *this, subtitle_t *current) { 607 char line[LINE_LEN + 1]; 608 float a,b; 609 int num=0; 610 char *p, *q; 611 612 do { 613 if (!read_line_from_input(this, line, LINE_LEN)) 614 return NULL; 615 } while (sscanf (line, "%f %f", &a, &b) !=2); 616 617 this->mpsub_position += (a*100.0); 618 current->start = (int) this->mpsub_position; 619 this->mpsub_position += (b*100.0); 620 current->end = (int) this->mpsub_position; 621 622 while (num < SUB_MAX_TEXT) { 623 if (!read_line_from_input(this, line, LINE_LEN)) 624 return NULL; 625 626 p=line; 627 while (isspace(*p)) 628 p++; 629 630 if (eol(*p) && num > 0) 631 return current; 632 633 if (eol(*p)) 634 return NULL; 635 636 for (q=p; !eol(*q); q++); 637 *q='\0'; 638 if (strlen(p)) { 639 current->text[num]=strdup(p); 640 printf(">%s<\n",p); 641 current->lines = ++num; 642 } else { 643 if (num) 644 return current; 645 else 646 return NULL; 647 } 648 } 649 650 return NULL; 651 } 652 653 static subtitle_t *sub_read_line_aqt (demux_sputext_t *this, subtitle_t *current) { 654 char line[LINE_LEN + 1]; 655 656 memset (current, 0, sizeof(subtitle_t)); 657 658 while (1) { 659 /* try to locate next subtitle_t */ 660 if (!read_line_from_input(this, line, LINE_LEN)) 661 return NULL; 662 if (!(sscanf (line, "-->> %ld", &(current->start)) <1)) 663 break; 664 } 665 666 if (!read_line_from_input(this, line, LINE_LEN)) 667 return NULL; 668 669 sub_readtext((char *) &line,¤t->text[0]); 670 current->lines = 1; 671 current->end = -1; 672 673 if (!read_line_from_input(this, line, LINE_LEN)) 674 return current;; 675 676 sub_readtext((char *) &line,¤t->text[1]); 677 current->lines = 2; 678 679 if ((current->text[0]=="") && (current->text[1]=="")) { 680 return NULL; 681 } 682 683 return current; 684 } 685 686 static subtitle_t *sub_read_line_jacobsub(demux_sputext_t *this, subtitle_t *current) { 687 char line1[LINE_LEN], line2[LINE_LEN], directive[LINE_LEN], *p, *q; 688 unsigned a1, a2, a3, a4, b1, b2, b3, b4, comment = 0; 689 static unsigned jacoTimeres = 30; 690 static int jacoShift = 0; 691 692 memset(current, 0, sizeof(subtitle_t)); 693 memset(line1, 0, LINE_LEN); 694 memset(line2, 0, LINE_LEN); 695 memset(directive, 0, LINE_LEN); 696 while (!current->text[0]) { 697 if (!read_line_from_input(this, line1, LINE_LEN)) { 698 return NULL; 699 } 700 if (sscanf 701 (line1, "%u:%u:%u.%u %u:%u:%u.%u %" LINE_LEN_QUOT "[^\n\r]", &a1, &a2, &a3, &a4, 702 &b1, &b2, &b3, &b4, line2) < 9) { 703 if (sscanf(line1, "@%u @%u %" LINE_LEN_QUOT "[^\n\r]", &a4, &b4, line2) < 3) { 704 if (line1[0] == '#') { 705 int hours = 0, minutes = 0, seconds, delta, inverter = 706 1; 707 unsigned units = jacoShift; 708 switch (toupper(line1[1])) { 709 case 'S': 710 if (isalpha(line1[2])) { 711 delta = 6; 712 } else { 713 delta = 2; 714 } 715 if (sscanf(&line1[delta], "%d", &hours)) { 716 if (hours < 0) { 717 hours *= -1; 718 inverter = -1; 719 } 720 if (sscanf(&line1[delta], "%*d:%d", &minutes)) { 721 if (sscanf 722 (&line1[delta], "%*d:%*d:%d", 723 &seconds)) { 724 sscanf(&line1[delta], "%*d:%*d:%*d.%d", 725 &units); 726 } else { 727 hours = 0; 728 sscanf(&line1[delta], "%d:%d.%d", 729 &minutes, &seconds, &units); 730 minutes *= inverter; 731 } 732 } else { 733 hours = minutes = 0; 734 sscanf(&line1[delta], "%d.%d", &seconds, 735 &units); 736 seconds *= inverter; 737 } 738 jacoShift = 739 ((hours * 3600 + minutes * 60 + 740 seconds) * jacoTimeres + 741 units) * inverter; 742 } 743 break; 744 case 'T': 745 if (isalpha(line1[2])) { 746 delta = 8; 747 } else { 748 delta = 2; 749 } 750 sscanf(&line1[delta], "%u", &jacoTimeres); 751 break; 752 } 753 } 754 continue; 755 } else { 756 current->start = 757 (unsigned long) ((a4 + jacoShift) * 100.0 / 758 jacoTimeres); 759 current->end = 760 (unsigned long) ((b4 + jacoShift) * 100.0 / 761 jacoTimeres); 762 } 763 } else { 764 current->start = 765 (unsigned 766 long) (((a1 * 3600 + a2 * 60 + a3) * jacoTimeres + a4 + 767 jacoShift) * 100.0 / jacoTimeres); 768 current->end = 769 (unsigned 770 long) (((b1 * 3600 + b2 * 60 + b3) * jacoTimeres + b4 + 771 jacoShift) * 100.0 / jacoTimeres); 772 } 773 current->lines = 0; 774 p = line2; 775 while ((*p == ' ') || (*p == '\t')) { 776 ++p; 777 } 778 if (isalpha(*p)||*p == '[') { 779 int cont, jLength; 780 781 if (sscanf(p, "%s %" LINE_LEN_QUOT "[^\n\r]", directive, line1) < 2) 782 return ERR; 783 jLength = strlen(directive); 784 for (cont = 0; cont < jLength; ++cont) { 785 if (isalpha(*(directive + cont))) 786 *(directive + cont) = toupper(*(directive + cont)); 787 } 788 if ((strstr(directive, "RDB") != NULL) 789 || (strstr(directive, "RDC") != NULL) 790 || (strstr(directive, "RLB") != NULL) 791 || (strstr(directive, "RLG") != NULL)) { 792 continue; 793 } 794 /* no alignment */ 795 #if 0 796 if (strstr(directive, "JL") != NULL) { 797 current->alignment = SUB_ALIGNMENT_HLEFT; 798 } else if (strstr(directive, "JR") != NULL) { 799 current->alignment = SUB_ALIGNMENT_HRIGHT; 800 } else { 801 current->alignment = SUB_ALIGNMENT_HCENTER; 802 } 803 #endif 804 strcpy(line2, line1); 805 p = line2; 806 } 807 for (q = line1; (!eol(*p)) && (current->lines < SUB_MAX_TEXT); ++p) { 808 switch (*p) { 809 case '{': 810 comment++; 811 break; 812 case '}': 813 if (comment) { 814 --comment; 815 /* the next line to get rid of a blank after the comment */ 816 if ((*(p + 1)) == ' ') 817 p++; 818 } 819 break; 820 case '~': 821 if (!comment) { 822 *q = ' '; 823 ++q; 824 } 825 break; 826 case ' ': 827 case '\t': 828 if ((*(p + 1) == ' ') || (*(p + 1) == '\t')) 829 break; 830 if (!comment) { 831 *q = ' '; 832 ++q; 833 } 834 break; 835 case '\\': 836 if (*(p + 1) == 'n') { 837 *q = '\0'; 838 q = line1; 839 current->text[current->lines++] = strdup(line1); 840 ++p; 841 break; 842 } 843 if ((toupper(*(p + 1)) == 'C') 844 || (toupper(*(p + 1)) == 'F')) { 845 ++p,++p; 846 break; 847 } 848 if ((*(p + 1) == 'B') || (*(p + 1) == 'b') || 849 /* actually this means "insert current date here" */ 850 (*(p + 1) == 'D') || 851 (*(p + 1) == 'I') || (*(p + 1) == 'i') || 852 (*(p + 1) == 'N') || 853 /* actually this means "insert current time here" */ 854 (*(p + 1) == 'T') || 855 (*(p + 1) == 'U') || (*(p + 1) == 'u')) { 856 ++p; 857 break; 858 } 859 if ((*(p + 1) == '\\') || 860 (*(p + 1) == '~') || (*(p + 1) == '{')) { 861 ++p; 862 } else if (eol(*(p + 1))) { 863 if (!read_line_from_input(this, directive, LINE_LEN)) 864 return NULL; 865 trail_space(directive); 866 strncat(line2, directive, 867 (LINE_LEN > 511) ? LINE_LEN : 511); 868 break; 869 } 870 default: 871 if (!comment) { 872 *q = *p; 873 ++q; 874 } 875 } 876 } 877 *q = '\0'; 878 current->text[current->lines] = strdup(line1); 879 } 880 current->lines++; 881 return current; 882 } 883 884 static subtitle_t *sub_read_line_subviewer2(demux_sputext_t *this, subtitle_t *current) { 885 char line[LINE_LEN+1]; 886 int a1,a2,a3,a4; 887 char *p=NULL; 888 int i,len; 889 890 while (!current->text[0]) { 891 if (!read_line_from_input(this, line, LINE_LEN)) return NULL; 892 if (line[0]!='{') 893 continue; 894 if ((len=sscanf (line, "{T %d:%d:%d:%d",&a1,&a2,&a3,&a4)) < 4) 895 continue; 896 current->start = a1*360000+a2*6000+a3*100+a4/10; 897 for (i=0; i<SUB_MAX_TEXT;) { 898 if (!read_line_from_input(this, line, LINE_LEN)) break; 899 if (line[0]=='}') break; 900 len=0; 901 for (p=line; *p!='\n' && *p!='\r' && *p; ++p,++len); 902 if (len) { 903 current->text[i]=(char *)malloc (len+1); 904 if (!current->text[i]) return ERR; 905 strncpy (current->text[i], line, len); current->text[i][len]='\0'; 906 ++i; 907 } else { 908 break; 909 } 910 } 911 current->lines=i; 912 } 913 return current; 914 } 915 916 static subtitle_t *sub_read_line_subrip09 (demux_sputext_t *this, subtitle_t *current) { 917 char line[LINE_LEN + 1]; 918 char *next; 919 int h, m, s; 920 int i; 921 922 memset (current, 0, sizeof(subtitle_t)); 923 924 do { 925 if (!read_line_from_input (this, line, LINE_LEN)) return NULL; 926 } while (sscanf (line, "[%d:%d:%d]", &h, &m, &s) != 3); 927 928 if (!read_line_from_input (this, line, LINE_LEN)) return NULL; 929 930 current->start = 360000 * h + 6000 * m + 100 * s; 931 current->end = -1; 932 933 next=line; 934 i=0; 935 while ((next = sub_readtext (next, &(current->text[i])))) { 936 if (current->text[i]==ERR) return ERR; 937 i++; 938 if (i>=SUB_MAX_TEXT) { 939 printf("Too many lines in a subtitle\n"); 940 current->lines=i; 941 return current; 942 } 943 } 944 current->lines= ++i; 945 946 return current; 947 } 948 949 /* Code from subreader.c of MPlayer 950 ** Sylvain "Skarsnik" Colinet <scolinet@gmail.com> 951 */ 952 953 static subtitle_t *sub_read_line_mpl2(demux_sputext_t *this, subtitle_t *current) { 954 char line[LINE_LEN+1]; 955 char line2[LINE_LEN+1]; 956 char *p, *next; 957 int i; 958 959 memset (current, 0, sizeof(subtitle_t)); 960 do { 961 if (!read_line_from_input (this, line, LINE_LEN)) return NULL; 962 } while ((sscanf (line, 963 "[%ld][%ld]%[^\r\n]", 964 &(current->start), &(current->end), line2) < 3)); 965 current->start *= 10; 966 current->end *= 10; 967 p=line2; 968 969 next=p, i=0; 970 while ((next = sub_readtext (next, &(current->text[i])))) { 971 if (current->text[i] == ERR) {return ERR;} 972 i++; 973 if (i >= SUB_MAX_TEXT) { 974 printf("Too many lines in a subtitle\n"); 975 current->lines = i; 976 return current; 977 } 978 } 979 current->lines= ++i; 980 981 return current; 982 } 983 984 985 static int sub_autodetect (demux_sputext_t *this) { 986 987 char line[LINE_LEN + 1]; 988 int i, j=0; 989 char p; 990 991 while (j < 100) { 992 j++; 993 if (!read_line_from_input(this, line, LINE_LEN)) 994 return FORMAT_UNKNOWN; 995 996 if ((sscanf (line, "{%d}{}", &i)==1) || 997 (sscanf (line, "{%d}{%d}", &i, &i)==2)) { 998 this->uses_time=0; 999 return FORMAT_MICRODVD; 1000 } 1001 1002 if (sscanf (line, "%d:%d:%d%*[,.]%d --> %d:%d:%d%*[,.]%d", &i, &i, &i, &i, &i, &i, &i, &i)==8) { 1003 this->uses_time=1; 1004 return FORMAT_SUBRIP; 1005 } 1006 1007 if (sscanf (line, "%d:%d:%d.%d,%d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){ 1008 this->uses_time=1; 1009 return FORMAT_SUBVIEWER; 1010 } 1011 1012 if (sscanf (line, "%d:%d:%d,%d,%d:%d:%d,%d", &i, &i, &i, &i, &i, &i, &i, &i)==8){ 1013 this->uses_time=1; 1014 return FORMAT_SUBVIEWER; 1015 } 1016 1017 if (strstr (line, "<SAMI>")) { 1018 this->uses_time=1; 1019 return FORMAT_SAMI; 1020 } 1021 if (sscanf (line, "%d:%d:%d:", &i, &i, &i )==3) { 1022 this->uses_time=1; 1023 return FORMAT_VPLAYER; 1024 } 1025 /* 1026 * A RealText format is a markup language, starts with <window> tag, 1027 * options (behaviour modifiers) are possible. 1028 */ 1029 if ( !strcasecmp(line, "<window") ) { 1030 this->uses_time=1; 1031 return FORMAT_RT; 1032 } 1033 if ((!memcmp(line, "Dialogue: Marked", 16)) || (!memcmp(line, "Dialogue: ", 10))) { 1034 this->uses_time=1; 1035 return FORMAT_SSA; 1036 } 1037 if (sscanf (line, "%d,%d,\"%c", &i, &i, (char *) &i) == 3) { 1038 this->uses_time=0; 1039 return FORMAT_PJS; 1040 } 1041 if (sscanf (line, "FORMAT=%d", &i) == 1) { 1042 this->uses_time=0; 1043 return FORMAT_MPSUB; 1044 } 1045 if (sscanf (line, "FORMAT=TIM%c", &p)==1 && p=='E') { 1046 this->uses_time=1; 1047 return FORMAT_MPSUB; 1048 } 1049 if (strstr (line, "-->>")) { 1050 this->uses_time=0; 1051 return FORMAT_AQTITLE; 1052 } 1053 if (sscanf(line, "@%d @%d", &i, &i) == 2 || 1054 sscanf(line, "%d:%d:%d.%d %d:%d:%d.%d", &i, &i, &i, &i, &i, &i, &i, &i) == 8) { 1055 this->uses_time = 1; 1056 return FORMAT_JACOBSUB; 1057 } 1058 if (sscanf(line, "{T %d:%d:%d:%d",&i, &i, &i, &i) == 4) { 1059 this->uses_time = 1; 1060 return FORMAT_SUBVIEWER2; 1061 } 1062 if (sscanf(line, "[%d:%d:%d]", &i, &i, &i) == 3) { 1063 this->uses_time = 1; 1064 return FORMAT_SUBRIP09; 1065 } 1066 1067 if (sscanf (line, "[%d][%d]", &i, &i) == 2) { 1068 this->uses_time = 1; 1069 return FORMAT_MPL2; 1070 } 1071 } 1072 return FORMAT_UNKNOWN; /* too many bad lines */ 1073 } 1074 1075 subtitle_t *sub_read_file (demux_sputext_t *this) { 1076 1077 int n_max; 1078 int timeout; 1079 subtitle_t *first; 1080 subtitle_t * (*func[])(demux_sputext_t *this,subtitle_t *dest)= 1081 { 1082 sub_read_line_microdvd, 1083 sub_read_line_subrip, 1084 sub_read_line_subviewer, 1085 sub_read_line_sami, 1086 sub_read_line_vplayer, 1087 sub_read_line_rt, 1088 sub_read_line_ssa, 1089 sub_read_line_pjs, 1090 sub_read_line_mpsub, 1091 sub_read_line_aqt, 1092 sub_read_line_jacobsub, 1093 sub_read_line_subviewer2, 1094 sub_read_line_subrip09, 1095 sub_read_line_mpl2, 1096 }; 1097 1098 /* Rewind (sub_autodetect() needs to read input from the beginning) */ 1099 if(fseek(this->file_ptr, 0, SEEK_SET) == -1) { 1100 printf("seek failed.\n"); 1101 return NULL; 1102 } 1103 this->buflen = 0; 1104 1105 this->format=sub_autodetect (this); 1106 if (this->format==FORMAT_UNKNOWN) { 1107 return NULL; 1108 } 1109 1110 /*printf("Detected subtitle file format: %d\n", this->format);*/ 1111 1112 /* Rewind */ 1113 if(fseek(this->file_ptr, 0, SEEK_SET) == -1) { 1114 printf("seek failed.\n"); 1115 return NULL; 1116 } 1117 this->buflen = 0; 1118 1119 this->num=0;n_max=32; 1120 first = (subtitle_t *) malloc(n_max*sizeof(subtitle_t)); 1121 if(!first) return NULL; 1122 timeout = MAX_TIMEOUT; 1123 1124 if (this->uses_time) timeout *= 100; 1125 else timeout *= 10; 1126 1127 while(1) { 1128 subtitle_t *sub; 1129 1130 if(this->num>=n_max){ 1131 n_max+=16; 1132 first=realloc(first,n_max*sizeof(subtitle_t)); 1133 } 1134 1135 sub = func[this->format] (this, &first[this->num]); 1136 1137 if (!sub) 1138 break; /* EOF */ 1139 1140 if (sub==ERR) 1141 ++this->errs; 1142 else { 1143 if (this->num > 0 && first[this->num-1].end == -1) { 1144 /* end time not defined in the subtitle */ 1145 if (timeout > 0) { 1146 /* timeout */ 1147 if (timeout > sub->start - first[this->num-1].start) { 1148 first[this->num-1].end = sub->start; 1149 } else 1150 first[this->num-1].end = first[this->num-1].start + timeout; 1151 } else { 1152 /* no timeout */ 1153 first[this->num-1].end = sub->start; 1154 } 1155 } 1156 ++this->num; /* Error vs. Valid */ 1157 } 1158 } 1159 /* timeout of last subtitle */ 1160 if (this->num > 0 && first[this->num-1].end == -1) 1161 if (timeout > 0) { 1162 first[this->num-1].end = first[this->num-1].start + timeout; 1163 } 1164 1165 #ifdef DEBUX_XINE_DEMUX_SPUTEXT 1166 { 1167 char buffer[1024]; 1168 1169 sprintf(buffer, "Read %i subtitles", this->num); 1170 1171 if(this->errs) 1172 sprintf(buffer + strlen(buffer), ", %i bad line(s).\n", this->errs); 1173 else 1174 strcat(buffer, "\n"); 1175 1176 printf("%s", buffer); 1177 } 1178 #endif 1179 1180 return first; 1181 } 1182 -
new file libs/libmythtv/xine_demux_sputext.h
- + 1 #ifndef XINE_DEMUX_SPUTEXT_H 2 #define XINE_DEMUX_SPUTEXT_H 3 4 #ifdef __cplusplus 5 extern "C" { 6 #endif 7 8 #include <ctype.h> 9 #include <stdio.h> 10 11 #define SUB_BUFSIZE 1024 12 #define SUB_MAX_TEXT 5 13 #define MAX_TIMEOUT 4 14 15 #define DEBUG_XINE_DEMUX_SPUTEXT 1 16 17 typedef struct { 18 19 int lines; ///< Count of text lines in this subtitle set. 20 21 long start; ///< Starting time in msec or starting frame 22 long end; ///< Ending time in msec or starting frame 23 24 char *text[SUB_MAX_TEXT]; ///< The subtitle text lines. 25 } subtitle_t; 26 27 typedef struct { 28 29 FILE* file_ptr; 30 31 int status; 32 33 char buf[SUB_BUFSIZE]; 34 off_t buflen; 35 36 float mpsub_position; 37 38 int uses_time; 39 int errs; 40 subtitle_t *subtitles; 41 int num; /* number of subtitle structs */ 42 int cur; /* current subtitle */ 43 int format; /* constants see below */ 44 char next_line[SUB_BUFSIZE]; /* a buffer for next line read from file */ 45 46 } demux_sputext_t; 47 48 49 subtitle_t *sub_read_file (demux_sputext_t*); 50 51 #ifdef __cplusplus 52 } 53 #endif 54 55 #endif -
libs/libmythtv/tv_play.cpp
old new 8 8 #include <qregexp.h> 9 9 #include <qfile.h> 10 10 #include <qtimer.h> 11 #include <qdir.h> 11 12 12 13 #include "mythdbcon.h" 13 14 #include "tv_play.h" … … 1283 1284 nvp->SetLength(playbackLen); 1284 1285 nvp->SetExactSeeks(gContext->GetNumSetting("ExactSeeking", 0)); 1285 1286 nvp->SetAutoCommercialSkip(autoCommercialSkip); 1287 FindAndLoadExternalSubs(*nvp, prbuffer->GetFilename()); 1286 1288 nvp->SetLiveTVChain(tvchain); 1287 1289 1288 1290 nvp->SetAudioStretchFactor(normal_speed); … … 6516 6518 typeStr = "SUBTITLE"; 6517 6519 selStr = "SELECTSUBTITLE_"; 6518 6520 grpStr = "SUBTITLEGROUP"; 6519 sel = activenvp->GetCaptionMode() & kDisplay Subtitle;6521 sel = activenvp->GetCaptionMode() & kDisplayAVSubtitle; 6520 6522 } 6521 6523 else if (kTrackTypeCC608 == type) 6522 6524 { … … 6970 6972 nvp->ITVRestart(chanid, cardid, isLive); 6971 6973 } 6972 6974 6975 /* \fn TV::FindAndLoadExternalSubs(NuppelVideoPlayer&, QString) 6976 * \brief Tries to find an external subtitle file in the same directory 6977 * in which the video file is. Tries to parse each found candidate file 6978 * until one is parsed succesfully. 6979 */ 6980 void TV::FindAndLoadExternalSubs(NuppelVideoPlayer& target, QString videoFile) 6981 { 6982 if (videoFile.length() < 1) 6983 return; 6984 6985 QString fileName = ""; 6986 QString dirName = ""; 6987 int dirPos = videoFile.findRev(QChar('/')); 6988 if (dirPos > 0) 6989 { 6990 fileName = videoFile.mid(dirPos + 1); 6991 dirName = videoFile.left(dirPos); 6992 } 6993 else 6994 { 6995 fileName = videoFile; 6996 dirName = "."; 6997 } 6998 6999 QString baseName = ""; 7000 int suffixPos = fileName.findRev(QChar('.')); 7001 if (suffixPos > 0) 7002 baseName = fileName.left(suffixPos); 7003 else 7004 baseName = fileName; 7005 7006 // the dir listing does not work if the filename has the following chars, 7007 // so we convert them to the wildcard '?' 7008 baseName = 7009 baseName.replace("[", "?").replace("]", "?").replace("(", "?") 7010 .replace(")", "?"); 7011 7012 // some Qt versions do not accept paths in the search string of 7013 // entryList() so we have to set the dir first 7014 QDir dir; 7015 dir.setPath(dirName); 7016 7017 // try to find files with the same base name, but ending with 7018 // '.srt', '.sub', or '.txt' 7019 QStringList candidates = dir.entryList( 7020 baseName + "*.srt; " + baseName + "*.sub; " + baseName + "*.txt;"); 7021 7022 for (unsigned i = 0; i < candidates.size(); ++i) 7023 { 7024 if (nvp->LoadExternalSubtitles(dirName + "/" + candidates[i])) 7025 { 7026 VERBOSE( 7027 VB_PLAYBACK, 7028 LOC + QString("Loaded text subtitles from %1."). 7029 arg(candidates[i])); 7030 return; 7031 } 7032 } 7033 } 7034 6973 7035 /* vim: set expandtab tabstop=4 shiftwidth=4: */ -
new file libs/libmythtv/textsubtitleparser.cpp
- + 1 // -*- Mode: c++ -*- 2 /** TextSubtitles 3 * Copyright (c) 2006 by Pekka Jääskeläinen 4 * Distributed as part of MythTV under GPL v2 and later. 5 */ 6 7 #include <stdio.h> 8 #include <qtextcodec.h> 9 #include <algorithm> 10 11 using std::lower_bound; 12 13 #include "textsubtitleparser.h" 14 #include "xine_demux_sputext.h" 15 16 bool operator<(const text_subtitle_t& left, 17 const text_subtitle_t& right) 18 { 19 return left.start < right.start; 20 } 21 22 23 /** \fn TextSubtitles::SubtitleChanged(uint64_t timecode) const 24 * \brief Returns true in case the subtitle to display has changed since 25 * the last Subtitles() call. 26 * 27 * This is used to avoid redisplaying subtitles that are already displaying. 28 * 29 * \param timecode The timecode (frame number or time stamp) of the 30 * current video position. 31 * \return True in case new subtitles should be displayed. 32 */ 33 bool TextSubtitles::SubtitleChanged(uint64_t timecode) const 34 { 35 return timecode < m_lastReturnedSubtitle.start || 36 timecode > m_lastReturnedSubtitle.end; 37 } 38 39 /** \fn TextSubtitles::Subtitles(uint64_t timecode) const 40 * \brief Returns the subtitles to display at the given timecode. 41 * 42 * \param timecode The timecode (frame number or time stamp) of the 43 * current video position. 44 * \return The subtitles as a list of strings. 45 */ 46 QStringList TextSubtitles::Subtitles(uint64_t timecode) const 47 { 48 if (m_subtitles.size() == 0) 49 return QStringList(); 50 51 text_subtitle_t searchTarget(timecode, timecode); 52 53 TextSubtitleList::const_iterator nextSubPos = 54 lower_bound(m_subtitles.begin(), m_subtitles.end(), searchTarget); 55 56 uint64_t startCode = 0, endCode = 0; 57 if (nextSubPos != m_subtitles.begin()) 58 { 59 TextSubtitleList::const_iterator currentSubPos = nextSubPos; 60 currentSubPos--; 61 62 const text_subtitle_t& sub = *currentSubPos; 63 if (sub.start <= timecode && sub.end >= timecode) 64 { 65 // found a sub to display 66 m_lastReturnedSubtitle = sub; 67 return QDeepCopy<QStringList>(m_lastReturnedSubtitle.textLines); 68 } 69 // the subtitle time span has ended, let's display a blank sub 70 startCode = sub.end + 1; 71 } 72 73 if (nextSubPos == m_subtitles.end()) 74 { 75 // at the end of video, the blank subtitle should last until 76 // forever 77 endCode = startCode + INT_MAX; 78 } 79 else 80 { 81 endCode = (*nextSubPos).start - 1; 82 } 83 // we are in a position in which there are no subtitles to display, 84 // return an empty subtitle and create a dummy empty subtitle for this 85 // time span so SubtitleChanged() functions also in this case 86 text_subtitle_t blankSub(startCode, endCode); 87 m_lastReturnedSubtitle = blankSub; 88 return QStringList(); 89 } 90 91 void TextSubtitles::AddSubtitle(const text_subtitle_t &newSub) 92 { 93 m_subtitles.push_back(newSub); 94 } 95 96 void TextSubtitles::Clear(void) 97 { 98 m_subtitles.clear(); 99 } 100 101 bool TextSubtitleParser::LoadSubtitles(QString fileName, TextSubtitles &target) 102 { 103 demux_sputext_t sub_data; 104 sub_data.file_ptr = fopen(fileName, "r"); 105 106 if (!sub_data.file_ptr) 107 return false; 108 109 subtitle_t *loaded_subs = sub_read_file(&sub_data); 110 if (!loaded_subs) 111 return false; 112 113 target.SetFrameBasedTiming(!sub_data.uses_time); 114 115 // convert the subtitles to our own format and free the original structures 116 for (int sub_i = 0; sub_i < sub_data.num; ++sub_i) 117 { 118 subtitle_t *sub = &loaded_subs[sub_i]; 119 text_subtitle_t newsub(sub->start, sub->end); 120 121 if (!target.IsFrameBasedTiming()) 122 { 123 newsub.start *= 10; // convert from csec to msec 124 newsub.end *= 10; 125 } 126 for (int line = 0; line < sub->lines; ++line) 127 { 128 newsub.textLines.push_back(QString(sub->text[line])); 129 free(sub->text[line]); 130 } 131 target.AddSubtitle(newsub); 132 } 133 134 free(loaded_subs); 135 fclose(sub_data.file_ptr); 136 137 return true; 138 } -
libs/libmythtv/osd.cpp
old new 23 23 #include "osdtypes.h" 24 24 #include "osdsurface.h" 25 25 #include "mythcontext.h" 26 #include "textsubtitleparser.h" 26 27 #include "libmyth/oldsettings.h" 27 28 #include "udpnotify.h" 28 29 … … 135 136 ok &= InitCC708(); 136 137 ok &= InitTeletext(); 137 138 ok &= InitMenu(); 138 ok &= Init DVBSub();139 ok &= InitSubtitles(); 139 140 ok &= InitInteractiveTV(); 140 141 return ok; 141 142 } … … 274 275 return true; 275 276 } 276 277 278 void OSD::SetTextSubtitles(const QStringList& lines) 279 { 280 const uint8_t SUBTITLE_FONT_SIZE = 18; 281 const uint8_t SUBTITLE_LINE_HEIGHT = 22; 282 const uint8_t MAX_CHARACTERS_PER_ROW = 50; 283 284 OSDSet* subtitleSet = GetSet("subtitles"); 285 if (subtitleSet == NULL) 286 return; 287 288 QString subText = ""; 289 int subLines = 0; 290 for (std::size_t i = 0; i < lines.size(); ++i) 291 { 292 293 const QString& line = lines[i]; 294 if (line.length() > MAX_CHARACTERS_PER_ROW) 295 { 296 // wrap long lines at word spaces 297 QStringList words = QStringList::split(" ", line); 298 QString newString = ""; 299 do 300 { 301 QString word = words.first(); 302 words.pop_front(); 303 304 if (newString.length() + word.length() + 1 > 305 MAX_CHARACTERS_PER_ROW) 306 { 307 // next word won't fit anymore, create a new line 308 subText.append(newString + "\n"); 309 ++subLines; 310 newString = ""; 311 } 312 newString.append(word + " "); 313 } 314 while(words.size() > 0); 315 316 subText.append(newString); 317 } 318 else 319 { 320 subText.append(line); 321 } 322 subText.append("\n"); 323 ++subLines; 324 } 325 326 ClearAll("subtitles"); 327 328 QString name = "text_subtitles"; 329 330 QRect area(0, displayheight - subLines * SUBTITLE_LINE_HEIGHT, 331 displaywidth, displayheight); 332 333 QString fontname = "text_subtitle_font"; 334 TTFFont *font = GetFont(fontname); 335 if (!font) 336 { 337 font = LoadFont(gContext->GetSetting("OSDCCFont"), SUBTITLE_FONT_SIZE); 338 339 if (font) 340 { 341 // set outline so we can see the font in white background video 342 font->setOutline(true); 343 fontMap[fontname] = font; 344 } 345 else 346 { 347 VERBOSE(VB_IMPORTANT, "Cannot load font for text subtitles."); 348 } 349 } 350 351 OSDTypeText *text = new OSDTypeText(name, font, "", area, wmult, hmult); 352 353 text->SetCentered(true); 354 text->SetMultiLine(true); 355 text->SetSelected(false); 356 text->SetText(subText); 357 text->SetSelected(false); 358 subtitleSet->AddType(text); 359 360 SetVisible(subtitleSet, 0); 361 } 362 363 void OSD::ClearTextSubtitles() 364 { 365 HideSet("subtitles"); 366 ClearAll("subtitles"); 367 } 368 277 369 bool OSD::InitMenu(void) 278 370 { 279 371 if (GetSet("menu")) … … 330 422 return true; 331 423 } 332 424 333 bool OSD::Init DVBSub(void)425 bool OSD::InitSubtitles(void) 334 426 { 335 // Create container for subtitles 427 // Create container for subtitles (DVB, DVD and external subs) 336 428 if (GetSet("subtitles")) 337 429 return true; 338 430 … … 341 433 new OSDSet(name, true, 342 434 osdBounds.width(), osdBounds.height(), 343 435 wmult, hmult, frameint); 436 344 437 container->SetPriority(30); 345 438 AddSet(container, name); 346 439 return true;