#ifndef VIDEOPLAYER_VIDEOSTATE_H #define VIDEOPLAYER_VIDEOSTATE_H #include <cstdint> #include <atomic> #include <array> #include <vector> #include <memory> #include <string> #include <mutex> #include <condition_variable> #include <osg/ref_ptr> namespace osg { class Texture2D; } #if defined(_MSC_VER) #pragma warning (push) #pragma warning (disable : 4244) #endif extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> #include <libavutil/channel_layout.h> // From version 54.56 binkaudio encoding format changed from S16 to FLTP. See: // https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d // https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872 #include <libswresample/swresample.h> } #if defined(_MSC_VER) #pragma warning (pop) #endif #include "videodefs.hpp" #define VIDEO_PICTURE_QUEUE_SIZE 50 extern "C" { struct SwsContext; struct AVPacket; struct AVFormatContext; struct AVStream; struct AVFrame; } namespace Video { struct VideoState; class MovieAudioFactory; class MovieAudioDecoder; class VideoThread; class ParseThread; struct ExternalClock { ExternalClock(); uint64_t mTimeBase; uint64_t mPausedAt; bool mPaused; std::mutex mMutex; void setPaused(bool paused); uint64_t get(); void set(uint64_t time); }; class PacketList { public: AVPacket* pkt = nullptr; PacketList *next = nullptr; }; struct PacketQueue { PacketQueue() : first_pkt(nullptr), last_pkt(nullptr), flushing(false), nb_packets(0), size(0) { } ~PacketQueue() { clear(); } PacketList *first_pkt, *last_pkt; std::atomic<bool> flushing; std::atomic<int> nb_packets; std::atomic<int> size; std::mutex mutex; std::condition_variable cond; void put(AVPacket *pkt); int get(AVPacket *pkt, VideoState *is); void flush(); void clear(); }; struct VideoPicture { VideoPicture() : pts(0.0) { } struct AVFrameDeleter { void operator()(AVFrame* frame) const; }; // Sets frame dimensions. // Must be called before writing to `rgbaFrame`. // Return -1 on error. int set_dimensions(int w, int h); std::unique_ptr<AVFrame, AVFrameDeleter> rgbaFrame; double pts; }; struct VideoState { VideoState(); ~VideoState(); void setAudioFactory(MovieAudioFactory* factory); void init(std::unique_ptr<std::istream>&& inputstream, const std::string& name); void deinit(); void setPaused(bool isPaused); void seekTo(double time); double getDuration() const; int stream_open(int stream_index, AVFormatContext *pFormatCtx); bool update(); static void video_thread_loop(VideoState *is); static void decode_thread_loop(VideoState *is); void video_display(VideoPicture* vp); void video_refresh(); int queue_picture(const AVFrame &pFrame, double pts); double synchronize_video(const AVFrame &src_frame, double pts); double get_audio_clock(); double get_video_clock() const; double get_external_clock(); double get_master_clock(); static int istream_read(void *user_data, uint8_t *buf, int buf_size); static int istream_write(void *user_data, uint8_t *buf, int buf_size); static int64_t istream_seek(void *user_data, int64_t offset, int whence); osg::ref_ptr<osg::Texture2D> mTexture; MovieAudioFactory* mAudioFactory; std::unique_ptr<MovieAudioDecoder> mAudioDecoder; ExternalClock mExternalClock; std::unique_ptr<std::istream> stream; AVFormatContext* format_ctx; AVCodecContext* video_ctx; AVCodecContext* audio_ctx; int av_sync_type; AVStream** audio_st; PacketQueue audioq; uint8_t* mFlushPktData; AVStream** video_st; double frame_last_pts; double video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame PacketQueue videoq; SwsContext* sws_context; int sws_context_w, sws_context_h; std::array<VideoPicture, VIDEO_PICTURE_QUEUE_SIZE+1> pictq; // allocate one extra to make sure we do not overwrite the osg::Image currently set on the texture int pictq_size; unsigned long pictq_rindex, pictq_windex; std::mutex pictq_mutex; std::condition_variable pictq_cond; std::unique_ptr<ParseThread> parse_thread; std::unique_ptr<VideoThread> video_thread; std::atomic<bool> mSeekRequested; uint64_t mSeekPos; std::atomic<bool> mVideoEnded; std::atomic<bool> mPaused; std::atomic<bool> mQuit; }; } #endif