[multimedia/kdenlive/release/26.08] src: Ensure attached picture stream are not incorrectly marked as video
Jean-Baptiste Mardelle <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 9c87390daa9692af34941e2f9617b6da90eea5cb by Jean-Baptiste Mardelle.
Committed on 20/07/2026 at 16:54.
Pushed by mardelle into branch 'release/26.08'.
Ensure attached picture stream are not incorrectly marked as video
Fix disabling/enabling video in clip properties
Limit clip stream to max 99
BUG: 523268
FIXED-IN: 26.08.0
M +1 -1 src/lib/audio/audioInfo.cpp
M +2 -2 src/lib/audio/audioStreamInfo.cpp
M +43 -3 src/mltcontroller/clipcontroller.cpp
M +2 -0 src/mltcontroller/clipcontroller.h
M +29 -4 src/mltcontroller/clippropertiescontroller.cpp
https://invent.kde.org/multimedia/kdenlive/-/commit/9c87390daa9692af34941e2f9617b6da90eea5cb
diff --git a/src/lib/audio/audioInfo.cpp b/src/lib/audio/audioInfo.cpp
index ca2c56802a..c513660fad 100644
--- a/src/lib/audio/audioInfo.cpp
+++ b/src/lib/audio/audioInfo.cpp
@@ -16,7 +16,7 @@ AudioInfo::AudioInfo(const std::shared_ptr<Mlt::Producer> &producer)
// Since we already receive an MLT producer, we do not need to initialize MLT:
// Mlt::Factory::init(nullptr);
// Get the number of streams and add the information of each of them if it is an audio stream.
- int streams = producer->get_int("meta.media.nb_streams");
+ int streams = qMin(99, producer->get_int("meta.media.nb_streams"));
for (int i = 0; i < streams; ++i) {
QByteArray propertyName = QStringLiteral("meta.media.%1.stream.type").arg(i).toLocal8Bit();
const char *streamtype = producer->get(propertyName.data());
diff --git a/src/lib/audio/audioStreamInfo.cpp b/src/lib/audio/audioStreamInfo.cpp
index 7a877ea8c4..9e12211169 100644
--- a/src/lib/audio/audioStreamInfo.cpp
+++ b/src/lib/audio/audioStreamInfo.cpp
@@ -18,7 +18,7 @@ AudioStreamInfo::AudioStreamInfo(const std::shared_ptr<Mlt::Producer> &producer,
, m_bitRate(0)
{
// Fetch audio streams
- int streams = producer->get_int("meta.media.nb_streams");
+ int streams = qMin(99, producer->get_int("meta.media.nb_streams"));
if (streams == 0) {
if (playlist) {
// Playlist clips do not provide stream info
@@ -198,7 +198,7 @@ void AudioStreamInfo::setAudioIndex(const std::shared_ptr<Mlt::Producer> &produc
{
m_audioStreamIndex = ix;
if (ix > -1) {
- int streams = producer->get_int("meta.media.nb_streams");
+ int streams = qMin(99, producer->get_int("meta.media.nb_streams"));
QList<int> audioStreams;
for (int i = 0; i < streams; ++i) {
QByteArray propertyName = QStringLiteral("meta.media.%1.stream.type").arg(i).toLocal8Bit();
diff --git a/src/mltcontroller/clipcontroller.cpp b/src/mltcontroller/clipcontroller.cpp
index db0a96a8ec..6407d01518 100644
--- a/src/mltcontroller/clipcontroller.cpp
+++ b/src/mltcontroller/clipcontroller.cpp
@@ -142,7 +142,7 @@ void ClipController::addMasterProducer(const std::shared_ptr<Mlt::Producer> &pro
QList<int> videoStreams;
QList<int> audioStreams;
QList<int> subtitleStreams;
- int aStreams = m_properties->get_int("meta.media.nb_streams");
+ int aStreams = qMin(99, m_properties->get_int("meta.media.nb_streams"));
for (int ix = 0; ix < aStreams; ++ix) {
char property[200];
snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
@@ -285,7 +285,7 @@ void ClipController::getInfoForProducer()
} else if (m_service == QLatin1String("avformat") || m_service == QLatin1String("avformat-novalidate")) {
audioIndex = getProducerIntProperty(QStringLiteral("audio_index"));
m_videoIndex = getProducerIntProperty(QStringLiteral("video_index"));
- if (m_videoIndex == -1) {
+ if (m_videoIndex == -1 && !hasVideoStreams()) {
m_clipType = ClipType::Audio;
} else {
if (audioIndex == -1) {
@@ -863,6 +863,9 @@ void ClipController::checkAudioVideo()
m_hasVideo = true;
break;
}
+ if (m_masterProducer->property_exists("video_index") && m_masterProducer->get_int("video_index") == -1) {
+ m_hasVideo = false;
+ }
if (m_clipType == ClipType::Timeline || m_clipType == ClipType::Playlist) {
if (m_audioInfo == nullptr) {
if (m_hasAudio) {
@@ -1224,7 +1227,8 @@ std::shared_ptr<MarkerSortModel> ClipController::getFilteredMarkerModel() const
bool ClipController::isFullRange() const
{
bool full = !qstrcmp(m_masterProducer->get("meta.media.color_range"), "full");
- for (int i = 0; !full && i < m_masterProducer->get_int("meta.media.nb_streams"); i++) {
+ int streams = qMin(99, m_masterProducer->get_int("meta.media.nb_streams"));
+ for (int i = 0; !full && i < streams; i++) {
QString key = QStringLiteral("meta.media.%1.stream.type").arg(i);
QString streamType(m_masterProducer->get(key.toLatin1().constData()));
if (streamType == "video") {
@@ -1274,3 +1278,39 @@ std::shared_ptr<Mlt::Producer> ClipController::sequenceProducer(const QUuid &)
QReadLocker lock(&m_producerLock);
return m_masterProducer;
}
+
+bool ClipController::hasVideoStreams()
+{
+ QReadLocker lock(&m_producerLock);
+ if (!m_properties) {
+ return 0;
+ }
+ int aStreams = qMin(99, m_properties->get_int("meta.media.nb_streams"));
+ for (int ix = 0; ix < aStreams; ++ix) {
+ char property[200];
+ snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
+ QString type = m_properties->get(property);
+ if (type == QLatin1String("video")) {
+ QString key = QStringLiteral("meta.media.%1.codec.name").arg(ix);
+ QString codec_name = m_properties->get(key.toLatin1().constData());
+ if (codec_name == QLatin1String("png")) {
+ // This is a cover image, skip
+ continue;
+ }
+ if (codec_name == QLatin1String("mjpeg")) {
+ key = QStringLiteral("meta.media.%1.stream.frame_rate").arg(ix);
+ QString fps = m_properties->get(key.toLatin1().constData());
+ if (fps.isEmpty()) {
+ key = QStringLiteral("meta.media.%1.codec.frame_rate").arg(ix);
+ fps = m_properties->get(key.toLatin1().constData());
+ }
+ if (fps == QLatin1String("90000")) {
+ // This is a cover image, skip
+ continue;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/mltcontroller/clipcontroller.h b/src/mltcontroller/clipcontroller.h
index 70b49654d1..4a8cd74c9d 100644
--- a/src/mltcontroller/clipcontroller.h
+++ b/src/mltcontroller/clipcontroller.h
@@ -257,4 +257,6 @@ private:
QDomElement m_effectsToLoad;
/** @brief Build the audio info object */
void buildAudioInfo(int audioIndex);
+ /** @brief Check if a producer has at least one video stream */
+ bool hasVideoStreams();
};
diff --git a/src/mltcontroller/clippropertiescontroller.cpp b/src/mltcontroller/clippropertiescontroller.cpp
index 4469b32076..6bb1ef65a3 100644
--- a/src/mltcontroller/clippropertiescontroller.cpp
+++ b/src/mltcontroller/clippropertiescontroller.cpp
@@ -713,8 +713,10 @@ QWidget *ClipPropertiesController::constructPropertiesPage()
// Video index
if (!m_videoStreams.isEmpty()) {
- QString vix = m_properties->get("video_index");
+ const QString vix = m_properties->get("video_index");
m_originalProperties.insert(QStringLiteral("video_index"), vix);
+ const QString vStream = m_properties->get("vstream");
+ m_originalProperties.insert(QStringLiteral("vstream"), vStream);
hlay = new QHBoxLayout;
KDualAction *ac = new KDualAction(i18n("Disable video"), i18n("Enable video"), this);
@@ -743,12 +745,16 @@ QWidget *ClipPropertiesController::constructPropertiesPage()
int vindx = -1;
if (activated) {
videoStream->setEnabled(false);
+ properties.insert(QStringLiteral("video_index"), QStringLiteral("-1"));
+ properties.insert(QStringLiteral("vstream"), QStringLiteral("-1"));
+ properties.insert(QStringLiteral("set.test_image"), QStringLiteral("1"));
} else {
videoStream->setEnabled(true);
vindx = videoStream->currentData().toInt();
+ properties.insert(QStringLiteral("video_index"), QString::number(vindx));
+ properties.insert(QStringLiteral("vstream"), QString());
+ properties.insert(QStringLiteral("set.test_image"), QStringLiteral("0"));
}
- properties.insert(QStringLiteral("video_index"), QString::number(vindx));
- properties.insert(QStringLiteral("set.test_image"), vindx > -1 ? QStringLiteral("0") : QStringLiteral("1"));
Q_EMIT updateClipProperties(m_id, m_originalProperties, properties);
m_originalProperties = properties;
});
@@ -1507,11 +1513,30 @@ void ClipPropertiesController::fillProperties()
// Find maximum stream index values
m_videoStreams.clear();
- int aStreams = m_sourceProperties->get_int("meta.media.nb_streams");
+ int aStreams = qMin(99, m_sourceProperties->get_int("meta.media.nb_streams"));
for (int ix = 0; ix < aStreams; ++ix) {
const QString propertName = QStringLiteral("meta.media.%1.stream.type").arg(ix);
QString type = m_sourceProperties->get(propertName.toUtf8().constData());
if (type == QLatin1String("video")) {
+ // Discard attached pic streams
+ QString key = QStringLiteral("meta.media.%1.codec.name").arg(ix);
+ QString codec_name = m_properties->get(key.toLatin1().constData());
+ if (codec_name == QLatin1String("png")) {
+ // This is a cover image, skip
+ continue;
+ }
+ if (codec_name == QLatin1String("mjpeg")) {
+ key = QStringLiteral("meta.media.%1.stream.frame_rate").arg(ix);
+ QString fps = m_properties->get(key.toLatin1().constData());
+ if (fps.isEmpty()) {
+ key = QStringLiteral("meta.media.%1.codec.frame_rate").arg(ix);
+ fps = m_properties->get(key.toLatin1().constData());
+ }
+ if (fps == QLatin1String("90000")) {
+ // This is a cover image, skip
+ continue;
+ }
+ }
m_videoStreams << ix;
}
}