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();
214 
218  bool addSeekEndPacket(double seekTargetSecs);
219 
224  bool addEndOfFilePacket();
225 
229  bool addIOFinishedPacket();
230 
236  bool addPacket(AVPacket& packet);
237 
243  bool waitForFrameQueueEmpty(bool& exitIOThreadSignalled);
244 
248  void cancelWaitForFrameQueueEmpty();
249 
253  void resetWaitForFrameQueueEmpty();
254 
258  bool waitForEndOfFileProcessed();
259 
263  void cancelWaitForEndOfFileProcessed();
264 
268  void resetEndOfFileProcessed();
269 
273  void waitSeekStartPacketProcessed();
274 
279  int waitForReceiveFrame();
280 
284  void drainSeekFrameQueue();
285 
290  Frame popFrame();
291 
297  Frame tryPopFrame(double pts);
298 
302  Frame peekFrame();
303 
307  Frame popSeekFrame();
308 
312  void notifyStartIOThread();
313 
317  void notifyExitIOThread();
318 
322  AVCodec& getCodec() { return *mCodec; }
323 
327  AVCodecContext& getCodecContext() { return *mCodecContext; }
328 
329  private:
330  enum class EDecodeFrameResult
331  {
332  GotFrame,
333  EndOfFile,
334  Exit
335  };
336 
337  void decodeThread();
338  EDecodeFrameResult decodeFrame(AVFrame& frame, int& frameFirstPacketDTS);
339  AVPacket* popPacket();
340  void clearFrameQueue(std::queue<Frame>& frameQueue, bool emitCallback);
341 
342  private:
343  using FrameQueue = std::queue<Frame>;
344 
345  Video* mVideo;
346  AVCodec* mCodec = nullptr;
347  AVCodecContext* mCodecContext = nullptr;
348 
349  int mStream = -1;
350 
351  std::thread mDecodeThread;
352  OnClearFrameQueueFunction mOnClearFrameQueueFunction;
353  bool mExitDecodeThreadSignalled = false;
354 
355  FrameQueue mFrameQueue;
356  FrameQueue mSeekFrameQueue;
357  FrameQueue* mActiveFrameQueue = &mFrameQueue;
358  mutable std::mutex mFrameQueueMutex;
359  std::condition_variable mFrameDataAvailableCondition;
360  std::condition_variable mFrameQueueRoomAvailableCondition;
361  bool mCancelWaitFrameQueueEmpty = false;
362 
363  // Producer/consumer variables for packet queue:
364  std::queue<AVPacket*> mPacketQueue;
365  std::mutex mPacketQueueMutex;
366  std::condition_variable mPacketAvailableCondition;
367 
368  bool mFinishedProducingFrames = false;
369 
370  std::unique_ptr<AVPacket> mSeekStartPacket;
371  std::unique_ptr<AVPacket> mSeekEndPacket;
372  std::unique_ptr<AVPacket> mEndOfFilePacket;
373  std::unique_ptr<AVPacket> mIOFinishedPacket;
374  double mSeekTargetSecs;
375 
376  utility::AutoResetEvent mEndOfFileProcessedEvent;
377  utility::AutoResetEvent mSeekStartProcessedEvent;
378  utility::AutoResetEvent mReceiveFrameEvent;
379  int mReceiveFrameResult = 0;
380 
381  double mLastFramePTSSecs = 0.0;
382  int mFrameFirstPacketDTS = -INT_MAX;
383  };
384 
385 
399  class NAPAPI Video final
400  {
401  public:
405  Video(const std::string& path);
406 
407  // Destructor
408  virtual ~Video();
409 
413  Video(Video&) = delete;
414 
418  Video& operator=(const Video&) = delete;
419 
423  Video(Video&&) = delete;
424 
428  Video& operator=(Video&&) = delete;
429 
435  virtual bool init(utility::ErrorState& errorState);
436 
443  Frame update(double deltaTime);
444 
451  void play(double time = 0.0);
452 
457  bool isPlaying() const { return mPlaying; }
458 
464  void stop(bool blocking);
465 
470  void seek(double seconds);
471 
475  double getCurrentTime() const;
476 
480  double getDuration() const { return mDuration; }
481 
485  int getWidth() const { return mWidth; }
486 
490  int getHeight() const { return mHeight; }
491 
495  const std::string& getPath() const { return mPath; }
496 
500  bool hasAudio() const { return mAudioState.isValid(); }
501 
507  bool audioEnabled() const { return hasAudio() && mDecodeAudio; }
508 
509  bool mLoop = false;
510  float mSpeed = 1.0f;
511 
513 
514  private:
515 
516  friend class audio::VideoNode;
517  friend class AVState;
518 
519  enum class EProducePacketResult : uint8_t
520  {
521  GotAudioPacket = 1,
522  GotVideoPacket = 2,
523  GotUnknownPacket = 4,
524  GotPacket = GotAudioPacket | GotVideoPacket | GotUnknownPacket,
525  EndOfFile = 8,
526  Error = 16
527  };
528 
529  enum class IOThreadState
530  {
531  WaitingForEOF,
532  Playing,
533  SeekRequest,
534  SeekingStartFrame,
535  SeekingTargetFrame
536  };
537 
541  static bool sInitAVState(AVState& destState, const AVStream& stream, AVDictionary*& options, utility::ErrorState& errorState);
542 
546  void ioThread();
547 
551  void startIOThread();
552 
559  EProducePacketResult ProducePacket(AVState* targetState);
560 
565  void exitIOThread(bool join);
566 
571  bool getNextAudioFrame(const AudioFormat& targetAudioFormat);
572 
576  void clearPacketQueue();
577 
581  void clearFrameQueue();
582 
586  void onClearVideoFrameQueue();
587 
591  void onClearAudioFrameQueue();
592 
596  void setErrorOccurred(const std::string& errorMessage);
597 
601  void finishSeeking(AVState& seekState, bool shouldDrainFrameQueue);
602 
606  void setSeekTarget(AVState& seekState, double seekTargetSecs);
607 
612  void setIOThreadState(IOThreadState threadState);
613 
618  bool allocatePacket(uint64_t inPacketSize);
619 
624  void deallocatePacket(uint64_t inPacketSize);
625 
633  void decodeAudioStream(bool enabled);
634 
643  bool OnAudioCallback(uint8_t* dataBuffer, int sizeInBytes, const AudioFormat& targetAudioFormat);
644 
645  private:
646  std::string mPath;
647  static const double sClockMax;
648  AVFormatContext* mFormatContext = nullptr;
649  bool mPlaying = false;
650  int mWidth = 0;
651  int mHeight = 0;
652  float mDuration = 0.0f;
653  double mSystemClockSecs = sClockMax;
654  double mAudioDecodeClockSecs = -1;
655  double mAudioClockSecs = -1;
656 
657  std::string mErrorMessage;
658 
659  std::mutex mTotalPacketQueueSizeLock;
660  std::condition_variable mPacketQueueRoomAvailableCondition;
661  uint64_t mTotalPacketQueueSize = 0;
662 
663  AVState mVideoState;
664  AVState mAudioState;
665  AVState* mSeekState = nullptr;
666  std::thread mIOThread;
667 
668  bool mExitIOThreadSignalled = false;
669 
670  int64_t mSeekKeyframeTarget = -1;
671  int64_t mSeekTarget = -1;
672  double mSeekTargetSecs = 0.0f;
673 
674  SwrContext* mAudioResampleContext = nullptr;
675  Frame mCurrentAudioFrame;
676  std::vector<uint8_t> mAudioResampleBuffer;
677  uint8_t* mCurrentAudioBuffer = nullptr;
678  uint64_t mAudioFrameReadOffset = 0;
679  uint64_t mAudioFrameSize = 0;
680  bool mDecodeAudio = false;
681  IOThreadState mIOThreadState = IOThreadState::Playing;
682  };
683 }
nap::Video::getDuration
double getDuration() const
Definition: video.h:480
nap::AudioFormat::EChannelLayout::_4Point1
@ _4Point1
nap::Video::hasAudio
bool hasAudio() const
Definition: video.h:500
nap::AudioFormat::EChannelLayout::_2_1
@ _2_1
nap::AudioFormat::ESampleFormat::S16
@ S16
signed 16 bits
nap::AVState::getCodecContext
AVCodecContext & getCodecContext()
Definition: video.h:327
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:322
nap::AudioFormat::ESampleFormat::DBL
@ DBL
double
nap::AudioFormat::EChannelLayout::Quad
@ Quad
nap::Video
Definition: video.h:399
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:495
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:485
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:457
nap::AudioFormat::EChannelLayout::_6Point1
@ _6Point1
nap::AudioFormat::EChannelLayout::_6Point0_Front
@ _6Point0_Front
nap::Video::getHeight
int getHeight() const
Definition: video.h:490
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:507
nap::Video::mDestructedSignal
nap::Signal< Video & > mDestructedSignal
This signal will be emitted before the Video resource is destructed.
Definition: video.h:512
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)