File indexing completed on 2024-04-28 04:51:59

0001 /*
0002 SPDX-FileCopyrightText: 2012 Simon A. Eugster (Granjow)  <simon.eu@gmail.com>
0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "audioStreamInfo.h"
0007 #include "kdenlivesettings.h"
0008 
0009 #include "kdenlive_debug.h"
0010 #include <KLocalizedString>
0011 #include <cstdlib>
0012 
0013 AudioStreamInfo::AudioStreamInfo(const std::shared_ptr<Mlt::Producer> &producer, int audioStreamIndex, bool playlist)
0014     : m_audioStreamIndex(audioStreamIndex)
0015     , m_ffmpegAudioIndex(0)
0016     , m_samplingRate(48000)
0017     , m_channels(2)
0018     , m_bitRate(0)
0019 {
0020     // Fetch audio streams
0021     int streams = producer->get_int("meta.media.nb_streams");
0022     if (playlist && streams == 0) {
0023         // Playlist clips do not provide stream info
0024         m_audioStreams.insert(0, i18n("Audio"));
0025         // TODO: compute playlist channels
0026         m_audioChannels.insert(0, 2);
0027     }
0028     int streamIndex = 1;
0029     for (int ix = 0; ix < streams; ix++) {
0030         char property[200];
0031         snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
0032         QString type = producer->get(property);
0033         if (type == QLatin1String("audio")) {
0034             memset(property, 0, 200);
0035             snprintf(property, sizeof(property), "meta.media.%d.codec.channels", ix);
0036             int chan = producer->get_int(property);
0037             memset(property, 0, 200);
0038             snprintf(property, sizeof(property), "kdenlive:streamname.%d", ix);
0039             QString channelDescription = producer->get(property);
0040             if (channelDescription.isEmpty()) {
0041                 channelDescription = QString("%1|").arg(streamIndex++);
0042                 switch (chan) {
0043                 case 1:
0044                     channelDescription.append(i18n("Mono "));
0045                     break;
0046                 case 2:
0047                     channelDescription.append(i18n("Stereo "));
0048                     break;
0049                 default:
0050                     channelDescription.append(i18n("%1 channels ", chan));
0051                     break;
0052                 }
0053                 // Frequency
0054                 memset(property, 0, 200);
0055                 snprintf(property, sizeof(property), "meta.media.%d.codec.sample_rate", ix);
0056                 QString frequency(producer->get(property));
0057                 if (frequency.endsWith(QLatin1String("000"))) {
0058                     frequency.chop(3);
0059                     frequency.append(i18n("kHz "));
0060                 } else {
0061                     frequency.append(i18n("Hz "));
0062                 }
0063                 channelDescription.append(frequency);
0064                 memset(property, 0, 200);
0065                 snprintf(property, sizeof(property), "meta.media.%d.codec.name", ix);
0066                 channelDescription.append(producer->get(property));
0067             } else {
0068                 streamIndex++;
0069             }
0070             m_audioStreams.insert(ix, channelDescription);
0071             m_audioChannels.insert(ix, chan);
0072         }
0073     }
0074     QString active = producer->get("kdenlive:active_streams");
0075     updateActiveStreams(active);
0076     if (m_audioStreams.count() > 1 && active.isEmpty()) {
0077         // initialize enabled streams
0078         QStringList streamString;
0079         for (int streamIx : qAsConst(m_activeStreams)) {
0080             streamString << QString::number(streamIx);
0081         }
0082         producer->set("kdenlive:active_streams", streamString.join(QLatin1Char(';')).toUtf8().constData());
0083     }
0084 
0085     if (audioStreamIndex > -1) {
0086         QByteArray key;
0087         key = QStringLiteral("meta.media.%1.codec.sample_fmt").arg(audioStreamIndex).toLocal8Bit();
0088         m_samplingFormat = QString::fromLatin1(producer->get(key.data()));
0089 
0090         key = QStringLiteral("meta.media.%1.codec.sample_rate").arg(audioStreamIndex).toLocal8Bit();
0091         m_samplingRate = producer->get_int(key.data());
0092 
0093         key = QStringLiteral("meta.media.%1.codec.bit_rate").arg(audioStreamIndex).toLocal8Bit();
0094         m_bitRate = producer->get_int(key.data());
0095 
0096         key = QStringLiteral("meta.media.%1.codec.channels").arg(audioStreamIndex).toLocal8Bit();
0097         m_channels = producer->get_int(key.data());
0098         setAudioIndex(producer, m_audioStreamIndex);
0099     }
0100 }
0101 
0102 AudioStreamInfo::~AudioStreamInfo() = default;
0103 
0104 int AudioStreamInfo::samplingRate() const
0105 {
0106     return m_samplingRate;
0107 }
0108 
0109 int AudioStreamInfo::channels() const
0110 {
0111     return m_channels;
0112 }
0113 
0114 QMap<int, QString> AudioStreamInfo::streams() const
0115 {
0116     return m_audioStreams;
0117 }
0118 
0119 QMap<int, int> AudioStreamInfo::streamChannels() const
0120 {
0121     return m_audioChannels;
0122 }
0123 
0124 int AudioStreamInfo::channelsForStream(int stream) const
0125 {
0126     if (m_audioChannels.contains(stream)) {
0127         return m_audioChannels.value(stream);
0128     }
0129     return m_channels;
0130 }
0131 
0132 QList<int> AudioStreamInfo::activeStreamChannels() const
0133 {
0134     if (m_activeStreams.size() == 1 && m_activeStreams.contains(INT_MAX)) {
0135         return m_audioChannels.values();
0136     }
0137     QList<int> activeChannels;
0138     QMapIterator<int, QString> i(m_audioStreams);
0139     while (i.hasNext()) {
0140         i.next();
0141         if (m_activeStreams.contains(i.key())) {
0142             activeChannels << m_audioChannels.value(i.key());
0143         }
0144     }
0145     return activeChannels;
0146 }
0147 
0148 QMap<int, QString> AudioStreamInfo::activeStreams() const
0149 {
0150     QMap<int, QString> active;
0151     QMapIterator<int, QString> i(m_audioStreams);
0152     if (m_activeStreams.size() == 1 && m_activeStreams.contains(INT_MAX)) {
0153         active.insert(INT_MAX, i18n("Merged streams"));
0154     } else {
0155         while (i.hasNext()) {
0156             i.next();
0157             if (m_activeStreams.contains(i.key())) {
0158                 active.insert(i.key(), i.value());
0159             }
0160         }
0161     }
0162     return active;
0163 }
0164 
0165 int AudioStreamInfo::bitrate() const
0166 {
0167     return m_bitRate;
0168 }
0169 
0170 int AudioStreamInfo::audio_index() const
0171 {
0172     return m_audioStreamIndex;
0173 }
0174 
0175 int AudioStreamInfo::ffmpeg_audio_index() const
0176 {
0177     return m_ffmpegAudioIndex;
0178 }
0179 
0180 void AudioStreamInfo::dumpInfo() const
0181 {
0182     qCDebug(KDENLIVE_LOG) << "Info for audio stream " << m_audioStreamIndex << "\n\tChannels: " << m_channels << "\n\tSampling rate: " << m_samplingRate
0183                           << "\n\tBit rate: " << m_bitRate;
0184 }
0185 
0186 void AudioStreamInfo::setAudioIndex(const std::shared_ptr<Mlt::Producer> &producer, int ix)
0187 {
0188     m_audioStreamIndex = ix;
0189     if (ix > -1) {
0190         int streams = producer->get_int("meta.media.nb_streams");
0191         QList<int> audioStreams;
0192         for (int i = 0; i < streams; ++i) {
0193             QByteArray propertyName = QStringLiteral("meta.media.%1.stream.type").arg(i).toLocal8Bit();
0194             QString type = producer->get(propertyName.data());
0195             if (type == QLatin1String("audio")) {
0196                 audioStreams << i;
0197             }
0198         }
0199         if (audioStreams.count() > 1 && m_audioStreamIndex < audioStreams.count()) {
0200             m_ffmpegAudioIndex = audioStreams.indexOf(m_audioStreamIndex);
0201         }
0202     }
0203 }
0204 
0205 void AudioStreamInfo::updateActiveStreams(const QString &activeStreams)
0206 {
0207     // -1 = disable all audio
0208     // empty = enable all audio or first depending on config
0209     m_activeStreams.clear();
0210     if (activeStreams.isEmpty()) {
0211         switch (KdenliveSettings::multistream()) {
0212         case 1:
0213             // Enable first stream only
0214             m_activeStreams << m_audioStreams.firstKey();
0215             break;
0216         case 2:
0217             // Enable the first two streams only
0218             {
0219                 QList<int> str = m_audioStreams.keys();
0220                 while (!str.isEmpty()) {
0221                     m_activeStreams << str.takeFirst();
0222                     if (m_activeStreams.size() == 2) {
0223                         break;
0224                     }
0225                 }
0226                 break;
0227             }
0228         default:
0229             // Enable all streams
0230             m_activeStreams = m_audioStreams.keys();
0231             break;
0232         }
0233         return;
0234     }
0235     QStringList st = activeStreams.split(QLatin1Char(';'));
0236     for (const QString &s : qAsConst(st)) {
0237         m_activeStreams << s.toInt();
0238     }
0239 }
0240 
0241 void AudioStreamInfo::renameStream(int ix, const QString &streamName)
0242 {
0243     if (m_audioStreams.contains(ix)) {
0244         m_audioStreams.insert(ix, streamName);
0245     }
0246 }