WIP #35
@@ -108,7 +108,88 @@ def capture_frames(
|
||||
)
|
||||
ctx.params = params
|
||||
|
||||
# Add message overlay stage if enabled
|
||||
# Add stages based on source type (similar to pipeline_runner)
|
||||
from engine.display import DisplayRegistry
|
||||
from engine.pipeline.adapters import create_stage_from_display
|
||||
from engine.data_sources.sources import EmptyDataSource
|
||||
from engine.pipeline.adapters import DataSourceStage
|
||||
|
||||
# Add source stage
|
||||
if preset.source == "empty":
|
||||
source_stage = DataSourceStage(
|
||||
EmptyDataSource(width=preset.viewport_width, height=preset.viewport_height),
|
||||
name="empty",
|
||||
)
|
||||
else:
|
||||
# For headlines/poetry, use the actual source
|
||||
from engine.data_sources.sources import HeadlinesDataSource, PoetryDataSource
|
||||
|
||||
if preset.source == "headlines":
|
||||
source_stage = DataSourceStage(HeadlinesDataSource(), name="headlines")
|
||||
elif preset.source == "poetry":
|
||||
source_stage = DataSourceStage(PoetryDataSource(), name="poetry")
|
||||
else:
|
||||
# Fallback to empty
|
||||
source_stage = DataSourceStage(
|
||||
EmptyDataSource(
|
||||
width=preset.viewport_width, height=preset.viewport_height
|
||||
),
|
||||
name="empty",
|
||||
)
|
||||
pipeline.add_stage("source", source_stage)
|
||||
|
||||
# Add font stage for headlines/poetry (with viewport filter)
|
||||
if preset.source in ["headlines", "poetry"]:
|
||||
from engine.pipeline.adapters import FontStage, ViewportFilterStage
|
||||
|
||||
# Add viewport filter to prevent rendering all items
|
||||
pipeline.add_stage(
|
||||
"viewport_filter", ViewportFilterStage(name="viewport-filter")
|
||||
)
|
||||
# Add font stage for block character rendering
|
||||
pipeline.add_stage("font", FontStage(name="font"))
|
||||
else:
|
||||
# Fallback to simple conversion for empty/other sources
|
||||
from engine.pipeline.adapters import SourceItemsToBufferStage
|
||||
|
||||
pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))
|
||||
|
||||
# Add camera stage
|
||||
from engine.camera import Camera
|
||||
from engine.pipeline.adapters import CameraStage, CameraClockStage
|
||||
|
||||
# Create camera based on preset
|
||||
if preset.camera == "feed":
|
||||
camera = Camera.feed()
|
||||
elif preset.camera == "scroll":
|
||||
camera = Camera.scroll(speed=0.1)
|
||||
elif preset.camera == "horizontal":
|
||||
camera = Camera.horizontal(speed=0.1)
|
||||
else:
|
||||
camera = Camera.feed()
|
||||
|
||||
camera.set_canvas_size(preset.viewport_width, preset.viewport_height * 2)
|
||||
|
||||
# Add camera update (for animation)
|
||||
pipeline.add_stage("camera_update", CameraClockStage(camera, name="camera-clock"))
|
||||
# Add camera stage
|
||||
pipeline.add_stage("camera", CameraStage(camera, name=preset.camera))
|
||||
|
||||
# Add effects
|
||||
if preset.effects:
|
||||
from engine.effects.registry import EffectRegistry
|
||||
from engine.pipeline.adapters import create_stage_from_effect
|
||||
|
||||
effect_registry = EffectRegistry()
|
||||
for effect_name in preset.effects:
|
||||
effect = effect_registry.get(effect_name)
|
||||
if effect:
|
||||
pipeline.add_stage(
|
||||
f"effect_{effect_name}",
|
||||
create_stage_from_effect(effect, effect_name),
|
||||
)
|
||||
|
||||
# Add message overlay stage if enabled (BEFORE display)
|
||||
if getattr(preset, "enable_message_overlay", False):
|
||||
from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage
|
||||
|
||||
@@ -120,9 +201,21 @@ def capture_frames(
|
||||
"message_overlay", MessageOverlayStage(config=overlay_config)
|
||||
)
|
||||
|
||||
# Add null display stage (LAST)
|
||||
null_display = DisplayRegistry.create("null")
|
||||
if null_display:
|
||||
pipeline.add_stage("display", create_stage_from_display(null_display, "null"))
|
||||
|
||||
# Build pipeline
|
||||
pipeline.build()
|
||||
|
||||
# Enable recording on null display if available
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "start_recording"):
|
||||
backend.start_recording()
|
||||
|
||||
# Capture frames
|
||||
frames = []
|
||||
start_time = time.time()
|
||||
@@ -132,18 +225,36 @@ def capture_frames(
|
||||
stage_result = pipeline.execute()
|
||||
frame_time = time.time() - frame_start
|
||||
|
||||
# Extract buffer from result
|
||||
buffer = stage_result.data if stage_result.success else []
|
||||
# Get frames from display recording
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "get_recorded_data"):
|
||||
recorded_frames = backend.get_recorded_data()
|
||||
# Add render_time_ms to each frame
|
||||
for frame in recorded_frames:
|
||||
frame["render_time_ms"] = frame_time * 1000
|
||||
frames = recorded_frames
|
||||
|
||||
frames.append(
|
||||
{
|
||||
"frame_number": i,
|
||||
"buffer": buffer,
|
||||
"width": preset.viewport_width,
|
||||
"height": preset.viewport_height,
|
||||
"render_time_ms": frame_time * 1000,
|
||||
}
|
||||
)
|
||||
# Fallback: create empty frames if no recording
|
||||
if not frames:
|
||||
for i in range(frame_count):
|
||||
frames.append(
|
||||
{
|
||||
"frame_number": i,
|
||||
"buffer": [],
|
||||
"width": preset.viewport_width,
|
||||
"height": preset.viewport_height,
|
||||
"render_time_ms": frame_time * 1000,
|
||||
}
|
||||
)
|
||||
|
||||
# Stop recording on null display
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "stop_recording"):
|
||||
backend.stop_recording()
|
||||
|
||||
total_time = time.time() - start_time
|
||||
|
||||
|
||||
Reference in New Issue
Block a user