wang-bin / QtAV

A cross-platform multimedia framework based on Qt and FFmpeg. 基于Qt和FFmpeg的跨平台高性能音视频播放框架. Recommand to use new sdk https://github.com/wang-bin/mdk-sdk
http://qtav.org
3.94k stars 1.5k forks source link

QtAV QIODevice android #256

Closed cnhbljp closed 10 years ago

cnhbljp commented 10 years ago

我发现在android下,单独使用您的例子 qiodevice 正常,但如果加上UI ,则程序会终止。 修改使用player的例子,直接播放文件正常,但是用QIODevice的方式播放文件,程序也会终止。

wang-bin commented 10 years ago

你代码貼下

cnhbljp commented 10 years ago

MainWindow.cpp

/** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin wbsecg1@gmail.com

include "Slider.h"

//#include "StatisticsView.h"

/ TODO:

define SLIDER_ON_VO 0

define AVDEBUG() \

qDebug("%s %s @%d", **FILE**, **FUNCTION**, **LINE**);

using namespace QtAV; const qreal kVolumeInterval = 0.05;

static void QLabelSetElideText(QLabel label, QString text, int W = 0) { QFontMetrics metrix(label->font()); int width = label->width() - label->indent() - label->margin(); if (W || label->parent()) { int w = W; if (!w) w = ((QWidget)label->parent())->width(); width = qMax(w - label->indent() - label->margin() - 13*(30), 0); //TODO: why 30? } QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); label->setText(clippedText); }

MainWindow::MainWindow(QWidget *parent) : QWidget(parent) , mIsReady(false) , mHasPendingPlay(false) , mNullAO(false) , mControlOn(false) , mShowControl(2) , mpPlayer(0) , mpRenderer(0) , mpTempRenderer(0) {

setMouseTracking(true); //mouseMoveEvent without press.
connect(this, SIGNAL(ready()), SLOT(processPendingActions()));
//QTimer::singleShot(10, this, SLOT(setupUi()));
setupUi();
//setToolTip(tr("Click black area to use shortcut(see right click menu)"));

}

MainWindow::~MainWindow() {

if (mpVolumeSlider && !mpVolumeSlider->parentWidget()) {
    mpVolumeSlider->close();
    delete mpVolumeSlider;
    mpVolumeSlider = 0;
}

}

void MainWindow::initPlayer() {

mpPlayer = new AVPlayer(this);
mIsReady = true;
//mpPlayer->setAudioOutput(AudioOutputFactory::create(AudioOutputId_OpenAL));

//QHash<QByteArray, QByteArray> dict;
//dict.insert("rtsp_transport", "tcp");
//mpPlayer->setOptionsForFormat(dict);
qDebug("player created");

// connect(mpStopBtn, SIGNAL(clicked()), mpPlayer, SLOT(stop())); // connect(mpForwardBtn, SIGNAL(clicked()), mpPlayer, SLOT(seekForward())); // connect(mpBackwardBtn, SIGNAL(clicked()), mpPlayer, SLOT(seekBackward())); connect(mpVolumeBtn, SIGNAL(clicked()), SLOT(showHideVolumeBar())); connect(mpVolumeSlider, SIGNAL(sliderPressed()), SLOT(setVolume())); connect(mpVolumeSlider, SIGNAL(valueChanged(int)), SLOT(setVolume()));

// connect(mpPlayer, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), SLOT(onMediaStatusChanged())); connect(mpPlayer, SIGNAL(error(QtAV::AVError)), this, SLOT(handleError(QtAV::AVError))); connect(mpPlayer, SIGNAL(started()), this, SLOT(onStartPlay())); connect(mpPlayer, SIGNAL(stopped()), this, SLOT(onStopPlay())); connect(mpPlayer, SIGNAL(paused(bool)), this, SLOT(onPaused(bool))); connect(mpPlayer, SIGNAL(positionChanged(qint64)), this, SLOT(onPositionChange(qint64))); qDebug( "intplayerend") ; emit ready(); //emit this signal after connection. otherwise the slots may not be called for the first time }

void MainWindow::setupUi() { QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setSpacing(0); mainLayout->setMargin(0); setLayout(mainLayout);

mpPlayerLayout = new QVBoxLayout();
mpControl = new QWidget(this);
mpControl->setMaximumHeight(25);

//mpPreview = new QLable(this);

mpTimeSlider = new Slider(mpControl);
mpTimeSlider->setDisabled(true);
//mpTimeSlider->setFixedHeight(8);
mpTimeSlider->setMaximumHeight(8);
mpTimeSlider->setTracking(true);
mpTimeSlider->setOrientation(Qt::Horizontal);
mpTimeSlider->setMinimum(0);

if SLIDER_ON_VO

QGraphicsOpacityEffect *oe = new QGraphicsOpacityEffect(this);
oe->setOpacity(0.5);
mpTimeSlider->setGraphicsEffect(oe);

endif //SLIDER_ON_VO

mpCurrent = new QLabel(mpControl);
mpCurrent->setToolTip(tr("Current time"));
mpCurrent->setMargin(2);
mpCurrent->setText("00:00:00");
mpDuration = new QLabel(mpControl);
mpDuration->setToolTip(tr("Duration"));
mpDuration->setMargin(2);
mpDuration->setText("00:00:00");

mPlayPixmap = QPixmap(":/theme/button-play-pause.png");
int w = mPlayPixmap.width(), h = mPlayPixmap.height();
mPausePixmap = mPlayPixmap.copy(QRect(w/2, 0, w/2, h));
mPlayPixmap = mPlayPixmap.copy(QRect(0, 0, w/2, h));
qDebug("%d x %d", mPlayPixmap.width(), mPlayPixmap.height());
mpPlayPauseBtn = new Button(mpControl);
int a = qMin(w/2, h);
const int kMaxButtonIconWidth = 20;
const int kMaxButtonIconMargin = kMaxButtonIconWidth/3;
a = qMin(a, kMaxButtonIconWidth);

mpPlayPauseBtn->setIconWithSates(mPlayPixmap);
mpPlayPauseBtn->setIconSize(QSize(a, a));
mpPlayPauseBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin);

// mpStopBtn = new Button(mpControl); // mpStopBtn->setIconWithSates(QPixmap(":/theme/button-stop.png")); // mpStopBtn->setIconSize(QSize(a, a)); // mpStopBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin); // mpBackwardBtn = new Button(mpControl); // mpBackwardBtn->setIconWithSates(QPixmap(":/theme/button-rewind.png")); // mpBackwardBtn->setIconSize(QSize(a, a)); // mpBackwardBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin); // mpForwardBtn = new Button(mpControl); // mpForwardBtn->setIconWithSates(QPixmap(":/theme/button-fastforward.png")); // mpForwardBtn->setIconSize(QSize(a, a)); // mpForwardBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin); // mpOpenBtn = new Button(mpControl); // mpOpenBtn->setToolTip(tr("Open")); // mpOpenBtn->setIconWithSates(QPixmap(":/theme/open_folder.png")); // mpOpenBtn->setIconSize(QSize(a, a)); // mpOpenBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin);

mpVolumeBtn = new Button();
mpVolumeBtn->setIconWithSates(QPixmap(":/theme/button-max-volume.png"));
mpVolumeBtn->setIconSize(QSize(a, a));
mpVolumeBtn->setMaximumSize(a+kMaxButtonIconMargin+2, a+kMaxButtonIconMargin);

mpVolumeSlider = new Slider();
mpVolumeSlider->hide();
mpVolumeSlider->setOrientation(Qt::Horizontal);
mpVolumeSlider->setMinimum(0);
const int kVolumeSliderMax = 100;
mpVolumeSlider->setMaximum(kVolumeSliderMax);
mpVolumeSlider->setMaximumHeight(8);
mpVolumeSlider->setMaximumWidth(188);
mpVolumeSlider->setValue(int(1.0/kVolumeInterval*qreal(kVolumeSliderMax)/100.0));
setVolume();
//subMenu->setEnabled(false);
  // TODO: move to a func or class

mainLayout->addLayout(mpPlayerLayout);
mainLayout->addWidget(mpTimeSlider);
mainLayout->addWidget(mpControl);

QHBoxLayout *controlLayout = new QHBoxLayout();
controlLayout->setSpacing(0);
controlLayout->setMargin(1);
mpControl->setLayout(controlLayout);
controlLayout->addWidget(mpCurrent);

QSpacerItem *space = new QSpacerItem(mpPlayPauseBtn->width(), mpPlayPauseBtn->height(), QSizePolicy::MinimumExpanding);
controlLayout->addSpacerItem(space);
controlLayout->addWidget(mpVolumeSlider);
controlLayout->addWidget(mpVolumeBtn);

controlLayout->addWidget(mpPlayPauseBtn);

// controlLayout->addWidget(mpStopBtn); // controlLayout->addWidget(mpBackwardBtn); // controlLayout->addWidget(mpForwardBtn);

// controlLayout->addWidget(mpOpenBtn);

 controlLayout->addWidget(mpDuration);

 // connect(mpOpenBtn, SIGNAL(clicked()), SLOT(openFile()));
connect(mpPlayPauseBtn, SIGNAL(clicked()), SLOT(togglePlayPause()));
connect(mpTimeSlider, SIGNAL(sliderPressed()), SLOT(seek()));
connect(mpTimeSlider, SIGNAL(sliderReleased()), SLOT(seek()));
connect(mpTimeSlider, SIGNAL(onHover(int,int)), SLOT(onTimeSliderHover(int,int)));
    qDebug( "setupuiend11") ;
QTimer::singleShot(0, this, SLOT(initPlayer()));
qDebug( "setupuiend") ;

}

void MainWindow::processPendingActions() { qDebug( "processPendingActions") ; if (!mpTempRenderer) return; setRenderer(mpTempRenderer); mpTempRenderer = 0; if (mHasPendingPlay) { mHasPendingPlay = false; qDebug( " play(mFile);") ; play(mFile); } }

void MainWindow::enableAudio(bool yes) { mNullAO = !yes; if (!mpPlayer) return; mpPlayer->enableAudio(yes); }

void MainWindow::setAudioOutput(AudioOutput *ao) { Q_UNUSED(ao); }

void MainWindow::setRenderer(QtAV::VideoRenderer *renderer) { if (!mIsReady) { mpTempRenderer = renderer; return; } if (!renderer) return;

if SLIDER_ON_VO

int old_pos = 0;
int old_total = 0;
if (mpTimeSlider) {
    old_pos = mpTimeSlider->value();
    old_total = mpTimeSlider->maximum();
    mpTimeSlider->hide();
} else {
}

endif //SLIDER_ON_VO

renderer->widget()->setMouseTracking(true); //mouseMoveEvent without press.
mpPlayer->setRenderer(renderer);
QWidget *r = 0;
if (mpRenderer)
    r = mpRenderer->widget();
//release old renderer and add new
if (r) {
    mpPlayerLayout->removeWidget(r);
    if (r->testAttribute(Qt::WA_DeleteOnClose)) {
        r->close();
    } else {
        r->close();
        delete r;
    }
    r = 0;
}
mpRenderer = renderer;
//setInSize?
mpPlayerLayout->addWidget(renderer->widget());
resize(renderer->widget()->size());

if SLIDER_ON_VO

if (mpTimeSlider) {
    mpTimeSlider->setParent(mpRenderer->widget());
    mpTimeSlider->show();
}

endif //SLIDER_ON_VO

if (mpPlayer->renderer()->id() == VideoRendererId_GLWidget) {
} else if (mpPlayer->renderer()->id() == VideoRendererId_XV) {
 } else {
 }
mpPlayer->renderer()->forcePreferredPixelFormat(false);
qDebug("setrenderend");

}

void MainWindow::play(const QString &name) { mFile = name; if (!mIsReady) { mHasPendingPlay = true; return; } // if (!mFile.contains("://") || mFile.startsWith("file://")) { // mTitle = QFileInfo(mFile).fileName(); // }

QFile vidfile(mFile);

if (!vidfile.open(QIODevice::ReadOnly))
    return ;

QByteArray data = vidfile.read(vidfile.size());

vidfile.seek(0);
QBuffer buf(&data);
if (buf.open(QIODevice::ReadOnly)) {
    mpPlayer->setIODevice(&buf);
    mpPlayer->play();
}

//mpPlayer->play(name); }

void MainWindow::setVideoDecoderNames(const QStringList &vd) { QStringList vdnames; foreach (QString v, vd) { vdnames << v.toLower(); } QVector vidp; QVector vids = GetRegistedVideoDecoderIds(); foreach (VideoDecoderId vid, vids) { QString v(VideoDecoderFactory::name(vid).c_str()); if (vdnames.contains(v.toLower())) { vidp.append(vid); } } }

void MainWindow::openFile() { QString file = QFileDialog::getOpenFileName(0, tr("Open a media file")); if (file.isEmpty()) return; play(file); }

void MainWindow::togglePlayPause() { if (mpPlayer->isPlaying()) { qDebug("isPaused = %d", mpPlayer->isPaused()); mpPlayer->pause(!mpPlayer->isPaused()); } else { if (mFile.isEmpty()) return; mpPlayer->play(); mpPlayPauseBtn->setIconWithSates(mPausePixmap); } }

void MainWindow::onSpinBoxChanged(double v) { if (!mpPlayer) return; mpPlayer->setSpeed(v); }

void MainWindow::onPaused(bool p) { if (p) { qDebug("start pausing..."); mpPlayPauseBtn->setIconWithSates(mPlayPixmap); } else { qDebug("stop pausing..."); mpPlayPauseBtn->setIconWithSates(mPausePixmap); } }

void MainWindow::onStartPlay() { mpRenderer->setRegionOfInterest(QRectF()); mFile = mpPlayer->file(); //open from EventFilter's menu setWindowTitle(mTitle);

mpPlayPauseBtn->setIconWithSates(mPausePixmap);
mpTimeSlider->setMaximum(mpPlayer->duration());
mpTimeSlider->setValue(0);
qDebug(">>>>>>>>>>>>>>enable slider");
mpTimeSlider->setEnabled(true);
mpDuration->setText(QTime(0, 0, 0).addMSecs(mpPlayer->duration()).toString("HH:mm:ss"));
setVolume();
mShowControl = 0;
QTimer::singleShot(3000, this, SLOT(tryHideControlBar()));

mCursorTimer = startTimer(3000);

}

void MainWindow::onStopPlay() { if (mpPlayer->currentRepeat() < mpPlayer->repeat()) return; mpPlayPauseBtn->setIconWithSates(mPlayPixmap); mpTimeSlider->setValue(0); qDebug(">>>>>>>>>>>>>>disable slider"); mpTimeSlider->setDisabled(true); mpCurrent->setText("00:00:00"); mpDuration->setText("00:00:00"); tryShowControlBar();

//mRepeateMax = 0;
killTimer(mCursorTimer);
unsetCursor();

}

void MainWindow::seekToMSec(int msec) { mpPlayer->seek(qint64(msec)); }

void MainWindow::seek() { mpPlayer->seek((qint64)mpTimeSlider->value()); }

void MainWindow::showHideVolumeBar() { if (mpVolumeSlider->isHidden()) { mpVolumeSlider->show(); } else { mpVolumeSlider->hide(); } }

void MainWindow::setVolume() { AudioOutput _ao = mpPlayer ? mpPlayer->audio() : 0; qreal v = qreal(mpVolumeSlider->value())_kVolumeInterval; if (ao) { ao->setVolume(v); } mpVolumeSlider->setToolTip(QString::number(v)); mpVolumeBtn->setToolTip(QString::number(v)); }

void MainWindow::closeEvent(QCloseEvent *e) { Q_UNUSED(e); if (mpPlayer) mpPlayer->stop(); qApp->quit(); }

void MainWindow::resizeEvent(QResizeEvent _e) { Q_UNUSED(e);

if 0

if (e->size() == qApp->desktop()->size()) {
    mpControl->hide();
    mpTimeSlider->hide();
} else {
    if (mpControl->isHidden())
        mpControl->show();
    if (mpTimeSlider->isHidden())
        mpTimeSlider->show();
}

endif

/_
if (mpTitle)
    QLabelSetElideText(mpTitle, QFileInfo(mFile).fileName(), e->size().width());
_/

if SLIDER_ON_VO

int m = 4;
QWidget *w = static_cast<QWidget_>(mpTimeSlider->parent());
if (w) {
    mpTimeSlider->resize(w->width() - m_2, 44);
    qDebug("%d %d %d", m, w->height() - mpTimeSlider->height() - m, w->width() - m_2);
    mpTimeSlider->move(m, w->height() - mpTimeSlider->height() - m);
}

endif //SLIDER_ON_VO

}

void MainWindow::timerEvent(QTimerEvent *e) { if (e->timerId() == mCursorTimer) { if (mpControl->isVisible()) return; setCursor(Qt::BlankCursor); } }

void MainWindow::onPositionChange(qint64 pos) { mpTimeSlider->setValue(pos); mpCurrent->setText(QTime(0, 0, 0).addMSecs(pos).toString("HH:mm:ss")); }

void MainWindow::keyPressEvent(QKeyEvent *e) { mControlOn = e->key() == Qt::Key_Control; }

void MainWindow::keyReleaseEvent(QKeyEvent *e) { Q_UNUSED(e); mControlOn = false; }

void MainWindow::mousePressEvent(QMouseEvent *e) { if (!mControlOn) return; mGlobalMouse = e->globalPos(); }

void MainWindow::mouseMoveEvent(QMouseEvent *e) { unsetCursor(); if (e->pos().y() > height() - mpTimeSlider->height() - mpControl->height()) { if (mShowControl == 0) { mShowControl = 1; tryShowControlBar(); } } else { if (mShowControl == 1) { mShowControl = 0; QTimer::singleShot(3000, this, SLOT(tryHideControlBar())); } } if (mControlOn && e->button() == Qt::LeftButton) { if (!mpRenderer || !mpRenderer->widget()) return; QRectF roi = mpRenderer->realROI(); QPointF delta = e->pos() - mGlobalMouse; roi.moveLeft(-delta.x()); roi.moveTop(-delta.y()); mpRenderer->setRegionOfInterest(roi); } }

void MainWindow::wheelEvent(QWheelEvent _e) { if (!mpRenderer || !mpRenderer->widget()) { return; }

if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)

qreal deg = e->angleDelta().y()/8;

else

qreal deg = e->delta()/8;

endif //QT_VERSION

if WHEEL_SPEED

if (!mControlOn) {
    qreal speed = mpPlayer->speed();
    mpPlayer->setSpeed(qMax(0.01, speed + deg/15.0_0.02));
    return;
}

endif //WHEEL_SPEED

QPointF p = mpRenderer->widget()->mapFrom(this, e->pos());
QPointF fp = mpRenderer->mapToFrame(p);
if (fp.x() < 0)
    fp.setX(0);
if (fp.y() < 0)
    fp.setY(0);
if (fp.x() > mpRenderer->frameSize().width())
    fp.setX(mpRenderer->frameSize().width());
if (fp.y() > mpRenderer->frameSize().height())
    fp.setY(mpRenderer->frameSize().height());
QRectF viewport = QRectF(mpRenderer->mapToFrame(QPointF(0, 0)), mpRenderer->mapToFrame(QPointF(mpRenderer->rendererWidth(), mpRenderer->rendererHeight())));
//qDebug("vo: (%.1f, %.1f)=> frame: (%.1f, %.1f)", p.x(), p.y(), fp.x(), fp.y());

qreal zoom = 1.0 + deg*3.14/180.0;
//qDebug("deg: %d, %d zoom: %.2f", e->angleDelta().x(), e->angleDelta().y(), zoom);
QTransform m;
m.translate(fp.x(), fp.y());
m.scale(1.0/zoom, 1.0/zoom);
m.translate(-fp.x(), -fp.y());
QRectF r = m.mapRect(mpRenderer->realROI());
mpRenderer->setRegionOfInterest((r | m.mapRect(viewport))&QRectF(QPointF(0,0), mpRenderer->frameSize()));

}

void MainWindow::about() { QtAV::about(); }

void MainWindow::help() { QFile f(qApp->applicationDirPath() + "/help.html"); if (!f.exists()) { f.setFileName(":/help.html"); } if (!f.open(QIODevice::ReadOnly)) { qWarning("Failed to open help.html: %s", qPrintable(f.errorString())); return; } QTextStream ts(&f); QString text = ts.readAll(); QMessageBox::information(0, "Help", text); }

void MainWindow::openUrl() { QString url = QInputDialog::getText(0, tr("Open an url"), tr("Url")); if (url.isEmpty()) return; play(url); }

void MainWindow::playOnlineVideo(QAction *action) { mTitle = action->text(); play(action->data().toString()); }

void MainWindow::tryHideControlBar() { if (mShowControl > 0) { return; } if (mpControl->isHidden() && mpTimeSlider->isHidden()) return; mpControl->hide(); mpTimeSlider->hide(); }

void MainWindow::tryShowControlBar() { unsetCursor(); if (mpTimeSlider->isHidden()) mpTimeSlider->show(); if (mpControl->isHidden()) mpControl->show(); }

//void MainWindow::showInfo() //{ //// // why crash if on stack //// static StatisticsView *sv = new StatisticsView; //// if (mpPlayer) //// sv->setStatistics(mpPlayer->statistics()); //// sv->show(); //}

void MainWindow::onTimeSliderHover(int pos, int value) { QToolTip::showText(mapToGlobal(mpTimeSlider->pos() + QPoint(pos, 0)), QTime(0, 0, 0).addMSecs(value).toString("HH:mm:ss")); }

void MainWindow::onTimeSliderLeave() {

}

void MainWindow::handleError(const AVError &e) { QMessageBox::warning(0, "Player error", e.string()); }

void MainWindow::onMediaStatusChanged() { QString status; AVPlayer _player = qobjectcast<AVPlayer>(sender()); switch (player->mediaStatus()) { case NoMedia: status = "No media"; break; case InvalidMedia: status = "Invalid meida"; break; case BufferingMedia: status = "Buffering..."; break; case BufferedMedia: status = "Buffered"; break; case LoadingMedia: status = "Loading..."; break; case LoadedMedia: status = "Loaded"; break; default: break; } if (!status.isEmpty()) setWindowTitle(status); }

void MainWindow::donate() { //QDesktopServices::openUrl(QUrl("https://sourceforge.net/p/qtav/wiki/Donate%20%E6%8D%90%E8%B5%A0/")); QDesktopServices::openUrl(QUrl("http://wang-bin.github.io/QtAV/#donate")); }

cnhbljp commented 10 years ago

main.cpp /** QtAV Player Demo: this file is part of QtAV examples Copyright (C) 2012-2014 Wang Bin wbsecg1@gmail.com

include

include

include

include

include <QtCore/QDir>

include <QtCore/QLocale>

include <QtCore/QTranslator>

include

include <QtAV/AVPlayer.h>

include <QtAV/VideoRendererTypes.h>

include <QtAV/OSDFilter.h>

include

include "MainWindow.h"

using namespace QtAV;

int main(int argc, char *argv[]) { QApplication a(argc, argv);

QString file =  a.arguments().last() ;
file =  QString::fromUtf8("/mnt/sdcard2/test.avi");
qDebug()<< "qtplayer filename  " << file ;

VideoRenderer *renderer = 0;

   renderer = VideoRendererFactory::create(VideoRendererId_Widget);

MainWindow window;
window.show();
window.setWindowState(Qt::WindowFullScreen);
window.setRenderer(renderer);

renderer->widget()->resize(1024, 730);

window.play(file);

int ret = a.exec();
return ret;

}

wang-bin commented 10 years ago

你这样必须挂呀,AVPlayer::setIODevice(QIODevice*) 不会获得指针的所有权,出了作用域,也就是对应的大括号外面那个QBuffer就销毁了,自然就崩了

wang-bin commented 10 years ago

另外,贴代码要代吗前后留一行,每行前面再空4格贴上来就会正常显示代码