Ticket #1748: external_text_subtitle_file_support.diff

File external_text_subtitle_file_support.diff, 62.5 KB (added by Pekka Jääskeläinen <pekka.jaaskelainen@…>, 13 years ago)

patch against trunk

  • libs/libmythtv/NuppelVideoPlayer.cpp

    Adds text-based subtitle support to the internal video player.
    
    Such subtitles are provided in external files with extensions
    like '.srt', '.txt', or '.sub'. The implementation uses a parser 
    taken from Xine to read in the subtitles.
    
    Following formats are supported by the parser: MicroDVD, SubRip, 
    SubViewer, SAMI, VPlayer, RT, Sub Station Alpha, PJS, MPSUB, 
    AQTitle, JacobSub, Subviewer2, Subrip09, and Mplayer sub 2.
    
    old new  
    119119uint track_type_to_display_mode[kTrackTypeCount+2] =
    120120{
    121121    kDisplayNone,
    122     kDisplaySubtitle,
     122    kDisplayAVSubtitle,
    123123    kDisplayCC608,
    124124    kDisplayCC708,
    125125    kDisplayTeletextCaptions,
     
    287287    if (weMadeBuffer)
    288288        delete ringBuffer;
    289289
    290     if (osdHasSubtitles || nonDisplayedSubtitles.size() > 0)
     290    if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0)
    291291        ClearSubtitles();
    292292
    293293    if (osd)
     
    458458    forceVideoOutput = type;
    459459}
    460460
     461#include "debug.h"
     462
    461463bool NuppelVideoPlayer::InitVideo(void)
    462464{
    463465    InitFilters();
     
    10111013            deleteIter = deleteMap.begin();
    10121014        }
    10131015    }
     1016
     1017
    10141018 
    10151019    // need this til proper DVD bookmarking is implemented
    10161020    if (!ringBuffer->isDVD())
     
    14231427
    14241428            DisableTeletext();
    14251429        }
    1426         if (kDisplaySubtitle & mode)
     1430        if (kDisplayAVSubtitle & mode)
    14271431        {
    14281432            msg += decoder->GetTrackDesc(kTrackTypeSubtitle,
    14291433                                         GetTrack(kTrackTypeSubtitle));
    14301434        }
     1435        if (kDisplayTextSubtitle & mode)
     1436        {
     1437            msg += QObject::tr("Text subtitles");
     1438        }
    14311439        if (kDisplayCC608 & mode)
    14321440        {
    14331441            msg += decoder->GetTrackDesc(kTrackTypeCC608,
     
    14501458void NuppelVideoPlayer::EnableCaptions(uint mode)
    14511459{
    14521460    QString msg = "";
    1453     if (kDisplaySubtitle & mode)
     1461    if (kDisplayAVSubtitle & mode)
    14541462    {
    14551463        msg += decoder->GetTrackDesc(kTrackTypeSubtitle,
    14561464                                     GetTrack(kTrackTypeSubtitle));
    14571465    }
     1466    if (kDisplayTextSubtitle & mode)
     1467    {
     1468        msg += QObject::tr("Text Subtitles");
     1469    }
    14581470    if (kDisplayNUVTeletextCaptions & mode)
    14591471        msg += QObject::tr("TXT") + QString(" %1").arg(ttPageNum, 3, 16);
    14601472    if (kDisplayCC608 & mode)
     
    15441556bool NuppelVideoPlayer::ToggleCaptions(uint type)
    15451557{
    15461558    uint mode = track_type_to_display_mode[type];
     1559
    15471560    uint origMode = textDisplayMode;
    15481561
    15491562    if (ringBuffer->isDVD() && GetCaptionMode() > 0)
     
    15641577    if (kDisplayCC708 & mode)
    15651578        EnableCaptions(kDisplayCC708);
    15661579
    1567     if (kDisplaySubtitle & mode)
    1568         EnableCaptions(kDisplaySubtitle);
     1580    if (kDisplayAVSubtitle & mode)
     1581        EnableCaptions(kDisplayAVSubtitle);
     1582
     1583    if (kDisplayTextSubtitle & mode)
     1584        EnableCaptions(kDisplayTextSubtitle);
    15691585
    15701586    if (kDisplayTeletextCaptions & mode)
    15711587        EnableCaptions(kDisplayTeletextCaptions);
     
    15881604    // figure out which text type to enable..
    15891605    bool captions_found = true;
    15901606    if (decoder->GetTrackCount(kTrackTypeSubtitle))
    1591         EnableCaptions(kDisplaySubtitle);
     1607        EnableCaptions(kDisplayAVSubtitle);
     1608    else if (textSubtitles.SubtitleCount() > 0)
     1609        EnableCaptions(kDisplayTextSubtitle);
    15921610    else if (decoder->GetTrackCount(kTrackTypeCC708))
    15931611        EnableCaptions(kDisplayCC708);
    15941612    else if (decoder->GetTrackCount(kTrackTypeTeletextCaptions))
     
    23792397    if (textDisplayMode & kDisplayNUVCaptions)
    23802398        ShowText();
    23812399
    2382     // handle with DVB/DVD subtitles
    2383     if (textDisplayMode & kDisplaySubtitle)
    2384         DisplaySubtitles();
    2385     else if (osdHasSubtitles)
     2400    // handle DVB/DVD subtitles decoded by ffmpeg (in AVSubtitle format)
     2401    if (textDisplayMode & kDisplayAVSubtitle)
     2402        DisplayAVSubtitles();
     2403    else if (textDisplayMode & kDisplayTextSubtitle)
     2404        DisplayTextSubtitles();
     2405    else if (osdHasSubtitles)
    23862406        ClearSubtitles();
    23872407
    23882408    // handle scan type changes
     
    26162636    if (fftime <= 0)
    26172637        fftime = (int)(seconds * video_frame_rate);
    26182638
    2619     if (osdHasSubtitles || nonDisplayedSubtitles.size() > 0)
     2639    if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0)
    26202640       ClearSubtitles();
    26212641
    26222642    return fftime > CalcMaxFFTime(fftime, false);
     
    26302650    if (rewindtime <= 0)
    26312651        rewindtime = (int)(seconds * video_frame_rate);
    26322652
    2633     if (osdHasSubtitles || nonDisplayedSubtitles.size() > 0)
     2653    if (osdHasSubtitles || nonDisplayedAVSubtitles.size() > 0)
    26342654       ClearSubtitles();
    26352655
    26362656    return rewindtime >= framesPlayed;
     
    55575577    if (kTrackTypeSubtitle == type)
    55585578    {
    55595579        DisableCaptions(textDisplayMode, false);
    5560         EnableCaptions(kDisplaySubtitle);
     5580        EnableCaptions(kDisplayAVSubtitle);
    55615581    }
    55625582    else if (kTrackTypeCC708 == type)
    55635583    {
     
    57075727                SetTrack(kTrackTypeSubtitle, 0);
    57085728        }
    57095729    }
    5710     else if ((textDisplayMode & kDisplaySubtitle) &&
     5730    else if ((textDisplayMode & kDisplayAVSubtitle) &&
    57115731             (vbimode == VBIMode::PAL_TT))
    57125732    {
    57135733        if ((uint)GetTrack(kTrackTypeSubtitle) + 1 <
     
    57675787        else
    57685788            SetCaptionsEnabled(false);
    57695789    }
    5770     else if ((textDisplayMode & kDisplaySubtitle) &&
     5790    else if ((textDisplayMode & kDisplayAVSubtitle) &&
    57715791             (vbimode == VBIMode::NTSC_CC))
    57725792    {
    57735793        if ((uint)GetTrack(kTrackTypeSubtitle) + 1 <
     
    57805800    }
    57815801}
    57825802
    5783 // updates new subtitles to the screen and clears old ones
    5784 void NuppelVideoPlayer::DisplaySubtitles()
     5803#define MAX_SUBTITLE_DISPLAY_TIME_MS 4500
     5804/** \fn NuppelVideoPlayer::DisplayAVSubtitles(void)
     5805 *  \brief Displays new subtitles and removes old ones.
     5806 *
     5807 *  This version is for AVSubtitles which are added with AddAVSubtitles()
     5808 *  when found in the input stream.
     5809 */
     5810void NuppelVideoPlayer::DisplayAVSubtitles()
    57855811{
    57865812    OSDSet *subtitleOSD;
    57875813    bool setVisible = false;
     
    58055831        osdHasSubtitles = false;
    58065832    }
    58075833
    5808     while (nonDisplayedSubtitles.size() > 0)
     5834    while (nonDisplayedAVSubtitles.size() > 0)
    58095835    {
    5810         const AVSubtitle subtitlePage = nonDisplayedSubtitles.front();
     5836        const AVSubtitle subtitlePage = nonDisplayedAVSubtitles.front();
    58115837
    58125838        if (subtitlePage.start_display_time > currentFrame->timecode)
    58135839            break;
    58145840
    5815         nonDisplayedSubtitles.pop_front();
     5841        nonDisplayedAVSubtitles.pop_front();
    58165842
    58175843        // clear old subtitles
    58185844        if (osdHasSubtitles)
     
    58285854            AVSubtitleRect* rect = &subtitlePage.rects[i];
    58295855
    58305856            bool displaysub = true;
    5831             if (nonDisplayedSubtitles.size() > 0 &&
    5832                 nonDisplayedSubtitles.front().start_display_time <
     5857            if (nonDisplayedAVSubtitles.size() > 0 &&
     5858                nonDisplayedAVSubtitles.front().start_display_time <
    58335859                currentFrame->timecode)
    58345860            {
    58355861                displaysub = false;
     
    58795905                if (subtitlePage.end_display_time <=
    58805906                    subtitlePage.start_display_time)
    58815907                {
    5882                     if (nonDisplayedSubtitles.size() > 0)
    5883                         osdSubtitlesExpireAt = nonDisplayedSubtitles.front().start_display_time;
     5908                    if (nonDisplayedAVSubtitles.size() > 0)
     5909                        osdSubtitlesExpireAt =
     5910                            nonDisplayedAVSubtitles.front().start_display_time;
    58845911                    else
    5885                         osdSubtitlesExpireAt += 4500;
     5912                        osdSubtitlesExpireAt += MAX_SUBTITLE_DISPLAY_TIME_MS;
    58865913                }
    58875914
    58885915                setVisible = true;
     
    59095936    }
    59105937}
    59115938
    5912 // hide subtitles and free the undisplayed subtitles
     5939/** \fn NuppelVideoPlayer::DisplayTextSubtitles(void)
     5940 *  \brief Displays subtitles in textual format.
     5941 *
     5942 *  This version is for subtitles that are loaded from an external subtitle
     5943 *  file by using the LoadExternalSubtitles() method. Subtitles are not
     5944 *  deleted after displaying so they can be displayed again after seeking.
     5945 */
     5946void NuppelVideoPlayer::DisplayTextSubtitles()
     5947{
     5948    VideoFrame *currentFrame = videoOutput->GetLastShownFrame();
     5949
     5950    if (!osd || !currentFrame) {
     5951        VERBOSE(VB_PLAYBACK, "osd or current video frame not found");
     5952        return;
     5953    }
     5954
     5955    QMutexLocker locker(&subtitleLock);
     5956
     5957    // frame time code in frames shown or millisecs from the start
     5958    // depending on the timing type of the subtitles
     5959    uint64_t playPos = 0;
     5960    if (textSubtitles.IsFrameBasedTiming()) {
     5961        // frame based subtitles get out of synch after running mythcommflag
     5962        // for the file, i.e., the following number is wrong and does not
     5963        // match the subtitle frame numbers:
     5964        playPos = currentFrame->frameNumber;
     5965    } else {
     5966        playPos = currentFrame->timecode;
     5967    }
     5968   
     5969    // check if we are showing a subtitle already and need to keep showing it
     5970    if (textSubtitles.SubtitlePosition() != textSubtitles.Subtitles().end()) {
     5971        text_subtitle_t& currentSub = *textSubtitles.SubtitlePosition();
     5972        if (currentSub.start <= playPos && currentSub.end >= playPos) {
     5973            return;
     5974        }
     5975    } else {
     5976        // haven't shown any subtitles yet or shown all subtitles,
     5977        // start looking from the beginning
     5978        textSubtitles.SubtitlePosition() = textSubtitles.Subtitles().begin();
     5979    }
     5980
     5981    osd->ClearTextSubtitles();
     5982    osdHasSubtitles = false;
     5983
     5984    TextSubtitleList::iterator nextSub = textSubtitles.SubtitlePosition();
     5985    do {   
     5986        if ((*nextSub).start <= playPos && (*nextSub).end >= playPos)
     5987            break; // found a sub to display
     5988
     5989        if ((*nextSub).start > playPos) {
     5990            if (nextSub == textSubtitles.Subtitles().begin())
     5991                return; // no sub to display at the moment             
     5992            --nextSub;
     5993            if ((*nextSub).end < playPos)
     5994                return; // no sub to display at the moment         
     5995        } else {
     5996            ++nextSub;
     5997            if ((*nextSub).start > playPos)
     5998                return; // no sub to display at the moment
     5999        }
     6000    } while (nextSub != textSubtitles.Subtitles().end());
     6001
     6002    text_subtitle_t& newSub = *nextSub;
     6003    QString subText;
     6004    textSubtitles.SubtitlePosition() = nextSub;
     6005    osd->SetTextSubtitles(newSub.textLines);
     6006    osdHasSubtitles = true;
     6007}
     6008
     6009
     6010// hide and free the undisplayed AVSubtitles
    59136011void NuppelVideoPlayer::ClearSubtitles()
    59146012{
    59156013    subtitleLock.lock();
    59166014
    5917     while (nonDisplayedSubtitles.size() > 0)
     6015    while (nonDisplayedAVSubtitles.size() > 0)
    59186016    {
    5919         AVSubtitle& subtitle = nonDisplayedSubtitles.front();
     6017        AVSubtitle& subtitle = nonDisplayedAVSubtitles.front();
    59206018
    59216019        // Because the subtitles were not displayed, OSDSet does not
    59226020        // free the OSDTypeImages in ClearAll(), so we have to free
     
    59316029        if (subtitle.num_rects > 0)
    59326030            av_free(subtitle.rects);
    59336031
    5934         nonDisplayedSubtitles.pop_front();
     6032        nonDisplayedAVSubtitles.pop_front();
    59356033    }
    59366034
    59376035    subtitleLock.unlock();
     
    59486046    }
    59496047}
    59506048
    5951 // adds a new subtitle to be shown
     6049// adds a new AVSubtitle to be shown, assumes the subtitles are pushed in
     6050// the order they should be shown
    59526051// FIXME: Need to fix subtitles to use a 64bit timestamp
    5953 void NuppelVideoPlayer::AddSubtitle(const AVSubtitle &subtitle)
     6052void NuppelVideoPlayer::AddAVSubtitle(const AVSubtitle &subtitle)
    59546053{
    59556054    subtitleLock.lock();
    5956     nonDisplayedSubtitles.push_back(subtitle);
     6055    nonDisplayedAVSubtitles.push_back(subtitle);
    59576056    subtitleLock.unlock();
    59586057}
    59596058
     
    59736072    }
    59746073}
    59756074
     6075/** \fn NuppelVideoPlayer::LoadExternalSubtitles(QString)
     6076 *  \brief Loads subtitles from an external file.
     6077 *
     6078 *  \return True in case the subtitle file format was detected and subtitles
     6079 *  were loaded successfully, false otherwise.
     6080 */
     6081bool NuppelVideoPlayer::LoadExternalSubtitles(QString subtitleFileName)
     6082{
     6083    QMutexLocker locker(&subtitleLock);
     6084    textSubtitles.Clear();
     6085    return TextSubtitleParser::LoadSubtitles(subtitleFileName, textSubtitles);
     6086}
     6087
    59766088void NuppelVideoPlayer::ChangeDVDTrack(bool ffw)
    59776089{
    59786090    if (!ringBuffer->isDVD())
  • libs/libmythtv/avformatdecoder.cpp

    old new  
    29602960                    {
    29612961                        subtitle.start_display_time += pts;
    29622962                        subtitle.end_display_time += pts;
    2963                         GetNVP()->AddSubtitle(subtitle);
     2963                        GetNVP()->AddAVSubtitle(subtitle);
    29642964                    }
    29652965
    29662966                    break;
  • libs/libmythtv/libmythtv.pro

    old new  
    199199    SOURCES += tv_play.cpp              NuppelVideoPlayer.cpp
    200200    SOURCES += DVDRingBuffer.cpp
    201201
     202    # Text subtitle parser
     203    HEADERS += textsubtitleparser.h     xine_demux_sputext.h
     204    SOURCES += textsubtitleparser.cpp   xine_demux_sputext.c
     205
    202206    # A/V decoders
    203207    HEADERS += decoderbase.h
    204208    HEADERS += nuppeldecoder.h          avformatdecoder.h
  • libs/libmythtv/osdtypes.h

    old new  
    283283    mutable uint    m_draw_info_len;
    284284    mutable vector<DrawInfo> m_draw_info;
    285285};
    286     
     286   
    287287class OSDTypeImage : public OSDType
    288288{
    289289  public:
  • libs/libmythtv/tv_play.h

    old new  
    352352
    353353    void ITVRestart(bool isLive);
    354354
     355    void FindAndLoadExternalSubs(NuppelVideoPlayer& target, QString videoFile);
     356
    355357    static QStringList GetValidRecorderList(uint chanid);
    356358    static QStringList GetValidRecorderList(const QString &channum);
    357359    static QStringList GetValidRecorderList(uint, const QString&);
  • libs/libmythtv/NuppelVideoPlayer.h

    old new  
    1414#include "recordingprofile.h"
    1515#include "videooutbase.h"
    1616#include "teletextdecoder.h"
     17#include "textsubtitleparser.h"
    1718#include "tv_play.h"
    1819#include "yuv2rgb.h"
    1920#include "cc608decoder.h"
     
    7071enum
    7172{
    7273    kTrackTypeAudio = 0,
    73     kTrackTypeSubtitle,
     74    kTrackTypeSubtitle, // subtitle track from ffmpeg
    7475    kTrackTypeCC608,
    7576    kTrackTypeCC708,
    7677    kTrackTypeTeletextCaptions,
    7778    kTrackTypeCount,
    78 
    7979    kTrackTypeTeletextMenu,
    8080};
    8181QString track_type_to_string(uint type);
     
    8787    kDisplayNone                = 0x00,
    8888    kDisplayNUVTeletextCaptions = 0x01,
    8989    kDisplayTeletextCaptions    = 0x02,
    90     kDisplaySubtitle            = 0x04,
     90    kDisplayAVSubtitle          = 0x04,
    9191    kDisplayCC608               = 0x08,
    9292    kDisplayCC708               = 0x10,
    9393    kDisplayNUVCaptions         = kDisplayNUVTeletextCaptions | kDisplayCC608,
    94     kDisplayAllCaptions         = 0x1f,
    95     kDisplayTeletextMenu        = 0x20,
     94    kDisplayTextSubtitle        = 0x20,
     95    kDisplayAllCaptions         = 0x3f,
     96    kDisplayTeletextMenu        = 0x40,
    9697};
    9798
    9899class NuppelVideoPlayer : public CC608Reader, public CC708Reader
     
    277278                      long long timecode);
    278279    void AddTextData(unsigned char *buffer, int len,
    279280                     long long timecode, char type);
    280     void AddSubtitle(const AVSubtitle& subtitle);
     281    void AddAVSubtitle(const AVSubtitle& subtitle);
    281282
    282283    // Closed caption and teletext stuff
    283284    uint GetCaptionMode(void) const { return textDisplayMode; }
     
    287288    bool ToggleCaptions(void);
    288289    bool ToggleCaptions(uint mode);
    289290    void SetCaptionsEnabled(bool);
     291    bool LoadExternalSubtitles(QString subtitleFileName);
    290292
    291293    // Teletext Menu and non-NUV teletext decoder
    292294    void EnableTeletext(void);
     
    477479    void  UpdateCC(unsigned char *inpos);
    478480
    479481    // Private subtitle stuff
    480     void  DisplaySubtitles(void);
     482    void  DisplayAVSubtitles(void);
     483    void  DisplayTextSubtitles(void);
    481484    void  ClearSubtitles(void);
    482485
    483486    // Private LiveTV stuff
     
    611614    bool      textDesired;
    612615    bool      osdHasSubtitles;
    613616    long long osdSubtitlesExpireAt;
    614     MythDeque<AVSubtitle> nonDisplayedSubtitles;
     617
     618    /// Subtitles loaded from the video stream by libavcodec.
     619    /// This should contain only undisplayed subtitles, old
     620    /// ones are deleted after displayed.
     621    MythDeque<AVSubtitle> nonDisplayedAVSubtitles;
     622
     623    /// Subtitles loaded from an external subtitle file.
     624    /// This contains all subtitles in textual format. No
     625    /// subtitles are deleted after displaying (so they can
     626    /// be displayed again after seeking). The list is ordered
     627    /// by the subtitle display start time.
     628    TextSubtitles textSubtitles;
    615629
    616630    CC708Service CC708services[64];
    617631    QString    osdfontname;
  • new file libs/libmythtv/textsubtitleparser.h

    - +  
     1// -*- Mode: c++ -*-
     2
     3#ifndef TEXT_SUBTITLE_PARSER_H
     4#define TEXT_SUBTITLE_PARSER_H
     5
     6#include <qstring.h>
     7#include <qvaluelist.h>
     8
     9#define MAX_TEXT_SUBTITLE_LINES 5
     10
     11struct text_subtitle_t {
     12   
     13    uint64_t start; ///< Starting time in msec or starting frame
     14    uint64_t end;   ///< Ending time in msec or ending frame
     15   
     16    QValueList<QString> textLines;
     17
     18    text_subtitle_t(long start_, long end_) : start(start_), end(end_) {}
     19   
     20    // this constructor is needed for Qt's containers
     21    text_subtitle_t() : start(0), end(0) {}
     22};
     23
     24typedef QValueList<text_subtitle_t> TextSubtitleList;
     25
     26class TextSubtitles {
     27public:
     28    TextSubtitles() : m_subtitles(), m_frameBasedTiming(false),
     29                      m_subtitlePosition(m_subtitles.end()) {};
     30    virtual ~TextSubtitles() {};
     31
     32    bool IsFrameBasedTiming() { return m_frameBasedTiming; }
     33    void SetFrameBasedTiming(bool frameBasedTiming)
     34        { m_frameBasedTiming = frameBasedTiming; }
     35
     36    void AddSubtitle(const text_subtitle_t& newSub);
     37    void Clear();
     38
     39    TextSubtitleList::iterator& SubtitlePosition()
     40        { return m_subtitlePosition; }
     41    TextSubtitleList& Subtitles() { return m_subtitles; }
     42    std::size_t SubtitleCount() { return m_subtitles.size(); }
     43
     44private:
     45    TextSubtitleList m_subtitles;
     46    bool m_frameBasedTiming;
     47    TextSubtitleList::iterator m_subtitlePosition;
     48};
     49
     50class TextSubtitleParser {
     51public:
     52    static bool LoadSubtitles(QString fileName, TextSubtitles& target);
     53};
     54
     55#endif
     56
  • libs/libmythtv/osd.h

    old new  
    4040class OSDTypeImage;
    4141class OSDTypePositionIndicator;
    4242class OSDSurface;
     43class OSDTypeText;
    4344class TV;
    4445class UDPNotifyOSDSet;
    4546class OSDListTreeType;
     
    4950class CC708Service;
    5051class TeletextViewer;
    5152
     53
    5254class OSD : public QObject
    5355{
    5456    Q_OBJECT
     
    151153    bool HasSet(const QString &name);
    152154    QRect GetSubtitleBounds();
    153155
     156    void SetTextSubtitles(const QStringList& lines);
     157    void ClearTextSubtitles();
     158
    154159 private:
    155160    bool InitDefaults(void);
    156161    bool InitCC608(void);
    157162    bool InitCC708(void);
    158163    bool InitTeletext(void);
    159     bool InitDVBSub(void);
     164    bool InitSubtitles(void);
    160165    bool InitMenu(void);
    161166    bool InitInteractiveTV(void);
    162167
  • 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
     77static int eol(char p) {
     78  return (p=='\r' || p=='\n' || p=='\0');
     79}
     80
     81static 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 */
     98static 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
     134static 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, "&nbsp;", 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
     208static 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
     230static 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
     263static 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
     301static 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
     393static 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
     448static 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
     498static 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]", &nothing,
     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]", &nothing,
     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
     568static 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
     606static 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
     653static 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,&current->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,&current->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
     686static 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
     884static 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
     916static 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
     953static 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
     985static 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
     1075subtitle_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
     5extern "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
     17typedef 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
     27typedef 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
     49subtitle_t *sub_read_file (demux_sputext_t*);
     50
     51#ifdef __cplusplus
     52}
     53#endif
     54
     55#endif
  • libs/libmythtv/tv_play.cpp

    old new  
    88#include <qregexp.h>
    99#include <qfile.h>
    1010#include <qtimer.h>
     11#include <qdir.h>
    1112
    1213#include "mythdbcon.h"
    1314#include "tv.h"
     
    11571158        nvp->ResetCaptions();
    11581159        nvp->ResetTeletext();
    11591160
     1161        /// external subtitle file hack start
     1162
     1163        /// external subtitle file hack end
     1164
    11601165        activenvp = nvp;
    11611166        activerbuffer = prbuffer;
    11621167        StartOSD();
     
    12791284    nvp->SetLength(playbackLen);
    12801285    nvp->SetExactSeeks(gContext->GetNumSetting("ExactSeeking", 0));
    12811286    nvp->SetAutoCommercialSkip(autoCommercialSkip);
     1287    FindAndLoadExternalSubs(*nvp, prbuffer->GetFilename());
    12821288    nvp->SetLiveTVChain(tvchain);
    12831289
    12841290    nvp->SetAudioStretchFactor(normal_speed);
     
    66246630        typeStr = "SUBTITLE";
    66256631        selStr  = "SELECTSUBTITLE_";
    66266632        grpStr  = "SUBTITLEGROUP";
    6627         sel     = activenvp->GetCaptionMode() & kDisplaySubtitle;
     6633        sel     = activenvp->GetCaptionMode() & kDisplayAVSubtitle;
    66286634    }
    66296635    else if (kTrackTypeCC608 == type)
    66306636    {
     
    70787084    nvp->ITVRestart(chanid, cardid, isLive);
    70797085}
    70807086
     7087/* \fn TV::FindAndLoadExternalSubs(NuppelVideoPlayer&, QString)
     7088 * \brief Tries to find an external subtitle file in the same directory
     7089 *  in which the video file is. Tries to parse each found candidate file
     7090 *  until one is parsed succesfully.
     7091 */
     7092void TV::FindAndLoadExternalSubs(NuppelVideoPlayer& target, QString videoFile)
     7093{
     7094    if (videoFile.length() < 1)
     7095        return;
     7096
     7097    QString baseName;
     7098
     7099    int suffixPos = videoFile.findRev(QChar('.'));
     7100    if (suffixPos > 0) {
     7101        baseName = videoFile.left(suffixPos);
     7102    } else {
     7103        baseName = videoFile;
     7104    }
     7105
     7106    // the detection does not work if the filaname has the following chars
     7107    baseName =
     7108        baseName.replace("[", "?").replace("]", "?").replace("(", "?")
     7109        .replace(")", "?");
     7110
     7111    // try to find files with the same base name, but ending with
     7112    // '.srt', '.sub', or '.txt'
     7113    QDir dir;   
     7114    QStringList candidates = dir.entryList(
     7115        baseName + "*.srt; " + baseName + "*.sub; " + baseName + "*.txt;",
     7116        QDir::Files);
     7117
     7118    for (int i = 0; i < candidates.size(); ++i) {
     7119        if (nvp->LoadExternalSubtitles(candidates[i])) {
     7120            VERBOSE(
     7121                VB_PLAYBACK,
     7122                LOC + QString("Loaded text subtitles from %1.").
     7123                arg(candidates[i]));
     7124            return;
     7125        }
     7126    }
     7127}
     7128
    70817129/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • new file libs/libmythtv/textsubtitleparser.cpp

    - +  
     1#include <stdio.h>
     2#include <qtextcodec.h>
     3
     4#include "textsubtitleparser.h"
     5#include "xine_demux_sputext.h"
     6#include "debug.h"
     7
     8void TextSubtitles::AddSubtitle(const text_subtitle_t& newSub)
     9{
     10    m_subtitles.push_back(newSub);
     11    m_subtitlePosition = m_subtitles.end();
     12}
     13
     14void TextSubtitles::Clear()
     15{
     16    m_subtitles.clear();
     17    m_subtitlePosition = m_subtitles.end();
     18}
     19
     20bool TextSubtitleParser::LoadSubtitles(QString fileName, TextSubtitles& target)
     21{
     22    demux_sputext_t sub_data;
     23    if ((sub_data.file_ptr = fopen(fileName, "r")) == NULL)
     24    {
     25        return false;
     26    }
     27
     28    subtitle_t* loaded_subs = NULL;
     29    if ((loaded_subs = sub_read_file(&sub_data)) == NULL)
     30    {
     31        return false;
     32    }
     33    target.SetFrameBasedTiming(!sub_data.uses_time);
     34
     35    // convert the subtitles to our own format and free the original
     36    // structures
     37    for (int sub_i = 0; sub_i < sub_data.num; ++sub_i) {
     38        subtitle_t* sub = &loaded_subs[sub_i];
     39        text_subtitle_t newsub(sub->start, sub->end);
     40
     41        if (!target.IsFrameBasedTiming()) {
     42            newsub.start *= 10; // convert from csec to msec
     43            newsub.end *= 10;
     44        }
     45        for (int line = 0; line < sub->lines; ++line) {
     46            newsub.textLines.push_back(QString(sub->text[line]));           
     47            free(sub->text[line]);
     48        }
     49        target.AddSubtitle(newsub);
     50    }
     51    free(loaded_subs);
     52    fclose(sub_data.file_ptr);
     53    return true;
     54}
  • libs/libmythtv/osd.cpp

    old new  
    2323#include "osdtypes.h"
    2424#include "osdsurface.h"
    2525#include "mythcontext.h"
     26#include "textsubtitleparser.h"
    2627#include "libmyth/oldsettings.h"
    2728#include "udpnotify.h"
    2829
     
    135136    ok &= InitCC708();
    136137    ok &= InitTeletext();
    137138    ok &= InitMenu();
    138     ok &= InitDVBSub();
     139    ok &= InitSubtitles();
    139140    ok &= InitInteractiveTV();
    140141    return ok;
    141142}
     
    274275    return true;
    275276}   
    276277
     278#include "debug.h"
     279
     280void OSD::SetTextSubtitles(const QStringList& lines)
     281{
     282    const uint8_t SUBTITLE_FONT_SIZE = 18;
     283    const uint8_t SUBTITLE_LINE_HEIGHT = 22;
     284    const uint8_t MAX_CHARACTERS_PER_ROW = 50;
     285
     286    OSDSet* subtitleSet = GetSet("subtitles");
     287    if (subtitleSet == NULL)
     288        return;
     289
     290
     291    QString subText = "";
     292    int subLines = 0;
     293    for (std::size_t i = 0; i < lines.size(); ++i) {
     294
     295        const QString& line = lines[i];
     296        if (line.length() > MAX_CHARACTERS_PER_ROW) {
     297            // wrap long lines at word spaces
     298            QStringList words = QStringList::split(" ", line);
     299            QString newString = "";
     300            do {
     301                QString word = words.first();
     302                words.pop_front();
     303
     304                if (newString.length() + word.length() + 1 >
     305                    MAX_CHARACTERS_PER_ROW) {
     306                    // next word won't fit anymore, create a new line
     307                    subText.append(newString + "\n");
     308                    ++subLines;
     309                    newString = "";
     310                }
     311                newString.append(word + " ");
     312            } while(words.size() > 0);
     313
     314            subText.append(newString);
     315        } else {
     316            subText.append(line);
     317        }
     318        subText.append("\n");
     319        ++subLines;
     320    }
     321
     322
     323    ClearAll("subtitles");
     324
     325    QString name = "text_subtitles";
     326
     327    QRect area(0, displayheight - subLines * SUBTITLE_LINE_HEIGHT,
     328               displaywidth, displayheight);
     329
     330    QString fontname = "text_subtitle_font";
     331    TTFFont *font = GetFont(fontname);
     332    if (!font)
     333    {
     334       
     335        font = LoadFont(gContext->GetSetting("OSDCCFont"), SUBTITLE_FONT_SIZE);
     336
     337        if (font) {
     338            // set outline so we can see the font in white background video
     339            font->setOutline(true);
     340            fontMap[fontname] = font;
     341        } else {
     342            DEBUG_OUT << "cannot load font" << std::endl;
     343        }
     344    }
     345
     346    OSDTypeText *text = new OSDTypeText(
     347        name, font, "", area, wmult, hmult);
     348 
     349    text->SetCentered(true);
     350    text->SetMultiLine(true);
     351    text->SetSelected(false);
     352    text->SetText(subText);
     353    text->SetSelected(false);
     354    subtitleSet->AddType(text);
     355
     356    SetVisible(subtitleSet, 0);
     357}   
     358
     359void OSD::ClearTextSubtitles()
     360{
     361    HideSet("subtitles");
     362    ClearAll("subtitles");
     363}
     364
    277365bool OSD::InitMenu(void)
    278366{
    279367    if (GetSet("menu"))
     
    330418    return true;
    331419}
    332420
    333 bool OSD::InitDVBSub(void)
     421#include "debug.h"
     422
     423bool OSD::InitSubtitles(void)
    334424{
    335     // Create container for subtitles
     425    // Create container for subtitles (DVB, DVD and external subs)
    336426    if (GetSet("subtitles"))
    337427        return true;
    338428
     
    341431        new OSDSet(name, true,
    342432                   osdBounds.width(), osdBounds.height(),
    343433                   wmult, hmult, frameint);
     434
    344435    container->SetPriority(30);
    345436    AddSet(container, name);
    346437    return true;
     
    458549        return font;
    459550
    460551    delete font;
     552    font = NULL;
    461553    fullname = gContext->GetShareDir() + name;
    462554
    463555    font = new TTFFont((char *)fullname.ascii(), size,
     
    467559        return font;
    468560
    469561    delete font;
     562    font = NULL;
    470563    if (themepath != "")
    471564    {
    472565        fullname = themepath + "/" + name;
     
    477570    }
    478571
    479572    delete font;
     573    font = NULL;
    480574
    481575    fullname = name;
    482576    font = new TTFFont((char *)fullname.ascii(), size,
     
    489583                                  "No OSD will be displayed.").arg(name));
    490584
    491585    delete font;
     586    font = NULL;
    492587    return NULL;
    493588}
    494589