/****************************************************************************** QtAV: Media play library based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * 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 #include #include 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 streams(StreamType st) const; // TODO: stream(StreamType), streams(StreamType) // current open stream int audioStream() const; QList audioStreams() const; int videoStream() const; QList videoStreams() const; int subtitleStream() const; QList 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 d; class InterruptHandler; friend class InterruptHandler; #if (FIXED_FPS_DURATION) void _loadDuration(); #endif }; } //namespace FAV #endif // QAV_DEMUXER_H