250 lines
8.9 KiB
C++
250 lines
8.9 KiB
C++
/******************************************************************************
|
|
QtAV: Media play library based on Qt and FFmpeg
|
|
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
|
|
|
* This file is part of QtAV
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
******************************************************************************/
|
|
|
|
#ifndef QAV_DEMUXER_H
|
|
#define QAV_DEMUXER_H
|
|
|
|
#include "AVError.h"
|
|
#include "Packet.h"
|
|
#include <QtCore/QVariant>
|
|
#include <QtCore/QObject>
|
|
#include <QtCore/QScopedPointer>
|
|
|
|
struct AVFormatContext;
|
|
struct AVCodecContext;
|
|
QT_BEGIN_NAMESPACE
|
|
class QIODevice;
|
|
QT_END_NAMESPACE
|
|
// TODO: force codec name. clean code
|
|
namespace FAV {
|
|
class AVError;
|
|
class MediaIO;
|
|
class Q_AV_EXPORT AVDemuxer : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
enum StreamType { //TODO: move to common MediaType
|
|
AudioStream,
|
|
VideoStream,
|
|
#if !(DO_NOT_USE_SUBTITLE)
|
|
SubtitleStream,
|
|
#endif
|
|
};
|
|
static const QStringList& supportedFormats();
|
|
static const QStringList& supportedExtensions();
|
|
/// Supported ffmpeg/libav input protocols(not complete). A static string list
|
|
static const QStringList& supportedProtocols();
|
|
|
|
AVDemuxer(QObject *parent = 0);
|
|
~AVDemuxer();
|
|
|
|
int playerID;
|
|
#if (REAR_SYNC_FRONT)
|
|
double rear_delay;
|
|
#endif
|
|
|
|
qint64 _lastDuration;
|
|
#if (FIXED_FPS_DURATION)
|
|
qint64 durationFixed;
|
|
qint32 frameCount;
|
|
#if (FORCE_BREAK_EOF)
|
|
qint64 endFilePosition; // 시간(DTS/PTS)으로 EOF 처리 불가능함 -> 파일 포지션으로 처리
|
|
#endif // FORCE_BREAK_EOF
|
|
#if (PREVENT_OVER_DURATION_RENDER)
|
|
qint64 originalDuraiont; // VideoThread 에 전달하여 원 재생시간 이상의 경우 DRAW 하지 않도록 변경
|
|
#endif // PREVENT_OVER_DURATION_RENDER
|
|
#endif //FIXED_FPS_DURATION
|
|
|
|
qint32 realVideoStreamCount; ///!< 실제(프레임이 존재하는) 비디오 스트림 개수
|
|
|
|
#if (PLAY_SYNC_FIX2)
|
|
qint64 frameDuration; // 각 프레임당 길이(SEEK 및 SLIDER BAR 계산시 durationFixed - frameDuration 으로 사용)
|
|
#endif // PLAY_SYNC_FIX2
|
|
|
|
MediaStatus mediaStatus() const;
|
|
bool atEnd() const;
|
|
QString fileName() const;
|
|
QIODevice* ioDevice() const;
|
|
/// not null for QIODevice, custom protocols
|
|
MediaIO* mediaIO() const;
|
|
/*!
|
|
* \brief setMedia
|
|
* \return whether the media source is changed
|
|
*/
|
|
bool setMedia(const QString& fileName);
|
|
bool setMedia(QIODevice* dev);
|
|
bool setMedia(MediaIO* in);
|
|
/*!
|
|
* \brief setFormat
|
|
* Force the input format. Useful if input stream is a raw video stream(fmt="rawvideo).
|
|
* formatForced() is reset if media changed. So you have to call setFormat() for every media
|
|
* you want to force the format.
|
|
* If AVFormatContext.format_whitelist contains only 1 format, then that format will be forced.
|
|
* For example, setOptions({"format_whitelist": "rawvideo"})
|
|
*/
|
|
void setFormat(const QString& fmt);
|
|
QString formatForced() const;
|
|
bool load();
|
|
bool unload();
|
|
bool isLoaded() const;
|
|
/*!
|
|
* \brief readFrame
|
|
* Read a packet from 1 of the streams. use packet() to get the result packet. packet() returns last valid packet.
|
|
* So do not use packet() if readFrame() failed.
|
|
* Call readFrame() and seek() in the same thread.
|
|
* \return true if no error. false if error occurs, eof reaches, interrupted by user or time out(getInterruptTimeout())
|
|
*/
|
|
bool readFrame(); // TODO: rename int readPacket(), return stream number
|
|
/*!
|
|
* \brief packet
|
|
* return the packet read by demuxer. packet is invalid if readFrame() returns false.
|
|
*/
|
|
Packet packet() const;
|
|
/*!
|
|
* \brief stream
|
|
* Current readFrame() readed stream index.
|
|
*/
|
|
int stream() const;
|
|
|
|
bool isSeekable() const; // TODO: change in unload?
|
|
void setSeekUnit(SeekUnit unit);
|
|
SeekUnit seekUnit() const;
|
|
void setSeekType(SeekType target);
|
|
SeekType seekType() const;
|
|
/*!
|
|
* \brief seek
|
|
* seek to a given position. Only support timestamp seek now.
|
|
* Experiment: if pos is out of range (>duration()), do nothing unless a seekable and variableSize MediaIO is used.
|
|
* \return false if fail
|
|
*/
|
|
bool seek(qint64 pos); //pos: ms
|
|
/*!
|
|
* \brief seek
|
|
* Percentage seek. duration() must be >0LL
|
|
* \param q [0, 1]
|
|
* TODO: what if duration() is not valid but size is known?
|
|
*/
|
|
bool seek(qreal q);
|
|
AVFormatContext* formatContext();
|
|
QString formatName() const;
|
|
QString formatLongName() const;
|
|
// TODO: rename startPosition()
|
|
qint64 startTime() const; //ms, AVFormatContext::start_time/1000
|
|
qint64 duration() const; //ms, AVFormatContext::duration/1000
|
|
qint64 startTimeUs() const; //us, AVFormatContext::start_time
|
|
qint64 durationUs() const; //us, AVFormatContext::duration
|
|
qint64 durationUSAudio() const; // Audio duration
|
|
//total bit rate
|
|
int bitRate() const; //AVFormatContext::bit_rate
|
|
qreal frameRate() const; //deprecated AVStream::avg_frame_rate
|
|
// if stream is -1, return the current video(or audio if no video) stream.
|
|
// TODO: audio/videoFrames?
|
|
qint64 frames(int stream = -1) const; //AVFormatContext::nb_frames
|
|
bool hasAttacedPicture() const;
|
|
/*!
|
|
* \brief setStreamIndex
|
|
* Set stream by index in stream list. call it after loaded.
|
|
* Stream/index will not change in next load() unless media source changed
|
|
* index < 0 is invalid
|
|
*/
|
|
bool setStreamIndex(StreamType st, int index);
|
|
// current open stream
|
|
int currentStream(StreamType st) const;
|
|
QList<int> streams(StreamType st) const;
|
|
// TODO: stream(StreamType), streams(StreamType)
|
|
// current open stream
|
|
int audioStream() const;
|
|
QList<int> audioStreams() const;
|
|
int videoStream() const;
|
|
QList<int> videoStreams() const;
|
|
int subtitleStream() const;
|
|
QList<int> subtitleStreams() const;
|
|
//codec. stream < 0: the stream going to play (or the stream set by setStreamIndex())
|
|
AVCodecContext* audioCodecContext(int stream = -1) const;
|
|
AVCodecContext* videoCodecContext(int stream = -1) const;
|
|
AVCodecContext* subtitleCodecContext(int stream = -1) const;
|
|
/**
|
|
* @brief getInterruptTimeout return the interrupt timeout
|
|
*/
|
|
qint64 getInterruptTimeout() const;
|
|
/**
|
|
* @brief setInterruptTimeout set the interrupt timeout
|
|
* @param timeout in ms
|
|
*/
|
|
void setInterruptTimeout(qint64 timeout);
|
|
bool isInterruptOnTimeout() const;
|
|
void setInterruptOnTimeout(bool value);
|
|
/**
|
|
* @brief getInterruptStatus return the interrupt status.
|
|
* \return -1: interrupted by user
|
|
* 0: not interrupted
|
|
* >0: timeout value of AVError::ErrorCode
|
|
*/
|
|
int getInterruptStatus() const;
|
|
/**
|
|
* @brief setInterruptStatus set the interrupt status
|
|
* @param interrupt <0: abort current operation like loading and reading packets.
|
|
* 0: no interrupt
|
|
*/
|
|
void setInterruptStatus(int interrupt);
|
|
/*!
|
|
* \brief setOptions
|
|
* libav's AVDictionary. we can ignore the flags used in av_dict_xxx because we can use hash api.
|
|
* empty value does nothing to current context if it is open, but will change AVDictionary options to null in next open.
|
|
* AVDictionary is used in avformat_open_input() and will not change unless user call setOptions()
|
|
* If an option is not found
|
|
*/
|
|
void setOptions(const QVariantHash &dict);
|
|
QVariantHash options() const;
|
|
Q_SIGNALS:
|
|
void unloaded();
|
|
void userInterrupted(); // NO direct connection because it's emit before interrupted happens
|
|
void loaded();
|
|
// emit when the first frame is read
|
|
void started();
|
|
void finished(); //end of file
|
|
void error(const FAV::AVError& e); //explictly use FAV::AVError in connection for Qt4 syntax
|
|
void mediaStatusChanged(FAV::MediaStatus status);
|
|
void seekableChanged();
|
|
#if (FIXED_FPS_DURATION)
|
|
void mediaEnded();
|
|
#endif
|
|
|
|
private:
|
|
void setMediaStatus(MediaStatus status);
|
|
// error code (errorCode) and message (msg) may be modified internally
|
|
void handleError(int averr, AVError::ErrorCode* errorCode, QString& msg);
|
|
|
|
class Private;
|
|
QScopedPointer<Private> d;
|
|
class InterruptHandler;
|
|
friend class InterruptHandler;
|
|
|
|
#if (FIXED_FPS_DURATION)
|
|
|
|
void _loadDuration();
|
|
#endif
|
|
|
|
};
|
|
|
|
} //namespace FAV
|
|
#endif // QAV_DEMUXER_H
|