Add playblast: viewport capture to H.264 MP4 via libavcodec

- TimelinePanel: Playblast button opens modal with output dir (native
  folder picker via IFileOpenDialog), custom resolution (HD/FHD/4K
  presets), frame range, and movie export toggle
- UsdSceneRenderer: CaptureFrame() reads pixels from resolved MSAA FBO;
  Render() stores last dimensions for off-screen re-render at capture res
- MovieEncoder: streams RGBA frames directly into MP4 using libavcodec/
  libswscale (H.264, tries libx264 → nvenc → qsv → amf → openh264)
- Application: CapturePlayblastFrame() re-renders at capture resolution
  each frame; opens/writes/closes MovieEncoder inline with the render loop
- FileDialog: BrowseFolder() using Vista-style IFileOpenDialog (COM)
- CMake: FindFFmpeg.cmake locates prebuilt BtbN GPL shared package in
  third_party/ffmpeg; links and copies DLLs for main and test targets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 09:36:30 +08:00
parent c796c76561
commit 0538dd3243
12 changed files with 764 additions and 3 deletions
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <cstdint>
namespace UsdLayerManager {
/// Streams RGBA frames directly into an H.264/MP4 file using libavcodec.
/// Usage:
/// MovieEncoder enc;
/// enc.Open("out.mp4", 1920, 1080, 24.0);
/// for each frame: enc.WriteFrame(rgbaPixels); // top-to-bottom, 4 bytes/px
/// enc.Close();
class MovieEncoder {
public:
MovieEncoder();
~MovieEncoder();
// Non-copyable
MovieEncoder(const MovieEncoder&) = delete;
MovieEncoder& operator=(const MovieEncoder&) = delete;
bool Open(const char* outputPath, int width, int height, double fps);
bool WriteFrame(const uint8_t* rgba);
bool Close(); // flush + write trailer; resets state for reuse
bool IsOpen() const { return m_open; }
const std::string& GetLastError() const { return m_error; }
private:
bool DrainPackets();
void Reset();
struct Impl;
Impl* m_impl = nullptr;
bool m_open = false;
std::string m_error;
};
} // namespace UsdLayerManager