/*========================================================================= Program: Visualization Toolkit Module: vtkFFMPEGVideoSource.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class vtkFFMPEGVideoSource * @brief Reader for ffmpeg supported formats * * Note this this class make use of multiple threads when decoding files. It has * a feed thread, a video drain thread, and an audio drain thread. The decoding * may use multiple threads as well as specified by DecodingThreads ivar. * * @sa * vtkVideoSource */ #ifndef vtkFFMPEGVideoSource_h #define vtkFFMPEGVideoSource_h #include "vtkIOFFMPEGModule.h" // For export macro #include "vtkMultiThreader.h" // for ivar #include "vtkNew.h" // for ivar #include "vtkVideoSource.h" #include // for std::condition_variable_any #include // for audio callback #include // for std::mutex class vtkFFMPEGVideoSourceInternal; class vtkFFMPEGVideoSource; // audio callback struct, outside the class so that we // can forward ref it struct vtkFFMPEGVideoSourceAudioCallbackData { int NumberOfSamples; int BytesPerSample; int NumberOfChannels; int SampleRate; int DataType; bool Packed; unsigned char** Data; vtkFFMPEGVideoSource* Caller; void* ClientData; }; // video callback struct, outside the class so that we // can forward ref it struct vtkFFMPEGVideoSourceVideoCallbackData { int Height; int LineSize[8]; unsigned char* Data[8]; // nullptr for empty planes vtkFFMPEGVideoSource* Caller; void* ClientData; }; class VTKIOFFMPEG_EXPORT vtkFFMPEGVideoSource : public vtkVideoSource { public: static vtkFFMPEGVideoSource* New(); vtkTypeMacro(vtkFFMPEGVideoSource, vtkVideoSource); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Standard VCR functionality: Record incoming video. */ void Record() override; /** * Standard VCR functionality: Play recorded video. */ void Play() override; /** * Standard VCR functionality: Stop recording or playing. */ void Stop() override; /** * Grab a single video frame. */ void Grab() override; ///@{ /** * Request a particular frame size (set the third value to 1). */ void SetFrameSize(int x, int y, int z) override; void SetFrameSize(int dim[3]) override { this->SetFrameSize(dim[0], dim[1], dim[2]); } ///@} /** * Request a particular frame rate (default 30 frames per second). */ void SetFrameRate(float rate) override; /** * Request a particular output format (default: VTK_RGB). */ void SetOutputFormat(int format) override; /** * Initialize the driver (this is called automatically when the * first grab is done). */ void Initialize() override; /** * Free the driver (this is called automatically inside the * destructor). */ void ReleaseSystemResources() override; ///@{ /** * Specify file name of the video */ vtkSetFilePathMacro(FileName); vtkGetFilePathMacro(FileName); ///@} /** * The internal function which actually does the grab. You will * definitely want to override this if you develop a vtkVideoSource * subclass. */ void InternalGrab() override; // is the video at the end of file? // Useful for while loops vtkGetMacro(EndOfFile, bool); // Is the video stream stereo 3d vtkGetMacro(Stereo3D, bool); // we do not use Invoke Observers here because this callback // will happen in a different thread that could conflict // with events from other threads. In this function you should // not block the thread (for example waiting for audio to play) // instead you should have enough buffering that you can consume // the provided data and return. Typically even 1 second of // buffer storage is enough to prevent blocking. typedef std::function AudioCallbackType; void SetAudioCallback(AudioCallbackType cb, void* clientData) { this->AudioCallback = cb; this->AudioCallbackClientData = clientData; } // we do not use Invoke Observers here because this callback // will happen in a different thread that could conflict // with events from other threads. In this function you should // not block the thread (for example waiting for video to play) // instead you should have enough buffering that you can consume // the provided data and return. typedef std::function VideoCallbackType; void SetVideoCallback(VideoCallbackType cb, void* clientData) { this->VideoCallback = cb; this->VideoCallbackClientData = clientData; } ///@{ /** * How many threads to use for the decoding codec * this will be in addition to the feed and drain threads. * the default value is 4. */ vtkSetMacro(DecodingThreads, int); vtkGetMacro(DecodingThreads, int); ///@} protected: vtkFFMPEGVideoSource(); ~vtkFFMPEGVideoSource() override; AudioCallbackType AudioCallback; void* AudioCallbackClientData; int DecodingThreads; static void* DrainAudioThread(vtkMultiThreader::ThreadInfo* data); void* DrainAudio(vtkMultiThreader::ThreadInfo* data); int DrainAudioThreadId; static void* DrainThread(vtkMultiThreader::ThreadInfo* data); void* Drain(vtkMultiThreader::ThreadInfo* data); int DrainThreadId; bool EndOfFile; std::condition_variable_any FeedCondition; std::mutex FeedMutex; std::condition_variable_any FeedAudioCondition; std::mutex FeedAudioMutex; static void* FeedThread(vtkMultiThreader::ThreadInfo* data); void* Feed(vtkMultiThreader::ThreadInfo* data); int FeedThreadId; char* FileName; vtkFFMPEGVideoSourceInternal* Internal; void ReadFrame(); bool Stereo3D; VideoCallbackType VideoCallback; void* VideoCallbackClientData; private: vtkFFMPEGVideoSource(const vtkFFMPEGVideoSource&) = delete; void operator=(const vtkFFMPEGVideoSource&) = delete; }; #endif