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); }
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(); }
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; }