Rust
This content is for v0.4. Switch to the latest version for up-to-date documentation.
alpha2 크레이트는 Rust 프로그램을 위한 엔진입니다. 이 페이지를 읽고 나면 GPU 디바이스를 열고, 프로젝트를 로드하고, 디코딩한 프레임을 엔진에 넘겨주고, 프리뷰 프레임을 렌더링하고, export를 프레임 단위로 진행하며 인코더에 넘길 YUV 플레인을 얻을 수 있습니다.
엔진은 wgpu를 통해 GPU에서 타임라인 프로젝트를 렌더링하고 오디오를 믹싱합니다. 미디어 파일을 읽거나 쓰지는 않습니다. 미디어 디코딩과 출력 인코딩은 여러분의 프로그램이 맡고, 엔진은 넘겨받은 프레임으로 프로젝트가 보여 주는 화면을 렌더링합니다.
크레이트 추가하기
섹션 제목: “크레이트 추가하기”[dependencies]alpha2 = "0.5"pollster = "1" # GPU 디바이스를 기다리는 데 쓸 실행기(어떤 것이든 가능)이 크레이트는 엔진 크레이트들을 묶은 파사드입니다. 모듈은 곧 그 크레이트들로, audio, engine(재생과 export), gpu, media, model(프로젝트 문서), render, time이 있습니다. 대부분의 프로그램이 쓰는 타입은 최상위에도 있습니다. Project, EngineProject, GpuContext, Renderer, FrameProvider, SourceView, Player, Exporter, ExportStep, QualityProfile, RenderSize, MediaTime, FrameRate, TimeRange입니다.
첫 렌더링
섹션 제목: “첫 렌더링”프로젝트는 1280x720, 30fps로 이미지 하나를 1초 동안 보여 줍니다.
{ "schema_version": 1, "id": "tutorial-project", "name": "Tutorial project", "settings": { "width": 1280, "height": 720, "frame_rate": { "num": 30, "den": 1 }, "sample_rate": 48000 }, "sources": [ { "id": "image-1", "locator": "asset://image-1", "kind": "image" } ], "timeline": { "tracks": [ { "id": "video-1", "name": "Main video", "kind": "video", "clips": [ { "id": "clip-1", "source_id": "image-1", "timeline_range": { "start": 0, "end": 120000 }, "source_range": { "start": 0, "end": 120000 } } ], "transitions": [] } ] }}프로그램은 프리뷰 프레임 하나를 렌더링한 다음 export 프레임을 모두 렌더링합니다.
use alpha2::media::{SourceId, VideoCodec};use alpha2::render::UploadTexture;use alpha2::{ EngineProject, ExportStep, Exporter, FrameProvider, GpuContext, MediaTime, Player, Project, QualityProfile, RenderSize, Renderer, SourceView,};
/// 애플리케이션이 디코딩한 이미지. 소스 `image-1`로 렌더러에 넘겨줍니다.struct Image { id: SourceId, pixels: UploadTexture,}
impl FrameProvider for Image { fn texture_for(&self, source: &SourceId, _time: MediaTime) -> Option<SourceView<'_>> { (source == &self.id).then(|| SourceView::from(&self.pixels)) }}
fn main() -> Result<(), Box<dyn std::error::Error>> { let project = Project::from_json(&std::fs::read_to_string("project.json")?)?; let project = EngineProject::new(project)?; let gpu = pollster::block_on(GpuContext::request_high_performance())?; let mut renderer = Renderer::new(gpu); // 프로젝트에서 이름으로 쓸 수 있는 내장 이펙트와 트랜지션. renderer.register_builtins().expect("the built-in definitions register");
// 프로젝트 크기의 불투명한 중간 회색. 프리멀티플라이드 sRGB RGBA8입니다. let size = RenderSize::new(1280, 720)?; let gray = [128, 128, 128, 255].repeat(1280 * 720); let image = Image { id: SourceId::new("image-1")?, pixels: renderer.upload_rgba8(size, &gray)?, };
// 프리뷰 프레임 하나를 RGBA8로 읽어 옵니다. let mut player = Player::new(QualityProfile::preview())?; let frame = player.render_at(&project, &renderer, &image, MediaTime::ZERO)?; if let Some(texture) = frame.texture { let rgba = renderer.read_rgba8(texture)?; println!("preview: {} bytes", rgba.len()); }
// export의 모든 프레임을 인코더에 넘길 YUV 4:2:0 플레인으로 얻습니다. let mut exporter = Exporter::for_project(&project, QualityProfile::export(), VideoCodec::H264)?; loop { match exporter.render_next(&project, &renderer, &image)? { ExportStep::Rendered { readback, .. } => { let planes = readback.wait()?; // planes.y, planes.u, planes.v: 인코더에 넘깁니다. println!("frame: Y {} U {} V {} bytes", planes.y.len(), planes.u.len(), planes.v.len()); } ExportStep::Waiting { missing_sources, .. } => { return Err(format!("no frame for {missing_sources:?}").into()); } ExportStep::Complete => break, } } Ok(())}구성 요소가 맞물리는 방식
섹션 제목: “구성 요소가 맞물리는 방식”- 프로젝트를 로드합니다.
Project::from_json이 문서를 읽고 검사합니다.EngineProject::new는 평가용 인덱스를 갖춘 불변 스냅샷을 만들며, 플레이어와 exporter는 이 스냅샷을 렌더링합니다. 편집할 때마다 새 스냅샷을 로드하세요. - GPU를 엽니다.
GpuContext::request_high_performance()는 외장(고성능) 어댑터를 우선해 디바이스를 엽니다.WGPU_POWER_PREF를 설정하면 그 값이 우선합니다. - 렌더러를 만듭니다.
Renderer::new(gpu)로 만든 뒤register_builtins()로 내장 이펙트와 트랜지션을 등록합니다. 각각은 프레임이 처음 그것을 그리거나warmup이 그것을 지정할 때 컴파일됩니다. 렌더러에 등록되지 않은 이펙트를 그리는 프레임은 실패합니다. - 프레임을 넘겨줍니다. 디코딩한 미디어에 대해
FrameProvider를 구현합니다. - 렌더링합니다. 프리뷰 프레임에는
Player, export 프레임에는Exporter를 씁니다.
FrameProvider
섹션 제목: “FrameProvider”FrameProvider는 엔진이 요청한 정확한 소스 시간에 맞춰 각 소스의 프레임을 렌더러에 넘겨줍니다.
fn texture_for(&self, source: &SourceId, time: MediaTime) -> Option<SourceView<'_>>;time은 소스 자체의 타임라인 위의 틱(tick) 시간이며, 트림하거나 재생 속도를 바꾼 클립에서는 타임라인 시간과 다릅니다. 아직 그 프레임이 없으면 None을 반환하세요. Renderer::upload_rgba8(size, pixels)는 프리멀티플라이드 sRGB RGBA8 픽셀을 UploadTexture로 바꾸며, 이것을 SourceView::from으로 넘겨줄 수 있습니다.
Player
섹션 제목: “Player”Player::new(QualityProfile::preview())로 플레이어를 만듭니다. render_at(&project, &renderer, &frames, time)은 주어진 시간에 보이는 프레임을 렌더링해 texture와 함께 반환합니다. renderer.read_rgba8(texture)로 이를 읽어 옵니다.
소스 프레임이 없으면 결과에 해당 소스가 표시되고(missing_sources), 플레이어의 누락 프레임 정책에 따라 마지막 프레임을 다시 보여 주거나(repeated) 아무것도 보여 주지 않습니다. 마지막 프레임과 같은 프레임은 다시 렌더링하지 않습니다(unchanged).
Exporter
섹션 제목: “Exporter”Exporter::for_project(&project, QualityProfile::export(), codec)은 export 품질로 타임라인 전체에 걸쳐 프로젝트의 프레임 그리드를 따라갑니다. codec(VideoCodec::H264 등)은 그 코덱이 기대하는 방식으로 크로마 샘플 위치를 정합니다. render_next를 호출할 때마다 ExportStep 하나가 반환됩니다.
| 단계 | 의미 |
|---|---|
Rendered { readback, .. } |
프레임이 렌더링되었습니다. readback.wait()가 y, u, v 플레인을 돌려줍니다. |
Waiting { missing_sources, .. } |
이 소스들이 없어 프레임을 그리지 못했습니다. 소스를 공급하고 render_next를 다시 호출하면 같은 프레임부터 export가 이어집니다. |
Complete |
모든 프레임이 끝났습니다. |
effective_range()는 요청한 범위를 프레임 그리드에 맞게 넓힌 범위이며, 그 시작점이 출력의 0초가 됩니다. audio_window(sample_rate)는 이에 대응하는 오디오 샘플 구간을 돌려줍니다.
GPU가 열리지 않을 때
섹션 제목: “GPU가 열리지 않을 때”request_high_performance가 실패하면 GpuError::step()이 어댑터를 찾지 못했는지, 어댑터가 디바이스 생성을 거부했는지 알려 줍니다(GpuAcquisitionStep: Adapter 또는 Device). 플랫폼이 알려 준 이유는 source()로 볼 수 있습니다.
디코딩과 인코딩
섹션 제목: “디코딩과 인코딩”엔진은 미디어 파일을 읽거나 쓰지 않습니다. alpha2-native는 FFmpeg 8로 디코딩, 프로브, 인코딩을 하며, 빌드하는 환경에 설치된 FFmpeg에 링크합니다. alpha2-host는 이를 사용해 프로젝트를 export하는 완전한 프로그램입니다.
이펙트
섹션 제목: “이펙트”alpha2::render::builtin_catalog()는 내장 이펙트와 트랜지션을 파라미터와 함께 나열합니다(이펙트 레퍼런스에도 있습니다). 크로마 키(alpha2.effect.chroma-key)와 3D LUT(alpha2.effect.lut-3d, .cube 파일을 Renderer::register_lut3d로 등록)도 내장 이펙트이며, 프로젝트에서 다른 이펙트와 똑같이 적용합니다. 이펙트와 컴포지션을 참고하세요.
wgpu 상호 운용
섹션 제목: “wgpu 상호 운용”기본 API에는 wgpu 타입이 드러나지 않습니다. 자체 wgpu 디바이스나 텍스처를 엔진과 공유하는 프로그램이라면 wgpu-interop 기능을 켜서 wgpu 타입을 쓰는 항목을 추가합니다.
GpuContext::from_parts는 여러분의 디바이스를 그대로 사용합니다.SourceView::from_wgpu는wgpu::TextureView를 소스로 넘겨줍니다.- 렌더링된 프레임의
wgpu텍스처를 가져오거나, 하드웨어 인코더의 플레인에 직접 렌더링할 수 있습니다.
alpha2 = { version = "0.5", features = ["wgpu-interop"] }기능을 켜면 alpha2::gpu::wgpu가 엔진이 빌드된 wgpu 크레이트를 가리킵니다. wgpu 타입은 직접 추가한 wgpu 의존성 대신 이 경로로 쓰세요.
