// Called by the video player to redraw the image. void MHIContext::UpdateOSD(InteractiveScreen *osdWindow, MythPainter *osdPainter) { if (!osdWindow || !osdPainter) return; QMutexLocker locker(&m_display_lock); m_updated = false; osdWindow->DeleteAllChildren(); // Copy all the display items into the display. list<MHIImageData*>::iterator it = m_display.begin(); for (int count = 0; it != m_display.end(); ++it, count++) { MHIImageData *data = *it; MythImage* image = osdPainter->GetFormatImage(); if (!image) continue; image->Assign(data->m_image); MythUIImage *uiimage = new MythUIImage(osdWindow, QString("itv%1") .arg(count)); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(data->m_x, data->m_y, data->m_image.width(), data->m_image.height())); } image->DecrRef(); } osdWindow->OptimiseDisplayedArea(); // N.B. bypasses OSD class hence no expiry set osdWindow->SetVisible(true); }
MythImage *MythPainter::GetImageFromString(const QString &msg, int flags, const QRect &r, const MythFontProperties &font) { QString incoming = font.GetHash() + QString::number(r.width()) + QString::number(r.height()) + QString::number(flags) + QString::number(font.color().rgba()) + msg; if (m_StringToImageMap.contains(incoming)) { m_StringExpireList.remove(incoming); m_StringExpireList.push_back(incoming); return m_StringToImageMap[incoming]; } MythImage *im = GetFormatImage(); if (im) { DrawTextPriv(im, msg, flags, r, font); m_SoftwareCacheSize += im->bytesPerLine() * im->height(); m_StringToImageMap[incoming] = im; m_StringExpireList.push_back(incoming); ExpireImages(m_MaxSoftwareCacheSize); } return im; }
void WebPage::slotIconChanged(void) { QIcon icon = m_browser->GetIcon(); if (icon.isNull()) { //FIXME use a default icon here? m_listItem->setImage(NULL); } else { if (m_listItem) { QPixmap pixmap = icon.pixmap(32, 32); QImage image = pixmap.toImage(); image = image.scaled( QSize(32,32), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); MythImage *mimage = GetMythPainter()->GetFormatImage(); mimage->Assign(image); m_listItem->setImage(mimage); } } m_parent->m_pageList->Refresh(); }
void ZMEvents::eventVisible(MythUIButtonListItem *item) { if (!item) return; if (item->HasImage()) return; Event *event = qVariantValue<Event*> (item->GetData()); if (event) { QImage image; if (ZMClient *zm = ZMClient::get()) { zm->getAnalyseFrame(event, 0, image); if (!image.isNull()) { MythImage *mimage = GetMythPainter()->GetFormatImage(); mimage->Assign(image); item->SetImage(mimage); mimage->SetChanged(); mimage->DecrRef(); } } } }
void MythPainter::DrawTextLayout(const QRect & canvasRect, const LayoutVector & layouts, const FormatVector & formats, const MythFontProperties & font, int alpha, const QRect & destRect) { if (canvasRect.isNull()) return; QRect canvas(canvasRect); QRect dest(destRect); MythImage *im = GetImageFromTextLayout(layouts, formats, font, canvas, dest); if (!im) { LOG(VB_GENERAL, LOG_ERR, QString("MythPainter::DrawTextLayout: " "Unable to create image.")); return; } if (im->isNull()) { LOG(VB_GENERAL, LOG_DEBUG, QString("MythPainter::DrawTextLayout: " "Rendered image is null.")); im->DecrRef(); return; } QRect srcRect(0, 0, dest.width(), dest.height()); DrawImage(dest, im, srcRect, alpha); im->DecrRef(); }
void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos) { VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; QRect scaled = vo->GetImageRect(pos); if (scaled.size() != pos.size()) { img = img.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = vo->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); if (image) { image->Assign(img); MythUIImage *uiimage = new MythUIImage(this, "dvd_button"); if (uiimage) { m_refreshArea = true; uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); } } }
MythImage* MythPainter::GetImageFromRect(const QRect &area, int radius, int ellipse, const QBrush &fillBrush, const QPen &linePen) { if (area.width() <= 0 || area.height() <= 0) return NULL; uint64_t hash1 = ((0xfff & (uint64_t)area.width())) + ((0xfff & (uint64_t)area.height()) << 12) + ((0xff & (uint64_t)fillBrush.style()) << 24) + ((0xff & (uint64_t)linePen.width()) << 32) + ((0xff & (uint64_t)radius) << 40) + ((0xff & (uint64_t)linePen.style()) << 48) + ((0xff & (uint64_t)ellipse) << 56); uint64_t hash2 = ((0xffffffff & (uint64_t)linePen.color().rgba())) + ((0xffffffff & (uint64_t)fillBrush.color().rgba()) << 32); QString incoming("R"); if (fillBrush.style() == Qt::LinearGradientPattern && fillBrush.gradient()) { const QLinearGradient *gradient = static_cast<const QLinearGradient*>(fillBrush.gradient()); if (gradient) { incoming = QString::number( ((0xfff & (uint64_t)gradient->start().x())) + ((0xfff & (uint64_t)gradient->start().y()) << 12) + ((0xfff & (uint64_t)gradient->finalStop().x()) << 24) + ((0xfff & (uint64_t)gradient->finalStop().y()) << 36)); QGradientStops stops = gradient->stops(); for (int i = 0; i < stops.size(); i++) { incoming += QString::number( ((0xfff * (uint64_t)(stops[i].first * 100))) + ((uint64_t)stops[i].second.rgba() << 12)); } } } incoming += QString::number(hash1) + QString::number(hash2); if (m_StringToImageMap.contains(incoming)) { m_StringExpireList.remove(incoming); m_StringExpireList.push_back(incoming); return m_StringToImageMap[incoming]; } MythImage *im = GetFormatImage(); if (im) { DrawRectPriv(im, area, radius, ellipse, fillBrush, linePen); m_SoftwareCacheSize += (im->bytesPerLine() * im->height()); m_StringToImageMap[incoming] = im; m_StringExpireList.push_back(incoming); ExpireImages(m_MaxSoftwareCacheSize); } return im; }
MythImage *MythPainter::GetFormatImage(void) { QMutexLocker locker(&m_allocationLock); MythImage *result = GetFormatImagePriv(); result->SetFileName("GetFormatImage"); m_allocatedImages.insert(result); return result; }
MythImage *AudioOutputGraph::GetImage(int64_t timecode) const { QMutexLocker lock(&m_mutex); Buffer::range_t avail = m_buffer->Avail(timecode); LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("(%1) using [%2..%3] avail [%4..%5]") .arg(timecode).arg(avail.first).arg(avail.second) .arg(m_buffer->First()).arg(m_buffer->Next()) ); const int width = m_buffer->Samples(avail); if (width <= 0) return nullptr; const unsigned range = 1U << m_buffer->BitsPerChannel(); const double threshold = 20 * log10(1.0 / range); // 16bit=-96.3296dB => ~6dB/bit const int height = (int)-ceil(threshold); // 96 if (height <= 0) return nullptr; QImage image(width, height, QImage::Format_ARGB32); image.fill(0); const int channels = m_buffer->Channels(); // Assume signed 16 bit/sample const int16_t *p = m_buffer->Data16(avail); for (int x = 0; x < width; ++x) { int left = p[0]; int right = channels > 1 ? p[1] : left; p += channels; unsigned avg = qAbs(left) + qAbs(right); double db = 20 * log10( (double)(avg ? avg : 1) / range); int idb = (int)ceil(db); QRgb rgb = idb <= m_dBsilence ? qRgb(255, 255, 255) : idb <= m_dBquiet ? qRgb( 0, 255, 255) : idb <= m_dBLoud ? qRgb( 0, 255, 0) : idb <= m_dbMax ? qRgb(255, 255, 0) : qRgb(255, 0, 0); int v = height - (int)(height * (db / threshold)); if (v >= height) v = height - 1; else if (v < 0) v = 0; for (int y = 0; y <= v; ++y) image.setPixel(x, height - 1 - y, rgb); } MythImage *mi = new MythImage(m_painter); mi->Assign(image); return mi; }
MythImage* MythQImagePainter::GetImageFromRect(const QSize &size, int radius, bool drawFill, const QColor &fillColor, bool drawLine, int lineWidth, const QColor &lineColor) { if (size.width() <= 0 || size.height() <= 0) return NULL; QString incoming = QString("RECT") + QString::number(size.width()) + QString::number(size.height()) + QString::number(radius) + QString::number(drawFill) + QString::number(fillColor.rgba()) + QString::number(drawLine) + QString::number(lineWidth) + QString::number(lineColor.rgba()); if (m_StringToImageMap.contains(incoming)) { m_StringExpireList.remove(incoming); m_StringExpireList.push_back(incoming); return m_StringToImageMap[incoming]; } QImage image(QSize(size.width(), size.height()), QImage::Format_ARGB32); image.fill(0x00000000); QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing); if (drawLine) painter.setPen(QPen(lineColor, lineWidth)); else painter.setPen(QPen(Qt::NoPen)); if (drawFill) painter.setBrush(QBrush(fillColor)); else painter.setBrush(QBrush(Qt::NoBrush)); if ((size.width() / 2) < radius) radius = size.width() / 2; if ((size.height() / 2) < radius) radius = size.height() / 2; QRect r(lineWidth / 2, lineWidth / 2, size.width() - lineWidth, size.height() - lineWidth); painter.drawRoundedRect(r, (qreal)radius, qreal(radius)); painter.end(); MythImage *im = GetFormatImage(); im->Assign(image); m_StringToImageMap[incoming] = im; m_StringExpireList.push_back(incoming); ExpireImages(MAX_CACHE_ITEMS); return im; }
void Player::updateFrame(const unsigned char* buffer) { QImage image(buffer, m_monitor.width, m_monitor.height, QImage::Format_RGB888); MythImage *img = GetMythMainWindow()->GetCurrentPainter()->GetFormatImage(); img->Assign(image); m_frameImage->SetImage(img); img->DecrRef(); }
void ThumbFinder::updatePositionBar(int64_t frame) { if (!m_positionImage) return; QSize size = m_positionImage->GetArea().size(); QPixmap *pixmap = new QPixmap(size.width(), size.height()); QPainter p(pixmap); QBrush brush(Qt::green); p.setBrush(brush); p.setPen(Qt::NoPen); p.fillRect(0, 0, size.width(), size.height(), brush); frm_dir_map_t::const_iterator it; brush.setColor(Qt::red); double startdelta, enddelta; for (it = m_deleteMap.begin(); it != m_deleteMap.end(); ++it) { if (it.key() != 0) startdelta = (m_archiveItem->duration * m_fps) / it.key(); else startdelta = size.width(); ++it; if (it == m_deleteMap.end()) { LOG(VB_GENERAL, LOG_ERR, "ThumbFinder: found a start cut but no cut end"); break; } if (it.key() != 0) enddelta = (m_archiveItem->duration * m_fps) / it.key(); else enddelta = size.width(); int start = (int) (size.width() / startdelta); int end = (int) (size.width() / enddelta); p.fillRect(start - 1, 0, end - start, size.height(), brush); } if (frame == 0) frame = 1; brush.setColor(Qt::yellow); int pos = (int) (size.width() / ((m_archiveItem->duration * m_fps) / frame)); p.fillRect(pos, 0, 3, size.height(), brush); MythImage *image = GetMythMainWindow()->GetCurrentPainter()->GetFormatImage(); image->Assign(*pixmap); m_positionImage->SetImage(image); p.end(); delete pixmap; }
void MythPainter::DrawEllipse(const QRect &area, const QBrush &fillBrush, const QPen &linePen, int alpha) { MythImage *im = GetImageFromRect(area, 0, 1, fillBrush, linePen); if (im) { DrawImage(area.x(), area.y(), im, alpha); im->DecrRef(); } }
/** * \copydoc MythUIType::customEvent() */ void MythUIImage::customEvent(QEvent *event) { if (event->type() == ImageLoadEvent::kEventType) { ImageLoadEvent *le = dynamic_cast<ImageLoadEvent*>(event); if (le->GetParent() != this) return; MythImage *image = le->GetImage(); if (!image) return; d->m_UpdateLock.lockForRead(); if (le->GetBasefile() != m_Filename) { d->m_UpdateLock.unlock(); #if 0 VERBOSE(VB_GUI|VB_FILE|VB_EXTRA, LOC + QString("customEvent(): Expecting '%2', got '%3'") .arg(m_Filename).arg(le->GetBasefile())); #endif image->DownRef(); return; } d->m_UpdateLock.unlock(); QString filename = le->GetFilename(); int number = le->GetNumber(); d->m_UpdateLock.lockForWrite(); if (m_ForceSize.isNull()) SetSize(image->size()); d->m_UpdateLock.unlock(); m_ImagesLock.lock(); if (m_Images[number]) { // If we got to this point, it means this same MythUIImage // was told to reload the same image, so we use the newest // copy of the image. m_Images[number]->DownRef(); // delete the original } m_Images[number] = image; m_ImagesLock.unlock(); SetRedraw(); d->m_UpdateLock.lockForWrite(); m_LastDisplay = QTime::currentTime(); d->m_UpdateLock.unlock(); } }
void TeletextScreen::OptimiseDisplayedArea(void) { VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; MythPainter *osd_painter = vo->GetOSDPainter(); if (!osd_painter) return; QHashIterator<int, QImage*> it(m_rowImages); while (it.hasNext()) { it.next(); MythImage* image = osd_painter->GetFormatImage(); if (!image || !it.value()) continue; int row = it.key(); image->Assign(*(it.value())); MythUIImage *uiimage = new MythUIImage(this, QString("ttrow%1") .arg(row)); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(0, row * m_rowHeight, m_safeArea.width(), m_rowHeight * 2)); } } QRegion visible; QListIterator<MythUIType *> i(m_ChildrenList); while (i.hasNext()) { MythUIType *img = i.next(); visible = visible.united(img->GetArea()); } if (visible.isEmpty()) return; QRect bounding = visible.boundingRect(); bounding = bounding.translated(m_safeArea.topLeft()); bounding = m_safeArea.intersected(bounding); int left = m_safeArea.left() - bounding.left(); int top = m_safeArea.top() - bounding.top(); SetArea(MythRect(bounding)); i.toFront();; while (i.hasNext()) { MythUIType *img = i.next(); img->SetArea(img->GetArea().translated(left, top)); } }
void MythPainter::DrawRoundRect(const QRect &area, int cornerRadius, const QBrush &fillBrush, const QPen &linePen, int alpha) { MythImage *im = GetImageFromRect(area, cornerRadius, 0, fillBrush, linePen); if (im) { DrawImage(area.x(), area.y(), im, alpha); im->DecrRef(); } }
/** * \brief Assign a set of MythImages to the widget for animation. * Use is strongly discouraged, use SetFilepattern() instead. * */ void MythUIImage::SetImages(QVector<MythImage *> &images) { Clear(); QWriteLocker updateLocker(&d->m_UpdateLock); QSize aSize = GetArea().size(); QVector<MythImage *>::iterator it; for (it = images.begin(); it != images.end(); ++it) { MythImage *im = (*it); if (!im) { QMutexLocker locker(&m_ImagesLock); m_Images[m_Images.size()] = im; continue; } im->UpRef(); if (!m_ForceSize.isNull()) { int w = (m_ForceSize.width() <= 0) ? im->width() : m_ForceSize.width(); int h = (m_ForceSize.height() <= 0) ? im->height() : m_ForceSize.height(); im->Resize(QSize(w, h), m_preserveAspect); } if (m_isReflected && !im->IsReflected()) im->Reflect(m_reflectAxis, m_reflectShear, m_reflectScale, m_reflectLength, m_reflectSpacing); if (m_isGreyscale && !im->isGrayscale()) im->ToGreyscale(); m_ImagesLock.lock(); m_Images[m_Images.size()] = im; m_ImagesLock.unlock(); aSize = aSize.expandedTo(im->size()); } SetImageCount(1, m_Images.size()); if (m_ForceSize.isNull()) SetSize(aSize); m_CurPos = 0; SetRedraw(); }
void ZMEvents::eventChanged(MythUIButtonListItem *item) { (void) item; if (m_eventNoText) { if (m_eventGrid->GetCount() > 0) m_eventNoText->SetText(QString("%1/%2") .arg(m_eventGrid->GetCurrentPos() + 1).arg(m_eventGrid->GetCount())); else m_eventNoText->SetText("0/0"); } // update the images for all the visible items for (int x = m_eventGrid->GetCurrentPos() - m_eventGrid->GetVisibleCount(); x < m_eventGrid->GetCurrentPos() + (int)m_eventGrid->GetVisibleCount(); x++) { if (x < 0 || x > (int)m_eventGrid->GetCount() - 1) continue; MythUIButtonListItem *gridItem = m_eventGrid->GetItemAt(x); if (gridItem && !gridItem->getImage()) { if (x < 0 || x > (int)m_eventList->size() - 1) continue; Event *event = m_eventList->at(x); if (event) { QImage image; if (class ZMClient *zm = ZMClient::get()) { zm->getAnalyseFrame(event->monitorID, event->eventID, 0, image); if (!image.isNull()) { MythImage *mimage = GetMythPainter()->GetFormatImage(); mimage->Assign(image); gridItem->setImage(mimage); mimage->SetChanged(); } } } } } }
void MythPainter::DrawText(const QRect &r, const QString &msg, int flags, const MythFontProperties &font, int alpha, const QRect &boundRect) { MythImage *im = GetImageFromString(msg, flags, r, font); if (!im) return; QRect destRect(boundRect); QRect srcRect(0,0,r.width(),r.height()); if (!boundRect.isEmpty() && boundRect != r) { int x = 0; int y = 0; int width = boundRect.width(); int height = boundRect.height(); if (boundRect.x() > r.x()) { x = boundRect.x()-r.x(); } else if (r.x() > boundRect.x()) { destRect.setX(r.x()); width = (boundRect.x() + boundRect.width()) - r.x(); } if (boundRect.y() > r.y()) { y = boundRect.y()-r.y(); } else if (r.y() > boundRect.y()) { destRect.setY(r.y()); height = (boundRect.y() + boundRect.height()) - r.y(); } if (width <= 0 || height <= 0) return; srcRect.setRect(x,y,width,height); } DrawImage(destRect, im, srcRect, alpha); im->DecrRef(); }
void MythYUVAPainter::DrawRect(const QRect &area, const QBrush &fillBrush, const QPen &linePen, int alpha) { QBrush brush(fillBrush); switch (fillBrush.style()) { case Qt::LinearGradientPattern: case Qt::RadialGradientPattern: case Qt::ConicalGradientPattern: { QGradient gradient = *fillBrush.gradient(); QGradientStops stops = gradient.stops(); for (QGradientStops::iterator it = stops.begin(); it != stops.end(); ++it) { it->second = rgb_to_yuv(it->second); it->second.setAlpha(alpha); } gradient.setStops(stops); brush = gradient; } break; default: brush.setColor(rgb_to_yuv(brush.color())); break; } QPen pen(linePen); pen.setColor(rgb_to_yuv(pen.color())); // We pull an image here, in the hopes that when DrawRect // pulls an image this will still be in the cache and have // the right properties. MythImage *im = GetImageFromRect(area, 0, 0, brush, pen); if (im) { im->SetToYUV(); im->DecrRef(); im = NULL; } MythQImagePainter::DrawRect(area, brush, pen, alpha); }
void ReferenceCounter::PrintDebug(void) { QReadLocker locker(&leakLock); uint cnt = 0; QMap<ReferenceCounter*,LeakInfo>::iterator it = leakMap.begin(); for (; it != leakMap.end(); ++it) cnt += (*it).name.startsWith("CommandLineArg") ? 0 : 1; LOG((cnt) ? VB_GENERAL : VB_REFCOUNT, LOG_INFO, QString("Leaked %1 reference counted objects").arg(cnt)); for (it = leakMap.begin(); it != leakMap.end(); ++it) { if ((*it).name.startsWith("CommandLineArg")) continue; LOG(VB_REFCOUNT, LOG_INFO, QString(" Leaked %1(0x%2) reference count %3") .arg((*it).name) .arg(reinterpret_cast<intptr_t>(it.key()),0,16) .arg((*it).refCount)); /// This part will not work on some non-Linux machines.. #if defined(__linux__) || defined(__LINUX__) MythImage *img = NULL; if ((*it).name.startsWith("MythImage") || (*it).name.startsWith("MythQtImage")) { img = reinterpret_cast<MythImage*>(it.key()); } if (img) { LOG(VB_REFCOUNT, LOG_INFO, QString(" Image id %1 cache size %2 file '%3'") .arg(img->GetID()) .arg(img->GetCacheSize()) .arg(img->GetFileName())); } #endif /// } }
void MythYUVAPainter::DrawEllipse(const QRect &area, const QBrush &fillBrush, const QPen &linePen, int alpha) { QBrush brush(fillBrush); brush.setColor(rgb_to_yuv(brush.color())); QPen pen(linePen); pen.setColor(rgb_to_yuv(pen.color())); // We pull an image here, in the hopes that when DrawRect // pulls an image this will still be in the cache and have // the right properties. MythImage *im = GetImageFromRect(area, 0, 1, brush, pen); if (im) { im->SetToYUV(); im->DecrRef(); im = NULL; } MythQImagePainter::DrawEllipse(area, brush, pen, alpha); }
void MythPainter::ExpireImages(int64_t max) { bool recompute = false; while (!m_StringExpireList.empty()) { if (m_SoftwareCacheSize < max) break; QString oldmsg = m_StringExpireList.front(); m_StringExpireList.pop_front(); QMap<QString, MythImage*>::iterator it = m_StringToImageMap.find(oldmsg); if (it == m_StringToImageMap.end()) { recompute = true; continue; } MythImage *oldim = *it; it = m_StringToImageMap.erase(it); if (oldim) { m_SoftwareCacheSize -= oldim->bytesPerLine() * oldim->height(); if (m_SoftwareCacheSize < 0) { m_SoftwareCacheSize = 0; recompute = true; } oldim->DecrRef(); } } if (recompute) { m_SoftwareCacheSize = 0; QMap<QString, MythImage*>::iterator it = m_StringToImageMap.begin(); for (; it != m_StringToImageMap.end(); ++it) m_SoftwareCacheSize += (*it)->bytesPerLine() * (*it)->height(); } }
void MythYUVAPainter::DrawText(const QRect &dest, const QString &msg, int flags, const MythFontProperties &font, int alpha, const QRect &boundRect) { MythFontProperties *converted = GetConvertedFont(font); if (converted) { // We pull an image here, in the hopes that when DrawText // pulls an image this will still be in the cache and have // the right properties. MythImage *im = GetImageFromString(msg, flags, dest, *converted); if (im) { im->SetToYUV(); im->DecrRef(); im = NULL; } MythQImagePainter::DrawText(dest, msg, flags, *converted, alpha, boundRect); } }
void MythPainter::ExpireImages(int max) { if (m_StringExpireList.size() < 1) return; while (m_SoftwareCacheSize > max) { QString oldmsg = m_StringExpireList.front(); m_StringExpireList.pop_front(); MythImage *oldim = NULL; if (m_StringToImageMap.contains(oldmsg)) oldim = m_StringToImageMap[oldmsg]; m_StringToImageMap.remove(oldmsg); if (oldim) { m_SoftwareCacheSize -= oldim->bytesPerLine() * oldim->height(); oldim->DownRef(); } } }
bool ThumbFinder::getFrameImage(bool needKeyFrame, int64_t requiredPTS) { AVPacket pkt; AVFrame orig; AVFrame retbuf; memset(&orig, 0, sizeof(AVFrame)); memset(&retbuf, 0, sizeof(AVFrame)); av_init_packet(&pkt); int frameFinished = 0; int keyFrame; int frameCount = 0; bool gotKeyFrame = false; while (av_read_frame(m_inputFC, &pkt) >= 0 && !frameFinished) { if (pkt.stream_index == m_videostream) { frameCount++; keyFrame = pkt.flags & AV_PKT_FLAG_KEY; if (m_startPTS == -1 && pkt.dts != AV_NOPTS_VALUE) { m_startPTS = pkt.dts; m_frameTime = pkt.duration; } if (keyFrame) gotKeyFrame = true; if (!gotKeyFrame && needKeyFrame) { av_packet_unref(&pkt); continue; } if (m_firstIFramePTS == -1) m_firstIFramePTS = pkt.dts; av_frame_unref(m_frame); frameFinished = 0; int ret = avcodec_receive_frame(m_codecCtx, m_frame); if (ret == 0) frameFinished = 1; if (ret == 0 || ret == AVERROR(EAGAIN)) ret = avcodec_send_packet(m_codecCtx, &pkt); if (requiredPTS != -1 && pkt.dts != AV_NOPTS_VALUE && pkt.dts < requiredPTS) frameFinished = false; m_currentPTS = pkt.dts; } av_packet_unref(&pkt); } if (frameFinished) { av_image_fill_arrays(retbuf.data, retbuf.linesize, m_outputbuf, AV_PIX_FMT_RGB32, m_frameWidth, m_frameHeight, IMAGE_ALIGN); AVFrame *tmp = m_frame; m_deinterlacer->DeinterlaceSingle(tmp, tmp); m_copy.Copy(&retbuf, AV_PIX_FMT_RGB32, tmp, m_codecCtx->pix_fmt, m_frameWidth, m_frameHeight); QImage img(m_outputbuf, m_frameWidth, m_frameHeight, QImage::Format_RGB32); QByteArray ffile = m_frameFile.toLocal8Bit(); if (!img.save(ffile.constData(), "JPEG")) { LOG(VB_GENERAL, LOG_ERR, "Failed to save thumb: " + m_frameFile); } if (m_updateFrame) { MythImage *mimage = GetMythMainWindow()->GetCurrentPainter()->GetFormatImage(); mimage->Assign(img); m_frameImage->SetImage(mimage); mimage->DecrRef(); } updateCurrentPos(); } return true; }
void SubtitleScreen::DisplayAVSubtitles(void) { if (!m_player || !m_subreader) return; AVSubtitles* subs = m_subreader->GetAVSubtitles(); QMutexLocker lock(&(subs->lock)); if (subs->buffers.empty() && (kDisplayAVSubtitle != m_subtitleType)) return; VideoOutput *videoOut = m_player->GetVideoOutput(); VideoFrame *currentFrame = videoOut ? videoOut->GetLastShownFrame() : NULL; if (!currentFrame || !videoOut) return; float tmp = 0.0; QRect dummy; videoOut->GetOSDBounds(dummy, m_safeArea, tmp, tmp, tmp); while (!subs->buffers.empty()) { const AVSubtitle subtitle = subs->buffers.front(); if (subtitle.start_display_time > currentFrame->timecode) break; ClearDisplayedSubtitles(); subs->buffers.pop_front(); for (std::size_t i = 0; i < subtitle.num_rects; ++i) { AVSubtitleRect* rect = subtitle.rects[i]; bool displaysub = true; if (subs->buffers.size() > 0 && subs->buffers.front().end_display_time < currentFrame->timecode) { displaysub = false; } if (displaysub && rect->type == SUBTITLE_BITMAP) { // AVSubtitleRect's image data's not guaranteed to be 4 byte // aligned. QSize img_size(rect->w, rect->h); QRect img_rect(rect->x, rect->y, rect->w, rect->h); QRect display(rect->display_x, rect->display_y, rect->display_w, rect->display_h); // XSUB and some DVD/DVB subs are based on the original video // size before the video was converted. We need to guess the // original size and allow for the difference int right = rect->x + rect->w; int bottom = rect->y + rect->h; if (subs->fixPosition || (currentFrame->height < bottom) || (currentFrame->width < right)) { int sd_height = 576; if ((m_player->GetFrameRate() > 26.0f) && bottom <= 480) sd_height = 480; int height = ((currentFrame->height <= sd_height) && (bottom <= sd_height)) ? sd_height : ((currentFrame->height <= 720) && bottom <= 720) ? 720 : 1080; int width = ((currentFrame->width <= 720) && (right <= 720)) ? 720 : ((currentFrame->width <= 1280) && (right <= 1280)) ? 1280 : 1920; display = QRect(0, 0, width, height); } QRect scaled = videoOut->GetImageRect(img_rect, &display); QImage qImage(img_size, QImage::Format_ARGB32); for (int y = 0; y < rect->h; ++y) { for (int x = 0; x < rect->w; ++x) { const uint8_t color = rect->pict.data[0][y*rect->pict.linesize[0] + x]; const uint32_t pixel = *((uint32_t*)rect->pict.data[1]+color); qImage.setPixel(x, y, pixel); } } if (scaled.size() != img_size) { qImage = qImage.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = videoOut->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); long long displayfor = subtitle.end_display_time - subtitle.start_display_time; if (displayfor == 0) displayfor = 60000; displayfor = (displayfor < 50) ? 50 : displayfor; long long late = currentFrame->timecode - subtitle.start_display_time; MythUIImage *uiimage = NULL; if (image) { image->Assign(qImage); QString name = QString("avsub%1").arg(i); uiimage = new MythUIImage(this, name); if (uiimage) { m_refreshArea = true; uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); m_expireTimes.insert(uiimage, currentFrame->timecode + displayfor); } } if (uiimage) { LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display %1AV subtitle for %2ms") .arg(subtitle.forced ? "FORCED " : "") .arg(displayfor)); if (late > 50) LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AV Sub was %1ms late").arg(late)); } } #ifdef USING_LIBASS else if (displaysub && rect->type == SUBTITLE_ASS) { InitialiseAssTrack(m_player->GetDecoder()->GetTrack(kTrackTypeSubtitle)); AddAssEvent(rect->ass); } #endif } m_subreader->FreeAVSubtitle(subtitle); } #ifdef USING_LIBASS RenderAssTrack(currentFrame->timecode); #endif }
/** * Update the various fields of a MythNotificationScreen. */ void MythNotificationScreen::Init(void) { if (!m_refresh) // nothing got changed so far, return return; AdjustYPosition(); if (m_artworkImage && (m_update & kImage)) { if (!m_imagePath.isNull()) { // We have a path to the image, use it m_artworkImage->SetFilename(m_imagePath); m_artworkImage->Load(); } else if (!m_image.isNull()) { // We don't have a path to the image, but the image itself MythImage *img = m_artworkImage->GetPainter()->GetFormatImage(); img->Assign(m_image); m_artworkImage->SetImage(img); img->DecrRef(); } else { // Will default to displaying whatever placeholder image is defined // in the xml by the themer, means we can show _something_ rather than // a big empty hole. Generally you always want to call Reset() in // these circumstances m_artworkImage->Reset(); } } if (m_update != kNone) { InfoMap tmap; tmap["title"] = m_title; if (m_update & kImage) { tmap["image"] = m_imagePath; } tmap["origin"] = m_origin; tmap["description"] = m_description; tmap["extra"] = m_extra; if (m_update & kDuration) { tmap["progress_text"] = m_progresstext; tmap["progress"] = QString("%1").arg((int)(m_progress * 100)); } SetTextFromMap(tmap); } if (m_update & kMetaData) { if (m_titleText && m_title.isNull()) { m_titleText->Reset(); } if (m_originText && m_origin.isNull()) { m_originText->Reset(); } if (m_descriptionText && m_description.isNull()) { m_descriptionText->Reset(); } if (m_extraText && m_extra.isNull()) { m_extraText->Reset(); } } if (m_update & kDuration) { if (m_progresstextText && m_progresstext.isEmpty()) { m_progresstextText->Reset(); } if (m_progressBar) { if (m_progress >= 0) { m_progressBar->SetStart(0); m_progressBar->SetTotal(100); m_progressBar->SetUsed(100 * m_progress); } else { // Same as above, calling Reset() allows for a sane, themer defined //default to be displayed m_progressBar->Reset(); } } } if (m_progressBar) { m_progressBar->SetVisible((m_content & kDuration) != 0); } SetErrorState(); if (m_mediaState && (m_update & kImage)) { m_mediaState->DisplayState(m_update & kNoArtwork ? "noartwork" : "ok"); LOG(VB_GUI, LOG_DEBUG, LOC + QString("Init: Set media state to %1").arg(m_update & kNoArtwork ? "noartwork" : "ok")); } // No field will be refreshed the next time unless specified otherwise m_update = kNone; if (GetScreenStack() && !m_added) { GetScreenStack()->AddScreen(this); m_added = true; } m_refresh = false; }
void BDOverlayScreen::DisplayBDOverlay(BDOverlay *overlay) { if (!overlay || !m_player) return; if (!overlay->m_data) { m_overlayArea = overlay->m_position; SetArea(MythRect(m_overlayArea)); DeleteAllChildren(); m_overlayMap.clear(); SetRedraw(); LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Initialised Size: %1x%2 (%3+%4) Plane: %5 Pts: %6") .arg(overlay->m_position.width()) .arg(overlay->m_position.height()) .arg(overlay->m_position.left()) .arg(overlay->m_position.top()) .arg(overlay->m_plane) .arg(overlay->m_pts)); BDOverlay::DeleteOverlay(overlay); return; } if (!m_overlayArea.isValid()) { LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Overlay image submitted before initialisation."); } VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; QRect rect = overlay->m_position; QString hash = QString("%1+%2+%3x%4") .arg(rect.left()).arg(rect.top()) .arg(rect.width()).arg(rect.height()); // remove if we already have this overlay if (m_overlayMap.contains(hash)) { LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Removing %1 (%2 left)") .arg(hash).arg(m_overlayMap.size())); MythUIImage *old = m_overlayMap.take(hash); DeleteChild(old); } // convert the overlay palette to ARGB uint32_t *origpalette = (uint32_t *)(overlay->m_palette); QVector<unsigned int> palette; for (int i = 0; i < 256; i++) { int y = (origpalette[i] >> 0) & 0xff; int cr = (origpalette[i] >> 8) & 0xff; int cb = (origpalette[i] >> 16) & 0xff; int a = (origpalette[i] >> 24) & 0xff; int r = int(y + 1.4022 * (cr - 128)); int b = int(y + 1.7710 * (cb - 128)); int g = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)); if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; if (r > 0xff) r = 0xff; if (g > 0xff) g = 0xff; if (b > 0xff) b = 0xff; palette.push_back((a << 24) | (r << 16) | (g << 8) | b); } // convert the image to QImage QImage img(rect.size(), QImage::Format_Indexed8); memcpy(img.bits(), overlay->m_data, rect.width() * rect.height()); img.setColorTable(palette); img.convertToFormat(QImage::Format_ARGB32); // add to screen QRect scaled = vo->GetImageRect(rect); if (scaled.size() != rect.size()) { img = img.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = vo->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); if (image) { image->Assign(img); MythUIImage *uiimage = new MythUIImage(this, "bdoverlay"); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); m_overlayMap.insert(hash, uiimage); LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Added %1 (%2 tot)") .arg(hash).arg(m_overlayMap.size())); } } SetRedraw(); BDOverlay::DeleteOverlay(overlay); }
MythImage *MythUIHelper::LoadCacheImage(QString srcfile, QString label, MythPainter *painter, ImageCacheMode cacheMode) { LOG(VB_GUI | VB_FILE, LOG_INFO, LOC + QString("LoadCacheImage(%1,%2)").arg(srcfile).arg(label)); if (srcfile.isEmpty() || label.isEmpty()) return NULL; if (!(kCacheForceStat & cacheMode)) { // Some screens include certain images dozens or even hundreds of // times. Even if the image is in the cache, there is still a // stat system call on the original file to see if it has changed. // This code relaxes the original-file check so that the check // isn't repeated if it was already done within kImageCacheTimeout // seconds. // This only applies to the MEMORY cache const uint kImageCacheTimeout = 60; uint now = MythDate::current().toTime_t(); QMutexLocker locker(d->m_cacheLock); if (d->imageCache.contains(label) && d->CacheTrack[label] + kImageCacheTimeout > now) { d->imageCache[label]->IncrRef(); return d->imageCache[label]; } } MythImage *ret = NULL; // Check Memory Cache ret = GetImageFromCache(label); // If the image is in the memory or we are not ignoring the disk cache // then proceed to check whether the source file is newer than our cached // copy if (ret || !(cacheMode & kCacheIgnoreDisk)) { // Create url to image in disk cache QString cachefilepath = GetThemeCacheDir() + '/' + label; QFileInfo cacheFileInfo(cachefilepath); // If the file isn't in the disk cache, then we don't want to bother // checking the last modified times of the original if (!cacheFileInfo.exists()) return NULL; // Now compare the time on the source versus our cached copy QDateTime srcLastModified; // For internet images this involves querying the headers of the remote // image. This is slow even without redownloading the whole image if ((srcfile.startsWith("http://")) || (srcfile.startsWith("https://")) || (srcfile.startsWith("ftp://"))) { // If the image is in the memory cache then skip the last modified // check, since memory cached images are loaded in the foreground // this can cause an intolerable delay. The images won't stay in // the cache forever and so eventually they will be checked. if (ret) srcLastModified = cacheFileInfo.lastModified(); else { srcLastModified = GetMythDownloadManager()->GetLastModified(srcfile); } } else if (srcfile.startsWith("myth://")) srcLastModified = RemoteFile::LastModified(srcfile); else { if (!FindThemeFile(srcfile)) return NULL; QFileInfo original(srcfile); if (original.exists()) srcLastModified = original.lastModified(); } // Now compare the timestamps, if the cached image is newer than the // source image we can use it, otherwise we want to remove it from the // cache if (cacheFileInfo.lastModified() >= srcLastModified) { // If we haven't already loaded the image from the memory cache // and we're not ignoring the disk cache, then it's time to load // it from there instead if (!ret && (cacheMode == kCacheNormal)) { if (painter) ret = painter->GetFormatImage(); // Load file from disk cache to memory cache if (ret->Load(cachefilepath)) { // Add to ram cache, and skip saving to disk since that is // where we found this in the first place. CacheImage(label, ret, true); } else { LOG(VB_GUI | VB_FILE, LOG_WARNING, LOC + QString("LoadCacheImage: Could not load :%1") .arg(cachefilepath)); ret->SetIsInCache(false); ret->DecrRef(); ret = NULL; } } } else { ret = NULL; // If file has changed on disk, then remove it from the memory // and disk cache RemoveFromCacheByURL(label); } } return ret; }