File VideoFileInput.h
File List > cpp > input > VideoFileInput.h
Go to the documentation of this file
/*
* SPDX-FileCopyrightText: 2026 Stagelab Coop SCCL
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileContributor: Ion Reguera <ion@stagelab.coop>
*
* This file is part of cuems-videocomposer.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef VIDEOCOMPOSER_VIDEOFILEINPUT_H
#define VIDEOCOMPOSER_VIDEOFILEINPUT_H
#include "InputSource.h"
#include "HardwareDecoder.h"
#include "AsyncDecodeQueue.h"
#include "../video/GPUTextureFrameBuffer.h"
#include <cuems_mediadecoder/MediaFileReader.h>
#include <cuems_mediadecoder/VideoDecoder.h>
#include <string>
#include <memory>
#include <cstdint>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <deque>
#include <sys/stat.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/hwcontext.h>
}
namespace videocomposer {
// Forward declarations
#ifdef HAVE_VAAPI_INTEROP
class VaapiInterop;
class DisplayBackend;
#endif
class VideoFileInput : public InputSource {
public:
VideoFileInput();
virtual ~VideoFileInput();
enum class HardwareDecodePreference {
AUTO,
SOFTWARE_ONLY,
VAAPI,
CUDA
};
// InputSource interface
bool open(const std::string& source) override;
void close() override;
bool isReady() const override;
bool readFrame(int64_t frameNumber, FrameBuffer& buffer) override;
bool seek(int64_t frameNumber) override;
void resetSeekState() override;
FrameInfo getFrameInfo() const override;
int64_t getCurrentFrame() const override;
CodecType detectCodec() const override;
bool supportsDirectGPUTexture() const override;
DecodeBackend getOptimalBackend() const override;
// Hardware decoding support
bool readFrameToTexture(int64_t frameNumber, GPUTextureFrameBuffer& textureBuffer);
// Additional methods specific to video files
void setIgnoreStartOffset(bool ignore) { ignoreStartOffset_ = ignore; }
bool getIgnoreStartOffset() const { return ignoreStartOffset_; }
void setNoIndex(bool noIndex) { noIndex_ = noIndex; }
bool getNoIndex() const { return noIndex_; }
// Index path helpers (used by cuems-videoindexer CLI)
static std::string getIndexPath(const std::string& videoPath);
static bool isCacheValid(const std::string& videoPath);
void setHardwareDecodePreference(HardwareDecodePreference preference) { hwPreference_ = preference; }
void setLoopMode(bool loop, int64_t totalFrames);
#ifdef HAVE_VAAPI_INTEROP
void setDisplayBackend(DisplayBackend* displayBackend);
bool hasVaapiZeroCopy() const;
#endif
void prewarmGPUPipeline();
private:
struct FrameIndex {
int64_t pkt_pts;
int64_t pkt_pos;
int64_t frame_pts;
int64_t frame_pos;
int64_t timestamp;
int64_t seekpts;
int64_t seekpos;
uint8_t key;
};
bool initializeFFmpeg();
bool openCodec();
bool openHardwareCodec();
bool indexFrames();
bool isIntraFrameCodec() const; // Check if codec is intra-frame only (all keyframes)
void setupDirectSeekMode(); // Setup direct seek mode for intra-frame codecs
bool seekToFrame(int64_t frameNumber);
bool seekByTimestamp(int64_t frameNumber);
int64_t parsePTSFromFrame(AVFrame* frame);
bool transferHardwareFrameToGPU(AVFrame* hwFrame, GPUTextureFrameBuffer& textureBuffer, bool skipSync = false);
void cleanup();
// Index caching
bool loadCachedIndex();
void saveCachedIndex() const;
// Media decoder module
cuems_mediadecoder::MediaFileReader mediaReader_;
cuems_mediadecoder::VideoDecoder videoDecoder_;
// FFmpeg objects (kept for compatibility and advanced operations)
AVFormatContext* formatCtx_; // Access via mediaReader_.getFormatContext()
AVCodecContext* codecCtx_; // Access via videoDecoder_.getCodecContext()
AVFrame* frame_;
AVFrame* frameFMT_;
SwsContext* swsCtx_;
int swsCtxWidth_;
int swsCtxHeight_;
AVPixelFormat swsCtxFormat_; // Track source format for sws context recreation
int videoStream_;
// Hardware decoding
AVBufferRef* hwDeviceCtx_; // Hardware device context
AVFrame* hwFrame_; // Hardware frame (for hardware decoding)
HardwareDecoder::Type hwDecoderType_; // Type of hardware decoder in use
bool useHardwareDecoding_; // Whether hardware decoding is enabled
bool codecCtxAllocated_; // Whether codecCtx_ was allocated separately (hardware) or is part of stream (software)
HardwareDecodePreference hwPreference_;
#ifdef HAVE_VAAPI_INTEROP
std::unique_ptr<VaapiInterop> vaapiInterop_; // VAAPI zero-copy interop (owned per-instance)
DisplayBackend* displayBackend_; // DisplayBackend for initializing interop (not owned)
#endif
// Frame indexing
FrameIndex* frameIndex_;
int64_t frameCount_;
int64_t lastDecodedPTS_;
int64_t lastDecodedFrameNo_;
bool scanComplete_;
bool byteSeek_;
bool noIndex_;
// Video properties
FrameInfo frameInfo_;
std::string currentFile_;
bool ignoreStartOffset_;
int64_t currentFrame_;
// Internal state
bool ready_;
AVRational frameRateQ_;
// Async frame pre-buffering (like mpv's decode-ahead)
struct CachedFrame {
int64_t frameNumber;
FrameBuffer buffer;
bool valid;
};
static constexpr size_t FRAME_CACHE_SIZE = 4; // Pre-buffer up to 4 frames
std::deque<CachedFrame> frameCache_;
std::mutex cacheMutex_;
// Async decode thread
std::unique_ptr<std::thread> decodeThread_;
std::condition_variable decodeCond_;
std::atomic<bool> decodeThreadRunning_{false};
std::atomic<int64_t> decodeTargetFrame_{-1};
std::atomic<bool> decodeThreadStop_{false};
void decodeThreadFunc();
bool decodeFrameInternal(int64_t frameNumber, FrameBuffer& buffer);
void startAsyncDecode(int64_t startFrame);
void stopAsyncDecode();
CachedFrame* findCachedFrame(int64_t frameNumber);
// Hardware decode frame tracking (per-instance, NOT static)
int64_t lastDecodedHWFrame_;
// NEW: Async decode queue for smooth hardware decoding
// This provides mpv-style pre-buffering to decouple decode latency from display timing
std::unique_ptr<AsyncDecodeQueue> asyncDecodeQueue_;
bool useAsyncDecode_; // Whether to use async decode (enabled for hardware decode)
};
} // namespace videocomposer
#endif // VIDEOCOMPOSER_VIDEOFILEINPUT_H