diff --git a/kaffeine/src/player-parts/libmpv-part/libmpv_part.cpp b/kaffeine/src/player-parts/libmpv-part/libmpv_part.cpp index 44655c6..2c0c1f2 100644 --- a/kaffeine/src/player-parts/libmpv-part/libmpv_part.cpp +++ b/kaffeine/src/player-parts/libmpv-part/libmpv_part.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,9 @@ #include #include #include +#include +#include +#include #include // Kaffeine @@ -180,6 +184,8 @@ void MpvPart::initActions() new KWidgetAction(m_volume, i18n("Volume"), 0, 0, 0, actionCollection(), "player_volume"); /*** Stream recording toolbar ***/ + new TDEAction(i18n("Take Screenshot"), "ksnapshot", Key_S, this, SLOT(slotShot()), actionCollection(), "record_shot"); + m_recordAction = new TDEToggleAction(i18n("&Record stream"), "player_record", Key_R, this, SLOT(slotToggleRecording()), actionCollection(), "record_toggle"); new TDEAction(i18n("Set recording file"), "document-open", 0, this, SLOT(slotSetRecordingFile()), actionCollection(), "record_open"); m_recordFile = new TQLabel(0); @@ -753,4 +759,159 @@ void MpvPart::hideContextMenu() { } } +#define THROW_ERROR(msg, details) \ + kdError() << "mpv: " << msg << " (" << details << ")" << endl; \ + KMessageBox::detailedError(nullptr, i18n(msg), details); \ + +#define THROW_ERROR_FREE(msg, details) \ + THROW_ERROR(msg, details) \ + mpv_free_node_contents(&result); + +void MpvPart::slotShot() { + mpv_node result; + int w, h, stride; + char *format; + mpv_byte_array *data; + + // Get screenshot as raw data in memory + const char *args[] = {"screenshot-raw", nullptr}; + if (!mpv_command_ret(m_mpv, args, &result) == MPV_ERROR_SUCCESS) { + THROW_ERROR("Internal error", "screenshot-raw command failed") + return; + } + + if (result.format != MPV_FORMAT_NODE_MAP) { + THROW_ERROR("Internal error", "screenshot-raw command returned wrong data format") + return; + } + + // Validate the result returned by `screenshot-raw` +#define CHECK_ARG(index, key) \ + if (qstrcmp(result.u.list->keys[index], key) != 0) { \ + TQString error = TQString("screenshot-raw returned wrong argument at #%1: %2") \ + .arg(index).arg(key); \ + THROW_ERROR_FREE("Internal error", error) \ + return; \ + } + +CHECK_ARG(0, "w") +w = result.u.list->values[0].u.int64; + +CHECK_ARG(1, "h") +h = result.u.list->values[1].u.int64; + +CHECK_ARG(2, "stride") +stride = result.u.list->values[2].u.int64; + +CHECK_ARG(3, "format") +format = result.u.list->values[3].u.string; + +CHECK_ARG(4, "data") +data = result.u.list->values[4].u.ba; + +#undef CHECK_ARG + + // Validate image format (we can handle only bgr0, which is the default anyway) + kdDebug() << "mpv: screenshot-raw image format: " << format << endl; + if (qstrcmp(format, "bgr0") != 0) { + THROW_ERROR_FREE("Internal error", "mpv: screenshot-raw returned image format other than bgr0") + return; + } + + // An empty image is useless but not a fatal error + if (w <= 0 || h <= 0) { + kdWarning() << "mpv: screenshot-raw returned empty image" << endl; + } + + // Create a TQDataStream to easily read the raw image data + TQByteArray ba; + ba.setRawData(static_cast(data->data), data->size); + TQDataStream s(ba, IO_ReadOnly); + s.setByteOrder(TQDataStream::BigEndian); + + // Create image from raw B8G8R8X8 data + TQImage img(w, h, 32); + for (uint y = 0; y < h; ++y) { + TQRgb *scanline = (TQRgb *)img.scanLine(y); + for (uint x = 0; x < w; x++) { + uchar r, g, b, z; + s >> b >> g >> r >> z; + scanline[x] = tqRgb(r, g, b); + } + } + + + // Try to launch ksnapshot + TQString ksError; + TQCString ksDcop; + + int ksSuccess = kapp->startServiceByDesktopName("ksnapshot", TQString::null, + &ksError, &ksDcop); + if (ksSuccess == 0) { + // Serialize the image to pass via dcop + TQByteArray pix; + TQDataStream ds(pix, IO_WriteOnly); + ds << img; + + // Send the serialized image to the newly initialized ksnapshot instance + if (!kapp->dcopClient()->send(ksDcop, "interface", "setPixmap(TQPixmap)", pix)) { + THROW_ERROR("Unable to launch KSnapshot!", i18n("You seem to be using an incompatible version of KSnapshot.")); + return; + } + } + else { + TQStringList mimeTypes = KImageIO::mimeTypes(KImageIO::Writing); + if (mimeTypes.isEmpty()) { + THROW_ERROR("No image formats supported!", "KImageIO does not support any image format for export."); + return; + } + + KFileDialog *saveDlg = new KFileDialog(TQString::null, TQString::null, + widget(), "screenshotSaver", false); + saveDlg->setOperationMode(KFileDialog::Saving); + saveDlg->setMimeFilter(mimeTypes); + + if (!saveDlg->exec()) { + return; + } + + KURL saveURL = saveDlg->selectedURL(); + if (saveURL.isEmpty()) return; + + TQString type = KImageIO::type(saveURL.url()); + if (!KImageIO::canWrite(type)) { + THROW_ERROR("Cannot write image", "Format " + type + " is unsupported for export.") + return; + } + + if (saveURL.isLocalFile()) { + if (!img.save(saveURL.path(), type.latin1())) { + THROW_ERROR("I/O Error", "Could not save file!") + return; + } + } + else { + KTempFile tempFile; + tempFile.setAutoDelete(true); + TQString tempFileName = tempFile.name(); + if (tempFileName.isEmpty()) { + THROW_ERROR("I/O Error", "Could not create temporary file!") + return; + } + + if (!img.save(tempFileName, type.latin1())) { + THROW_ERROR("I/O Error", "Could not write to temporary file!") + return; + } + + if (!TDEIO::NetAccess::upload(tempFileName, saveURL, widget())) { + THROW_ERROR("Upload error", "Could not upload image.") + return; + } + } + } +} +#undef THROW_ERROR +#undef THROW_ERROR_FREE + #include "libmpv_part.moc" \ No newline at end of file diff --git a/kaffeine/src/player-parts/libmpv-part/libmpv_part.h b/kaffeine/src/player-parts/libmpv-part/libmpv_part.h index 3350d78..8658778 100644 --- a/kaffeine/src/player-parts/libmpv-part/libmpv_part.h +++ b/kaffeine/src/player-parts/libmpv-part/libmpv_part.h @@ -102,6 +102,8 @@ class MpvPart : public KaffeinePart void slotNext(); void slotSetPosition(uint); /* percent */ + void slotShot(); + void slotToggleRecording(); void startRecording(); void stopRecording(); diff --git a/kaffeine/src/player-parts/libmpv-part/libmpv_part.rc b/kaffeine/src/player-parts/libmpv-part/libmpv_part.rc index 74f2893..48b2983 100644 --- a/kaffeine/src/player-parts/libmpv-part/libmpv_part.rc +++ b/kaffeine/src/player-parts/libmpv-part/libmpv_part.rc @@ -12,7 +12,9 @@ + + @@ -34,6 +36,8 @@