NAP
video.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 #include <condition_variable>
8 #include <queue>
9 #include <thread>
10 #include <mutex>
11 #include <climits>
12 #include <cassert>
13 #include <nap/resource.h>
14 #include <rtti/factory.h>
15 #include <utility/autoresetevent.h>
16 #include <nap/signalslot.h>
17 
18 struct AVPacket;
19 struct AVCodec;
20 struct AVCodecParserContext;
21 struct AVCodecContext;
22 struct AVFormatContext;
23 struct AVFrame;
24 struct SwrContext;
25 struct AVDictionary;
26 struct AVStream;
27 
28 namespace nap
29 {
30  // Forward Declares
31  class Video;
32  namespace audio
33  {
34  class VideoNode;
35  }
36 
41  {
42  public:
43  enum class EChannelLayout : uint8_t
44  {
45  Mono,
46  Stereo,
47  _2Point1,
48  _2_1,
49  Surround,
50  _3Point1,
51  _4Point0,
52  _4Point1,
53  _2_2,
54  Quad,
55  _5Point0,
56  _5Point1,
59  _6Point0,
61  Hexagonal,
62  _6Point1,
65  _7Point0,
67  _7Point1,
70  Octagonal,
73  };
74 
78  enum class ESampleFormat
79  {
80  U8,
81  S16,
82  S32,
83  FLT,
84  DBL,
85  S64
86  };
87 
91  AudioFormat(EChannelLayout channelLayout, ESampleFormat sampleFormat, int sampleRate);
92 
96  AudioFormat(int numChannels, ESampleFormat sampleFormat, int sampleRate);
97 
101  int getSampleRate() const { return mSampleRate; }
102 
106  int64_t getChannelLayout() const { return mChannelLayout; }
107 
111  int getSampleFormat() const { return mSampleFormat; }
112 
113  private:
114  int mSampleRate;
115  int64_t mChannelLayout;
116  int mSampleFormat;
117  };
118 
119 
123  struct NAPAPI Frame
124  {
125  bool isValid() const { return mFrame != nullptr; }
126  void free();
127 
128  AVFrame* mFrame = nullptr;
129  double mPTSSecs = 0.0;
130  int mFirstPacketDTS = 0;
131  };
132 
133 
138  class NAPAPI AVState final
139  {
140  public:
141  using OnClearFrameQueueFunction = std::function<void()>;
142 
147  AVState(Video& video);
148 
152  ~AVState();
153 
160  void init(int stream, AVCodec* codec, AVCodecContext* codecContext);
161 
165  void close();
166 
170  bool isValid() const { return mStream != -1; }
171 
175  int getStream() const { return mStream; }
176 
181  void startDecodeThread(const OnClearFrameQueueFunction& onClearFrameQueueFunction = OnClearFrameQueueFunction());
182 
187  void exitDecodeThread(bool join);
188 
193  bool isFinished() const;
194 
198  void clearFrameQueue();
199 
203  void clearPacketQueue();
204 
208  bool matchesStream(const AVPacket& packet) const;
209 
213  bool addSeekStartPacket(const bool& exitIOThreadSignalled);
214 
218  bool addSeekEndPacket(const bool& exitIOThreadSignalled, double seekTargetSecs);
219 
224  bool addEndOfFilePacket(const bool& exitIOThreadSignalled);
225 
229  bool addIOFinishedPacket(const bool& exitIOThreadSignalled);
230 
237  bool addPacket(AVPacket& packet, const bool& exitIOThreadSignalled);
238 
244  bool waitForFrameQueueEmpty(bool& exitIOThreadSignalled);
245 
249  void cancelWaitForFrameQueueEmpty();
250 
254  void resetWaitForFrameQueueEmpty();
255 
259  bool waitForEndOfFileProcessed();
260 
264  void cancelWaitForEndOfFileProcessed();
265 
269  void resetEndOfFileProcessed();
270 
274  void waitSeekStartPacketProcessed();
275 
280  int waitForReceiveFrame();
281 
285  void drainSeekFrameQueue();
286 
291  Frame popFrame();
292 
298  Frame tryPopFrame(double pts);
299 
303  Frame peekFrame();
304 
308  Frame popSeekFrame();
309 
313  void notifyStartIOThread();
314 
318  void notifyExitIOThread();
319 
323  AVCodec& getCodec() { return *mCodec; }
324 
328  AVCodecContext& getCodecContext() { return *mCodecContext; }
329 
330  private:
331  enum class EDecodeFrameResult
332  {
333  GotFrame,
334  EndOfFile,
335  Exit
336  };
337 
338  void decodeThread();
339  EDecodeFrameResult decodeFrame(AVFrame& frame, int& frameFirstPacketDTS);
340  AVPacket* popPacket();
341  void clearFrameQueue(std::queue<Frame>& frameQueue, bool emitCallback);
342 
343  private:
344  using FrameQueue = std::queue<Frame>;
345 
346  Video* mVideo;
347  AVCodec* mCodec = nullptr;
348  AVCodecContext* mCodecContext = nullptr;
349 
350  int mStream = -1;
351 
352  std::thread mDecodeThread;
353  OnClearFrameQueueFunction mOnClearFrameQueueFunction;
354  bool mExitDecodeThreadSignalled = false;
355 
356  FrameQueue mFrameQueue;
357  FrameQueue mSeekFrameQueue;
358  FrameQueue* mActiveFrameQueue = &mFrameQueue;
359  mutable std::mutex mFrameQueueMutex;
360  std::condition_variable mFrameDataAvailableCondition;
361  std::condition_variable mFrameQueueRoomAvailableCondition;
362  bool mCancelWaitFrameQueueEmpty = false;
363 
364  // Producer/consumer variables for packet queue:
365  std::queue<AVPacket*> mPacketQueue;
366  std::mutex mPacketQueueMutex;
367  std::condition_variable mPacketAvailableCondition;
368 
369  bool mFinishedProducingFrames = false;
370 
371  std::unique_ptr<AVPacket> mSeekStartPacket;
372  std::unique_ptr<AVPacket> mSeekEndPacket;
373  std::unique_ptr<AVPacket> mEndOfFilePacket;
374  std::unique_ptr<AVPacket> mIOFinishedPacket;
375  double mSeekTargetSecs;
376 
377  utility::AutoResetEvent mEndOfFileProcessedEvent;
378  utility::AutoResetEvent mSeekStartProcessedEvent;
379  utility::AutoResetEvent mReceiveFrameEvent;
380  int mReceiveFrameResult = 0;
381 
382  double mLastFramePTSSecs = 0.0;
383  int mFrameFirstPacketDTS = -INT_MAX;
384  };
385 
386 
400  class NAPAPI Video final
401  {
402  public:
406  Video(const std::string& path);
407 
408  // Destructor
409  virtual ~Video();
410 
414  Video(Video&) = delete;
415 
419  Video& operator=(const Video&) = delete;
420 
424  Video(Video&&) = delete;
425 
429  Video& operator=(Video&&) = delete;
430 
436  virtual bool init(utility::ErrorState& errorState);
437 
444  Frame update(double deltaTime);
445 
452  void play(double time = 0.0);
453 
458  bool isPlaying() const { return mPlaying; }
459 
465  void stop(bool blocking);
466 
471  void seek(double seconds);
472 
476  double getCurrentTime() const;
477 
481  double getDuration() const { return mDuration; }
482 
486  int getWidth() const { return mWidth; }
487 
491  int getHeight() const { return mHeight; }
492 
496  const std::string& getPath() const { return mPath; }
497 
501  bool hasAudio() const { return mAudioState.isValid(); }
502 
508  bool audioEnabled() const { return hasAudio() && mDecodeAudio; }
509 
510  bool mLoop = false;
511  float mSpeed = 1.0f;
512 
514 
515  private:
516 
517  friend class audio::VideoNode;
518  friend class AVState;
519 
520  enum class EProducePacketResult : uint8_t
521  {
522  GotAudioPacket = 1,
523  GotVideoPacket = 2,
524  GotUnknownPacket = 4,
525  GotPacket = GotAudioPacket | GotVideoPacket | GotUnknownPacket,
526  EndOfFile = 8,
527  Error = 16
528  };
529 
530  enum class IOThreadState
531  {
532  WaitingForEOF,
533  Playing,
534  SeekRequest,
535  SeekingStartFrame,
536  SeekingTargetFrame
537  };
538 
542  static bool sInitAVState(AVState& destState, const AVStream& stream, AVDictionary*& options, utility::ErrorState& errorState);
543 
547  void ioThread();
548 
552  void startIOThread();
553 
560  EProducePacketResult ProducePacket(AVState* targetState);
561 
566  void exitIOThread(bool join);
567 
572  bool getNextAudioFrame(const AudioFormat& targetAudioFormat);
573 
577  void clearPacketQueue();
578 
582  void clearFrameQueue();
583 
587  void onClearVideoFrameQueue();
588 
592  void onClearAudioFrameQueue();
593 
597  void setErrorOccurred(const std::string& errorMessage);
598 
602  void finishSeeking(AVState& seekState, bool shouldDrainFrameQueue);
603 
607  void setSeekTarget(AVState& seekState, double seekTargetSecs);
608 
613  void setIOThreadState(IOThreadState threadState);
614 
619  bool allocatePacket(uint64_t inPacketSize);
620 
625  void deallocatePacket(uint64_t inPacketSize);
626 
634  void decodeAudioStream(bool enabled);
635 
644  bool OnAudioCallback(uint8_t* dataBuffer, int sizeInBytes, const AudioFormat& targetAudioFormat);
645 
646  private:
647  std::string mPath;
648  static const double sClockMax;
649  AVFormatContext* mFormatContext = nullptr;
650  bool mPlaying = false;
651  int mWidth = 0;
652  int mHeight = 0;
653  float mDuration = 0.0f;
654  double mSystemClockSecs = sClockMax;
655  double mAudioDecodeClockSecs = -1;
656  double mAudioClockSecs = -1;
657 
658  std::string mErrorMessage;
659 
660  std::mutex mTotalPacketQueueSizeLock;
661  std::condition_variable mPacketQueueRoomAvailableCondition;
662  uint64_t mTotalPacketQueueSize = 0;
663 
664  AVState mVideoState;
665  AVState mAudioState;
666  AVState* mSeekState = nullptr;
667  std::thread mIOThread;
668 
669  bool mExitIOThreadSignalled = false;
670 
671  int64_t mSeekKeyframeTarget = -1;
672  int64_t mSeekTarget = -1;
673  double mSeekTargetSecs = 0.0f;
674 
675  SwrContext* mAudioResampleContext = nullptr;
676  Frame mCurrentAudioFrame;
677  std::vector<uint8_t> mAudioResampleBuffer;
678  uint8_t* mCurrentAudioBuffer = nullptr;
679  uint64_t mAudioFrameReadOffset = 0;
680  uint64_t mAudioFrameSize = 0;
681  bool mDecodeAudio = false;
682  IOThreadState mIOThreadState = IOThreadState::Playing;
683  };
684 }
nap::Video::getDuration
double getDuration() const
Definition: video.h:481
nap::AudioFormat::EChannelLayout::_4Point1
@ _4Point1
nap::Video::hasAudio
bool hasAudio() const
Definition: video.h:501
nap::AudioFormat::EChannelLayout::_2_1
@ _2_1
nap::AudioFormat::ESampleFormat::S16
@ S16
signed 16 bits
nap::AVState::getCodecContext
AVCodecContext & getCodecContext()
Definition: video.h:328
nap::AudioFormat::EChannelLayout::Stereo_Downmix
@ Stereo_Downmix
nap::AudioFormat::EChannelLayout::_5Point1
@ _5Point1
nap::getCurrentTime
NAPAPI SystemTimeStamp getCurrentTime()
nap::AVState::getStream
int getStream() const
Definition: video.h:175
nap::AudioFormat::EChannelLayout::Hexadecagonal
@ Hexadecagonal
nap::AudioFormat::EChannelLayout::_5Point0
@ _5Point0
nap::AudioFormat::ESampleFormat
ESampleFormat
Definition: video.h:78
nap::EWebSocketLogLevel::Error
@ Error
Recoverable error. Solution might be closing the connection with an appropiate error code.
nap::AVState::getCodec
AVCodec & getCodec()
Definition: video.h:323
nap::AudioFormat::ESampleFormat::DBL
@ DBL
double
nap::AudioFormat::EChannelLayout::Quad
@ Quad
nap::Video
Definition: video.h:400
nap::AudioFormat::getChannelLayout
int64_t getChannelLayout() const
Definition: video.h:106
nap::AVState::OnClearFrameQueueFunction
std::function< void()> OnClearFrameQueueFunction
Definition: video.h:141
nap::utility::ErrorState
Definition: errorstate.h:19
nap::AudioFormat
Definition: video.h:40
nap::Video::getPath
const std::string & getPath() const
Definition: video.h:496
nap::Frame
Definition: video.h:123
nap::AudioFormat::getSampleRate
int getSampleRate() const
Definition: video.h:101
nap::AudioFormat::EChannelLayout::Mono
@ Mono
nap::AudioFormat::ESampleFormat::S64
@ S64
signed 64 bits
nap::AudioFormat::EChannelLayout::_5Point0_Back
@ _5Point0_Back
nap::AudioFormat::EChannelLayout::_6Point1_Back
@ _6Point1_Back
nap::Video::getWidth
int getWidth() const
Definition: video.h:486
nap::audio::VideoNode
Definition: videoaudio.h:26
nap::AudioFormat::EChannelLayout::_5Point1_Back
@ _5Point1_Back
nap::AudioFormat::EChannelLayout::_2_2
@ _2_2
nap::AudioFormat::EChannelLayout::_7Point1_Wide
@ _7Point1_Wide
nap::AVState
Definition: video.h:138
nap::AudioFormat::EChannelLayout::_6Point0
@ _6Point0
nap::AudioFormat::EChannelLayout::Octagonal
@ Octagonal
nap::Signal
Definition: signalslot.h:28
nap::Video::isPlaying
bool isPlaying() const
Definition: video.h:458
nap::AudioFormat::EChannelLayout::_6Point1
@ _6Point1
nap::AudioFormat::EChannelLayout::_6Point0_Front
@ _6Point0_Front
nap::Video::getHeight
int getHeight() const
Definition: video.h:491
nap::rtti::EPropertyFileType::Video
@ Video
Points to a video file, must be used with EPropertyMetaData::FileLink.
nap::AudioFormat::EChannelLayout::Surround
@ Surround
nap::AudioFormat::EChannelLayout::_7Point1
@ _7Point1
nap::AudioFormat::EChannelLayout::_4Point0
@ _4Point0
nap::Frame::isValid
bool isValid() const
Definition: video.h:125
nap::Video::audioEnabled
bool audioEnabled() const
Definition: video.h:508
nap::Video::mDestructedSignal
nap::Signal< Video & > mDestructedSignal
This signal will be emitted before the Video resource is destructed.
Definition: video.h:513
nap
Definition: templateapp.h:17
nap::AudioFormat::ESampleFormat::FLT
@ FLT
float
nap::AudioFormat::EChannelLayout::_7Point1_Wide_Back
@ _7Point1_Wide_Back
nap::AudioFormat::getSampleFormat
int getSampleFormat() const
Definition: video.h:111
nap::AudioFormat::EChannelLayout::_7Point0_Front
@ _7Point0_Front
nap::AVState::isValid
bool isValid() const
Definition: video.h:170
nap::AudioFormat::ESampleFormat::S32
@ S32
signed 32 bits
nap::AudioFormat::EChannelLayout
EChannelLayout
Definition: video.h:43
nap::AudioFormat::EChannelLayout::_3Point1
@ _3Point1
nap::AudioFormat::ESampleFormat::U8
@ U8
unsigned 8 bits
nap::AudioFormat::EChannelLayout::Hexagonal
@ Hexagonal
nap::AudioFormat::EChannelLayout::_7Point0
@ _7Point0
nap::AudioFormat::EChannelLayout::_6Point1_Front
@ _6Point1_Front
nap::AudioFormat::EChannelLayout::Stereo
@ Stereo
nap::AudioFormat::EChannelLayout::_2Point1
@ _2Point1
nap::AudioFormat::AudioFormat
AudioFormat(EChannelLayout channelLayout, ESampleFormat sampleFormat, int sampleRate)