#pragma once #include #include #include #include #import #import class H264Encoder { public: H264Encoder(); ~H264Encoder(); // Initialize encoder // @param width: frame width // @param height: frame height // @param fps: target frame rate // @param bitrate: target bitrate in kbps (0 = auto) bool open(int width, int height, int fps, int bitrate = 0); // Close encoder and release resources void close(); // Check if encoder is open bool isOpen() const { return m_session != nullptr; } // Encode a frame // @param bgra: BGRA pixel data (bottom-up or top-down) // @param bpp: bits per pixel (32 for BGRA) // @param stride: bytes per row // @param width: frame width // @param height: frame height // @param outData: pointer to receive encoded data pointer // @param outSize: pointer to receive encoded data size // @param flipVertical: true if image is bottom-up (BMP format) // @return: encoded size, or 0 on failure int encode(const uint8_t* bgra, uint8_t bpp, uint32_t stride, uint32_t width, uint32_t height, uint8_t** outData, uint32_t* outSize, bool flipVertical = true); // Force next frame to be keyframe void forceKeyframe() { m_forceKeyframe = true; } // Get last error message const char* getLastError() const { return m_lastError; } private: // VideoToolbox compression callback static void compressionCallback(void* outputCallbackRefCon, void* sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer); // Process encoded sample buffer void processSampleBuffer(CMSampleBufferRef sampleBuffer); // Convert BGRA to I420 (YUV) void convertBGRAtoI420(const uint8_t* bgra, uint32_t stride, uint32_t width, uint32_t height, bool flipVertical); private: VTCompressionSessionRef m_session; int m_width; int m_height; int m_fps; int m_bitrate; // YUV buffers std::vector m_yPlane; std::vector m_uPlane; std::vector m_vPlane; // Output buffer std::vector m_outputBuffer; std::mutex m_outputMutex; // State std::atomic m_forceKeyframe; int64_t m_frameCount; char m_lastError[256]; };