Ejemplo n.º 1
0
void SubtitleScreen::DisplayTextSubtitles(void)
{
    if (!InitialiseFont(m_fontStretch) || !m_player || !m_subreader)
        return;

    bool changed = false;
    VideoOutput *vo = m_player->GetVideoOutput();
    if (vo)
    {
        QRect oldsafe = m_safeArea;
        m_safeArea = m_player->GetVideoOutput()->GetSafeRect();
        if (oldsafe != m_safeArea)
        {
            changed = true;
            int height = (m_safeArea.height() * m_textFontZoom) / 2000;
            gTextSubFont->GetFace()->setPixelSize(height);
            gTextSubFont->GetFace()->setItalic(false);
            gTextSubFont->GetFace()->setUnderline(false);
            gTextSubFont->SetColor(Qt::white);
        }
    }
    else
    {
        return;
    }

    VideoFrame *currentFrame = vo->GetLastShownFrame();
    if (!currentFrame)
        return;

    TextSubtitles *subs = m_subreader->GetTextSubtitles();
    subs->Lock();
    uint64_t playPos = 0;
    if (subs->IsFrameBasedTiming())
    {
        // frame based subtitles get out of synch after running mythcommflag
        // for the file, i.e., the following number is wrong and does not
        // match the subtitle frame numbers:
        playPos = currentFrame->frameNumber;
    }
    else
    {
        // Use timecodes for time based SRT subtitles. Feeding this into
        // NormalizeVideoTimecode() should adjust for non-zero start times
        // and wraps. For MPEG, wraps will occur just once every 26.5 hours
        // and other formats less frequently so this should be sufficient.
        // Note: timecodes should now always be valid even in the case
        // when a frame doesn't have a valid timestamp. If an exception is
        // found where this is not true then we need to use the frameNumber
        // when timecode is not defined by uncommenting the following lines.
        //if (currentFrame->timecode == 0)
        //    playPos = (uint64_t)
        //        ((currentFrame->frameNumber / video_frame_rate) * 1000);
        //else
        playPos = m_player->GetDecoder()->NormalizeVideoTimecode(currentFrame->timecode);
    }
    if (playPos != 0)
        changed |= subs->HasSubtitleChanged(playPos);
    if (!changed)
    {
        subs->Unlock();
        return;
    }

    DeleteAllChildren();
    SetRedraw();
    if (playPos == 0)
    {
        subs->Unlock();
        return;
    }

    QStringList rawsubs = subs->GetSubtitles(playPos);
    if (rawsubs.empty())
    {
        subs->Unlock();
        return;
    }

    OptimiseTextSubs(rawsubs);
    subs->Unlock();
    DrawTextSubtitles(rawsubs, 0, 0);
}
Ejemplo n.º 2
0
void TextSubtitleParser::LoadSubtitles(const QString &fileName,
                                       TextSubtitles &target,
                                       bool inBackground)
{
    if (inBackground)
    {
        if (!SubtitleLoadHelper::IsLoading(&target))
            MThreadPool::globalInstance()->
                start(new SubtitleLoadHelper(fileName, &target),
                      "SubtitleLoadHelper");
        return;
    }
    demux_sputext_t sub_data;
    RemoteFileWrapper rfile(fileName/*, false, false, 0*/);

    LOG(VB_VBI, LOG_INFO,
        QString("Preparing to load subtitle file (%1)").arg(fileName));
    if (!rfile.isOpen())
    {
        LOG(VB_VBI, LOG_INFO,
            QString("Failed to load subtitle file (%1)").arg(fileName));
        return;
    }
    target.SetHasSubtitles(true);
    target.SetFilename(fileName);

    // Only reload if rfile.GetFileSize() has changed.
    off_t new_len = rfile.GetFileSize();
    if (target.GetByteCount() == new_len)
    {
        LOG(VB_VBI, LOG_INFO,
            QString("Filesize unchanged (%1), not reloading subs (%2)")
            .arg(new_len).arg(fileName));
        target.SetLastLoaded();
        return;
    }
    LOG(VB_VBI, LOG_INFO,
        QString("Preparing to read %1 subtitle bytes from %2")
        .arg(new_len).arg(fileName));
    target.SetByteCount(new_len);
    sub_data.rbuffer_len = new_len;
    sub_data.rbuffer_text = new char[sub_data.rbuffer_len + 1];
    sub_data.rbuffer_cur = 0;
    sub_data.errs = 0;
    int numread = rfile.Read(sub_data.rbuffer_text, sub_data.rbuffer_len);
    LOG(VB_VBI, LOG_INFO,
        QString("Finished reading %1 subtitle bytes (requested %2)")
        .arg(numread).arg(new_len));

    // try to determine the text codec
    QByteArray test(sub_data.rbuffer_text, sub_data.rbuffer_len);
    QTextCodec *textCodec = QTextCodec::codecForUtfText(test, NULL);
    if (!textCodec)
    {
        LOG(VB_VBI, LOG_WARNING, "Failed to autodetect a UTF encoding.");
        QString codec = gCoreContext->GetSetting("SubtitleCodec", "");
        if (!codec.isEmpty())
            textCodec = QTextCodec::codecForName(codec.toLatin1());
        if (!textCodec)
            textCodec = QTextCodec::codecForName("utf-8");
        if (!textCodec)
        {
            LOG(VB_VBI, LOG_ERR,
                QString("Failed to find codec for subtitle file '%1'")
                .arg(fileName));
            return;
        }
    }

    LOG(VB_VBI, LOG_INFO, QString("Opened subtitle file '%1' with codec '%2'")
        .arg(fileName).arg(textCodec->name().constData()));

    // load the entire subtitle file, converting to unicode as we go
    QScopedPointer<QTextDecoder> dec(textCodec->makeDecoder());
    QString data = dec->toUnicode(sub_data.rbuffer_text, sub_data.rbuffer_len);
    if (data.isEmpty())
    {
        LOG(VB_VBI, LOG_WARNING,
            QString("Data loaded from subtitle file '%1' is empty.")
            .arg(fileName));
        return;
    }

    // convert back to utf-8 for parsing
    QByteArray ba = data.toUtf8();
    delete[] sub_data.rbuffer_text;
    sub_data.rbuffer_text = ba.data();
    sub_data.rbuffer_len = ba.size();

    subtitle_t *loaded_subs = sub_read_file(&sub_data);
    if (!loaded_subs)
    {
        // Don't delete[] sub_data.rbuffer_text; because the
        // QByteArray destructor will clean up.
        LOG(VB_VBI, LOG_ERR, QString("Failed to read subtitles from '%1'")
            .arg(fileName));
        return;
    }

    LOG(VB_VBI, LOG_INFO, QString("Found %1 subtitles in file '%2'")
        .arg(sub_data.num).arg(fileName));
    target.SetFrameBasedTiming(!sub_data.uses_time);
    target.Clear();

    // convert the subtitles to our own format, free the original structures
    // and convert back to unicode
    textCodec = QTextCodec::codecForName("utf-8");
    if (textCodec)
        dec.reset(textCodec->makeDecoder());

    for (int sub_i = 0; sub_i < sub_data.num; ++sub_i)
    {
        const subtitle_t *sub = &loaded_subs[sub_i];
        text_subtitle_t newsub(sub->start, sub->end);

        if (!target.IsFrameBasedTiming())
        {
            newsub.start *= 10; // convert from csec to msec
            newsub.end *= 10;
        }

        for (int line = 0; line < sub->lines; ++line)
        {
            const char *subLine = sub->text[line];
            QString str;
            if (textCodec)
                str = dec->toUnicode(subLine, strlen(subLine));
            else
                str = QString(subLine);
            newsub.textLines.push_back(str);

            free(sub->text[line]);
        }
        target.AddSubtitle(newsub);
    }

    // textCodec object is managed by Qt, do not delete...

    free(loaded_subs);
    // Don't delete[] sub_data.rbuffer_text; because the QByteArray
    // destructor will clean up.

    target.SetLastLoaded();
}
Ejemplo n.º 3
0
bool TextSubtitleParser::LoadSubtitles(const QString &fileName,
                                       TextSubtitles &target)
{
    demux_sputext_t sub_data;
    RemoteFile rfile(fileName, false, false, 0);

    LOG(VB_VBI, LOG_INFO,
        QString("Preparing to load subtitle file (%1)").arg(fileName));
    if (!rfile.Open())
    {
        LOG(VB_VBI, LOG_INFO,
            QString("Failed to load subtitle file (%1)").arg(fileName));
        return false;
    }
    target.SetHasSubtitles(true);
    target.SetFilename(fileName);

    // Only reload if rfile.GetRealFileSize() has changed.
    off_t new_len = rfile.GetFileSize();
    if (target.GetByteCount() == new_len)
    {
        LOG(VB_VBI, LOG_INFO,
            QString("Filesize unchanged (%1), not reloading subs (%2)")
            .arg(new_len).arg(fileName));
        target.SetLastLoaded();
        return new_len;
    }
    LOG(VB_VBI, LOG_INFO,
        QString("Preparing to read %1 subtitle bytes from %2")
        .arg(new_len).arg(fileName));
    target.SetByteCount(new_len);
    sub_data.rbuffer_len = new_len;
    sub_data.rbuffer_text = new char[sub_data.rbuffer_len + 1];
    sub_data.rbuffer_cur = 0;
    sub_data.errs = 0;
    int numread = rfile.Read(sub_data.rbuffer_text, sub_data.rbuffer_len);
    LOG(VB_VBI, LOG_INFO,
        QString("Finished reading %1 subtitle bytes (requested %2)")
        .arg(numread).arg(new_len));
    subtitle_t *loaded_subs = sub_read_file(&sub_data);
    if (!loaded_subs)
    {
        delete sub_data.rbuffer_text;
        return false;
    }

    target.SetFrameBasedTiming(!sub_data.uses_time);
    target.Clear();

    QTextCodec *textCodec = NULL;
    QString codec = gCoreContext->GetSetting("SubtitleCodec", "");
    if (!codec.isEmpty())
        textCodec = QTextCodec::codecForName(codec.toLatin1());
    if (!textCodec)
        textCodec = QTextCodec::codecForName("utf-8");
    if (!textCodec)
    {
        delete sub_data.rbuffer_text;
        return false;
    }

    QTextDecoder *dec = textCodec->makeDecoder();

    // convert the subtitles to our own format and free the original structures
    for (int sub_i = 0; sub_i < sub_data.num; ++sub_i)
    {
        const subtitle_t *sub = &loaded_subs[sub_i];
        text_subtitle_t newsub(sub->start, sub->end);

        if (!target.IsFrameBasedTiming())
        {
            newsub.start *= 10; // convert from csec to msec
            newsub.end *= 10;
        }

        for (int line = 0; line < sub->lines; ++line)
        {
            const char *subLine = sub->text[line];
            QString str = dec->toUnicode(subLine, strlen(subLine));
            newsub.textLines.push_back(str);

            free(sub->text[line]);
        }
        target.AddSubtitle(newsub);
    }

    delete dec;
    // textCodec object is managed by Qt, do not delete...

    free(loaded_subs);
    delete sub_data.rbuffer_text;

    target.SetLastLoaded();

    return true;
}