Skip to content

Commit

Permalink
sub: support internal tracks switch. but update at next demuxer read
Browse files Browse the repository at this point in the history
  • Loading branch information
wang-bin committed Aug 3, 2015
1 parent 20bce31 commit 23b8dc3
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 12 deletions.
10 changes: 9 additions & 1 deletion qml/QmlAV/QmlAVPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class QmlAVPlayer : public QObject, public QQmlParserStatus
Q_PROPERTY(QUrl externalAudio READ externalAudio WRITE setExternalAudio NOTIFY externalAudioChanged)
Q_PROPERTY(QVariantList internalAudioTracks READ internalAudioTracks NOTIFY internalAudioTracksChanged)
Q_PROPERTY(QVariantList externalAudioTracks READ externalAudioTracks NOTIFY externalAudioTracksChanged)
Q_PROPERTY(QVariantList internalSubtitleTracks READ internalSubtitleTracks NOTIFY internalSubtitleTracksChanged)
// internal subtitle, e.g. mkv embedded subtitles
Q_PROPERTY(int internalSubtitleTrack READ internalSubtitleTrack WRITE setInternalSubtitleTrack NOTIFY internalSubtitleTrackChanged)
public:
enum Loop { Infinite = -1 };
enum PlaybackState {
Expand Down Expand Up @@ -200,6 +203,9 @@ class QmlAVPlayer : public QObject, public QQmlParserStatus
void setExternalAudio(const QUrl& url);
QVariantList externalAudioTracks() const;

int internalSubtitleTrack() const;
void setInternalSubtitleTrack(int value);
QVariantList internalSubtitleTracks() const;
public Q_SLOTS:
void play();
void pause();
Expand Down Expand Up @@ -241,6 +247,8 @@ public Q_SLOTS:
void internalAudioTracksChanged();
void externalAudioChanged();
void externalAudioTracksChanged();
void internalSubtitleTrackChanged();
void internalSubtitleTracksChanged();

void errorChanged();
void error(Error error, const QString &errorString);
Expand Down Expand Up @@ -282,9 +290,9 @@ private Q_SLOTS:
ChannelLayout mChannelLayout;
int m_timeout;
bool m_abort_timeout;
QVariantList m_audio_streams, m_external_audio_streams;
int m_audio_track;
QUrl m_audio;
int m_sub_track;

QScopedPointer<MediaMetaData> m_metaData;
QVariantMap vcodec_opt;
Expand Down
25 changes: 24 additions & 1 deletion qml/QmlAVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ QmlAVPlayer::QmlAVPlayer(QObject *parent) :
, m_timeout(30000)
, m_abort_timeout(true)
, m_audio_track(0)
, m_sub_track(0)
{
classBegin();
}
Expand All @@ -86,6 +87,7 @@ void QmlAVPlayer::classBegin()
if (mpPlayer)
return;
mpPlayer = new AVPlayer(this);
connect(mpPlayer, SIGNAL(internalSubtitleTracksChanged(QVariantList)), SIGNAL(internalSubtitleTracksChanged()));
connect(mpPlayer, SIGNAL(internalAudioTracksChanged(QVariantList)), SIGNAL(internalAudioTracksChanged()));
connect(mpPlayer, SIGNAL(externalAudioTracksChanged(QVariantList)), SIGNAL(externalAudioTracksChanged()));
connect(mpPlayer, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged()));
Expand Down Expand Up @@ -379,6 +381,26 @@ QVariantList QmlAVPlayer::internalAudioTracks() const
return mpPlayer ? mpPlayer->internalAudioTracks() : QVariantList();
}

int QmlAVPlayer::internalSubtitleTrack() const
{
return m_sub_track;
}

void QmlAVPlayer::setInternalSubtitleTrack(int value)
{
if (m_sub_track == value)
return;
m_sub_track = value;
Q_EMIT internalSubtitleTrackChanged();
if (mpPlayer)
mpPlayer->setSubtitleStream(value);
}

QVariantList QmlAVPlayer::internalSubtitleTracks() const
{
return mpPlayer ? mpPlayer->internalSubtitleTracks() : QVariantList();
}

QStringList QmlAVPlayer::videoCodecPriority() const
{
return mVideoCodecs;
Expand Down Expand Up @@ -508,6 +530,7 @@ void QmlAVPlayer::setPlaybackState(PlaybackState playbackState)
mpPlayer->setInterruptOnTimeout(m_abort_timeout);
mpPlayer->setRepeat(mLoopCount - 1);
mpPlayer->setAudioStream(m_audio_track);
mpPlayer->setSubtitleStream(m_sub_track);
if (!vcodec_opt.isEmpty()) {
QVariantHash vcopt;
for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) {
Expand Down Expand Up @@ -666,7 +689,7 @@ void QmlAVPlayer::_q_started()
// TODO: in load()?
m_metaData->setValuesFromStatistics(mpPlayer->statistics());
if (!mHasAudio) {
mHasAudio = mpPlayer->audioStreamCount() > 0;
mHasAudio = !mpPlayer->internalAudioTracks().isEmpty();
if (mHasAudio)
emit hasAudioChanged();
}
Expand Down
5 changes: 3 additions & 2 deletions qml/plugins.qmltypes
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import QtQuick.tooling 1.2
import QtQuick.tooling 1.1

// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
Expand All @@ -7,7 +7,6 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable QtAV 1.6'

Module {
dependencies: ["QtQuick 2.0"]
Component {
name: "MediaMetaData"
prototype: "QObject"
Expand Down Expand Up @@ -208,6 +207,8 @@ Module {
Property { name: "externalAudio"; type: "QUrl" }
Property { name: "internalAudioTracks"; type: "QVariantList"; isReadonly: true }
Property { name: "externalAudioTracks"; type: "QVariantList"; isReadonly: true }
Property { name: "internalSubtitleTracks"; type: "QVariantList"; isReadonly: true }
Property { name: "internalSubtitleTrack"; type: "int" }
Signal { name: "loopCountChanged" }
Signal { name: "videoOutChanged" }
Signal { name: "paused" }
Expand Down
1 change: 1 addition & 0 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,7 @@ bool AVPlayer::setSubtitleStream(int n)
return true;
// TODO: xxxchanged signal
d->subtitle_track = n;
Q_EMIT subtitleStreamChanged(n);
if (!d->demuxer.isLoaded())
return true;
return d->applySubtitleStream(n, this);
Expand Down
6 changes: 6 additions & 0 deletions src/AVPlayerPrivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ QVariantList AVPlayer::Private::getTracksInfo(AVDemuxer *demuxer, AVDemuxer::Str
t["id"] = info.size();
t["file"] = demuxer->fileName();
AVStream *stream = demuxer->formatContext()->streams[s];
AVCodecContext *ctx = stream->codec;
if (ctx) {
t["codec"] = QByteArray(avcodec_descriptor_get(ctx->codec_id)->name);
if (ctx->extradata)
t["extra"] = QByteArray((const char*)ctx->extradata, ctx->extradata_size);
}
AVDictionaryEntry *tag = av_dict_get(stream->metadata, "language", NULL, 0);
if (!tag)
tag = av_dict_get(stream->metadata, "lang", NULL, 0);
Expand Down
7 changes: 4 additions & 3 deletions src/QtAV/AVPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@ class Q_AV_EXPORT AVPlayer : public QObject
int currentAudioStream() const;
int currentVideoStream() const;
int currentSubtitleStream() const;
QTAV_DEPRECATED int audioStreamCount() const;
QTAV_DEPRECATED int videoStreamCount() const;
QTAV_DEPRECATED int subtitleStreamCount() const;
int audioStreamCount() const;
int videoStreamCount() const;
int subtitleStreamCount() const;
/*!
* \brief capture and save current frame to "$HOME/.QtAV/filename_pts.png".
* To capture with custom configurations, such as name and dir, use
Expand Down Expand Up @@ -474,6 +474,7 @@ public slots:
void contrastChanged(int val);
void hueChanged(int val);
void saturationChanged(int val);
void subtitleStreamChanged(int value);
/*!
* \brief internalAudioTracksChanged
* Emitted when media is loaded. \sa internalAudioTracks
Expand Down
7 changes: 6 additions & 1 deletion src/QtAV/private/PlayerSubtitle.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define QTAV_PLAYERSUBTITLE_H

#include <QtCore/QObject>
#include <QtCore/QVariant>
#include <QtAV/QtAV_Global.h>

namespace QtAV {
Expand Down Expand Up @@ -68,17 +69,21 @@ private Q_SLOTS:
void onPlayerPositionChanged();
void onPlayerStart();
void tryReload();
void tryReloadInternalSub();
void updateInternalSubtitleTracks(const QVariantList& tracks);
void processInternalSubtitlePacket(const QtAV::Packet& packet);
void processInternalSubtitleHeader(const QByteArray &codec, const QByteArray& data);
private:
void connectSignals();
void disconnectSignals();
void tryReload(int flag); //1: internal, 2: external, 3: internal+external
private:
bool m_auto;
bool m_enabled;
bool m_enabled; // TODO: m_enable_external
AVPlayer *m_player;
Subtitle *m_sub;
QString m_file;
QVariantList m_tracks;
};

}
Expand Down
68 changes: 64 additions & 4 deletions src/subtitle/PlayerSubtitle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,19 @@ void PlayerSubtitle::onPlayerStart()
return;
m_sub->setFileName(m_file);
m_sub->setFuzzyMatch(false);
m_sub->loadAsync();
if (m_file.isEmpty()) {
const int n = m_player->currentSubtitleStream();
if (n >= 0 && !m_tracks.isEmpty() && m_tracks.size() <= n) {
m_sub->processHeader(QByteArray(), QByteArray()); // reset
return;
}
QVariantMap track = m_tracks[n].toMap();
QByteArray codec(track.value("codec").toByteArray());
QByteArray data(track.value("extra").toByteArray());
m_sub->processHeader(codec, data);
} else {
m_sub->loadAsync();
}
return;
}
if (m_file != m_sub->fileName())
Expand Down Expand Up @@ -188,13 +200,57 @@ void PlayerSubtitle::onEnabledChanged(bool value)

void PlayerSubtitle::tryReload()
{
if (!m_enabled)
return;
tryReload(3);
}

void PlayerSubtitle::tryReloadInternalSub()
{
tryReload(1);
}

void PlayerSubtitle::tryReload(int flag)
{
if (!m_player)
return;
if (!m_player->isPlaying())
return;
m_sub->loadAsync();
const int kReloadInternal = 1;
const int kReloadExternal = 1<<1;
if (flag & kReloadExternal) {
if (!m_file.isEmpty() && m_enabled) { //engine changed
m_sub->processHeader(QByteArray(), QByteArray()); // reset
m_sub->loadAsync();
return;
}
}
if (!(flag & kReloadInternal)) { // if internal flag is set, try internal first, then fallback to external if internal is failed
if (!m_enabled)
return;
if (flag & kReloadExternal) {
m_sub->processHeader(QByteArray(), QByteArray()); // reset
m_sub->loadAsync();
}
return;
}

const int n = m_player->currentSubtitleStream();
if (n < 0 || m_tracks.isEmpty() || m_tracks.size() <= n) {
m_sub->processHeader(QByteArray(), QByteArray()); // reset, null processor
//try to fallback to external sub if an invalid internal sub track is set
if ((flag & kReloadExternal) && m_enabled)
m_sub->loadAsync();
return;
}
QVariantMap track = m_tracks[n].toMap();
QByteArray codec(track.value("codec").toByteArray());
QByteArray data(track.value("extra").toByteArray());
m_sub->processHeader(codec, data);
// TODO: call processData with subtitle at current time current track
}

void PlayerSubtitle::updateInternalSubtitleTracks(const QVariantList &tracks)
{
m_tracks = tracks;
}

void PlayerSubtitle::processInternalSubtitlePacket(const QtAV::Packet &packet)
Expand All @@ -214,6 +270,9 @@ void PlayerSubtitle::connectSignals()
connect(m_player, SIGNAL(started()), this, SLOT(onPlayerStart()));
connect(m_player, SIGNAL(internalSubtitlePacketRead(QtAV::Packet)), this, SLOT(processInternalSubtitlePacket(QtAV::Packet)));
connect(m_player, SIGNAL(internalSubtitleHeaderRead(QByteArray,QByteArray)), this, SLOT(processInternalSubtitleHeader(QByteArray,QByteArray)));
connect(m_player, SIGNAL(internalSubtitleTracksChanged(QVariantList)), this, SLOT(updateInternalSubtitleTracks(QVariantList)));
// try to reload internal subtitle track. if failed and external subtitle is enabled, fallback to external
connect(m_player, SIGNAL(subtitleStreamChanged(int)), this, SLOT(tryReloadInternalSub()));
connect(m_sub, SIGNAL(codecChanged()), this, SLOT(tryReload()));
connect(m_sub, SIGNAL(enginesChanged()), this, SLOT(tryReload()));
}
Expand All @@ -225,6 +284,7 @@ void PlayerSubtitle::disconnectSignals()
disconnect(m_player, SIGNAL(started()), this, SLOT(onPlayerStart()));
disconnect(m_player, SIGNAL(internalSubtitlePacketRead(QtAV::Packet)), this, SLOT(processInternalSubtitlePacket(QtAV::Packet)));
disconnect(m_player, SIGNAL(internalSubtitleHeaderRead(QByteArray,QByteArray)), this, SLOT(processInternalSubtitleHeader(QByteArray,QByteArray)));
disconnect(m_player, SIGNAL(internalSubtitleTracksChanged(QVariantList)), this, SLOT(updateInternalSubtitleTracks(QVariantList)));
disconnect(m_sub, SIGNAL(codecChanged()), this, SLOT(tryReload()));
disconnect(m_sub, SIGNAL(enginesChanged()), this, SLOT(tryReload()));
}
Expand Down

0 comments on commit 23b8dc3

Please sign in to comment.