11#[ cfg( feature = "screenrecording" ) ]
22use core:: time:: Duration ;
33use std:: time:: { SystemTime , UNIX_EPOCH } ;
4- #[ cfg( feature = "screenrecording" ) ]
5- use std:: { fs:: File , io:: Write , sync:: mpsc:: channel} ;
64
75use bevy_app:: { App , Plugin , Update } ;
86use bevy_ecs:: prelude:: * ;
9- #[ cfg( feature = "screenrecording" ) ]
10- use bevy_image:: Image ;
117use bevy_input:: { common_conditions:: input_just_pressed, keyboard:: KeyCode } ;
12- #[ cfg( feature = "screenrecording" ) ]
13- use bevy_render:: view:: screenshot:: ScreenshotCaptured ;
148use bevy_render:: view:: screenshot:: { save_to_disk, Screenshot } ;
15- #[ cfg( feature = "screenrecording" ) ]
16- use bevy_time:: Time ;
179use bevy_window:: { PrimaryWindow , Window } ;
18- #[ cfg( feature = "screenrecording" ) ]
19- use tracing:: info;
20- #[ cfg( feature = "screenrecording" ) ]
21- use x264:: { Colorspace , Encoder , Setup } ;
22- #[ cfg( feature = "screenrecording" ) ]
10+ #[ cfg( all( not( target_os = "windows" ) , feature = "screenrecording" ) ) ]
2311pub use x264:: { Preset , Tune } ;
2412
2513/// File format the screenshot will be saved in
@@ -82,6 +70,50 @@ impl Plugin for EasyScreenshotPlugin {
8270 }
8371}
8472
73+ /// Placeholder
74+ #[ cfg( all( target_os = "windows" , feature = "screenrecording" ) ) ]
75+ pub enum Preset {
76+ /// Placeholder
77+ Ultrafast ,
78+ /// Placeholder
79+ Superfast ,
80+ /// Placeholder
81+ Veryfast ,
82+ /// Placeholder
83+ Faster ,
84+ /// Placeholder
85+ Fast ,
86+ /// Placeholder
87+ Medium ,
88+ /// Placeholder
89+ Slow ,
90+ /// Placeholder
91+ Slower ,
92+ /// Placeholder
93+ Veryslow ,
94+ /// Placeholder
95+ Placebo ,
96+ }
97+
98+ /// Placeholder
99+ #[ cfg( all( target_os = "windows" , feature = "screenrecording" ) ) ]
100+ pub enum Tune {
101+ /// Placeholder
102+ None ,
103+ /// Placeholder
104+ Film ,
105+ /// Placeholder
106+ Animation ,
107+ /// Placeholder
108+ Grain ,
109+ /// Placeholder
110+ StillImage ,
111+ /// Placeholder
112+ Psnr ,
113+ /// Placeholder
114+ Ssim ,
115+ }
116+
85117#[ cfg( feature = "screenrecording" ) ]
86118/// Add this plugin to your app to enable easy screen recording.
87119pub struct EasyScreenRecordPlugin {
@@ -107,13 +139,6 @@ impl Default for EasyScreenRecordPlugin {
107139 }
108140}
109141
110- #[ cfg( feature = "screenrecording" ) ]
111- enum RecordCommand {
112- Start ( String , Preset , Tune ) ,
113- Stop ,
114- Frame ( Image ) ,
115- }
116-
117142#[ cfg( feature = "screenrecording" ) ]
118143/// Controls screen recording
119144#[ derive( Message ) ]
@@ -126,90 +151,118 @@ pub enum RecordScreen {
126151
127152#[ cfg( feature = "screenrecording" ) ]
128153impl Plugin for EasyScreenRecordPlugin {
154+ #[ cfg_attr(
155+ target_os = "windows" ,
156+ expect( unused_variables, reason = "not working on windows" )
157+ ) ]
129158 fn build ( & self , app : & mut App ) {
130- let ( tx, rx) = channel :: < RecordCommand > ( ) ;
159+ #[ cfg( target_os = "windows" ) ]
160+ {
161+ tracing:: warn!( "Screen recording is not currently supported on Windows: see https://github.com/bevyengine/bevy/issues/22132" ) ;
162+ }
163+ #[ cfg( not( target_os = "windows" ) ) ]
164+ {
165+ use bevy_image:: Image ;
166+ use bevy_render:: view:: screenshot:: ScreenshotCaptured ;
167+ use bevy_time:: Time ;
168+ use std:: { fs:: File , io:: Write , sync:: mpsc:: channel} ;
169+ use tracing:: info;
170+ use x264:: { Colorspace , Encoder , Setup } ;
131171
132- let frame_time = self . frame_time ;
172+ enum RecordCommand {
173+ Start ( String , Preset , Tune ) ,
174+ Stop ,
175+ Frame ( Image ) ,
176+ }
133177
134- std:: thread:: spawn ( move || {
135- let mut encoder: Option < Encoder > = None ;
136- let mut setup = None ;
137- let mut file: Option < File > = None ;
138- let mut frame = 0 ;
139- loop {
140- let Ok ( next) = rx. recv ( ) else {
141- break ;
142- } ;
143- match next {
144- RecordCommand :: Start ( name, preset, tune) => {
145- info ! ( "starting recording at {}" , name) ;
146- file = Some ( File :: create ( name) . unwrap ( ) ) ;
147- setup = Some ( Setup :: preset ( preset, tune, false , true ) . high ( ) ) ;
148- }
149- RecordCommand :: Stop => {
150- info ! ( "stopping recording" ) ;
151- if let Some ( encoder) = encoder. take ( ) {
152- let mut flush = encoder. flush ( ) ;
153- let mut file = file. take ( ) . unwrap ( ) ;
154- while let Some ( result) = flush. next ( ) {
155- let ( data, _) = result. unwrap ( ) ;
156- file. write_all ( data. entirety ( ) ) . unwrap ( ) ;
157- }
178+ let ( tx, rx) = channel :: < RecordCommand > ( ) ;
179+
180+ let frame_time = self . frame_time ;
181+
182+ std:: thread:: spawn ( move || {
183+ let mut encoder: Option < Encoder > = None ;
184+ let mut setup = None ;
185+ let mut file: Option < File > = None ;
186+ let mut frame = 0 ;
187+ loop {
188+ let Ok ( next) = rx. recv ( ) else {
189+ break ;
190+ } ;
191+ match next {
192+ RecordCommand :: Start ( name, preset, tune) => {
193+ info ! ( "starting recording at {}" , name) ;
194+ file = Some ( File :: create ( name) . unwrap ( ) ) ;
195+ setup = Some ( Setup :: preset ( preset, tune, false , true ) . high ( ) ) ;
158196 }
159- }
160- RecordCommand :: Frame ( image) => {
161- if let Some ( setup) = setup. take ( ) {
162- let mut new_encoder = setup
163- . fps ( ( 1000 / frame_time. as_millis ( ) ) as u32 , 1 )
164- . build ( Colorspace :: RGB , image. width ( ) as i32 , image. height ( ) as i32 )
165- . unwrap ( ) ;
166- let headers = new_encoder. headers ( ) . unwrap ( ) ;
167- file. as_mut ( )
168- . unwrap ( )
169- . write_all ( headers. entirety ( ) )
170- . unwrap ( ) ;
171- encoder = Some ( new_encoder) ;
197+ RecordCommand :: Stop => {
198+ info ! ( "stopping recording" ) ;
199+ if let Some ( encoder) = encoder. take ( ) {
200+ let mut flush = encoder. flush ( ) ;
201+ let mut file = file. take ( ) . unwrap ( ) ;
202+ while let Some ( result) = flush. next ( ) {
203+ let ( data, _) = result. unwrap ( ) ;
204+ file. write_all ( data. entirety ( ) ) . unwrap ( ) ;
205+ }
206+ }
172207 }
173- if let Some ( encoder) = encoder. as_mut ( ) {
174- let pts = ( frame_time. as_millis ( ) * frame) as i64 ;
175-
176- frame += 1 ;
177- let ( data, _) = encoder
178- . encode (
179- pts,
180- x264:: Image :: rgb (
208+ RecordCommand :: Frame ( image) => {
209+ if let Some ( setup) = setup. take ( ) {
210+ let mut new_encoder = setup
211+ . fps ( ( 1000 / frame_time. as_millis ( ) ) as u32 , 1 )
212+ . build (
213+ Colorspace :: RGB ,
181214 image. width ( ) as i32 ,
182215 image. height ( ) as i32 ,
183- & image. try_into_dynamic ( ) . unwrap ( ) . to_rgb8 ( ) ,
184- ) ,
185- )
186- . unwrap ( ) ;
187- file. as_mut ( ) . unwrap ( ) . write_all ( data. entirety ( ) ) . unwrap ( ) ;
216+ )
217+ . unwrap ( ) ;
218+ let headers = new_encoder. headers ( ) . unwrap ( ) ;
219+ file. as_mut ( )
220+ . unwrap ( )
221+ . write_all ( headers. entirety ( ) )
222+ . unwrap ( ) ;
223+ encoder = Some ( new_encoder) ;
224+ }
225+ if let Some ( encoder) = encoder. as_mut ( ) {
226+ let pts = ( frame_time. as_millis ( ) * frame) as i64 ;
227+
228+ frame += 1 ;
229+ let ( data, _) = encoder
230+ . encode (
231+ pts,
232+ x264:: Image :: rgb (
233+ image. width ( ) as i32 ,
234+ image. height ( ) as i32 ,
235+ & image. try_into_dynamic ( ) . unwrap ( ) . to_rgb8 ( ) ,
236+ ) ,
237+ )
238+ . unwrap ( ) ;
239+ file. as_mut ( ) . unwrap ( ) . write_all ( data. entirety ( ) ) . unwrap ( ) ;
240+ }
188241 }
189242 }
190243 }
191- }
192- } ) ;
244+ } ) ;
193245
194- let frame_time = self . frame_time ;
246+ let frame_time = self . frame_time ;
195247
196- app. add_message :: < RecordScreen > ( ) . add_systems (
197- Update ,
198- (
199- ( move |mut messages : MessageWriter < RecordScreen > , mut recording : Local < bool > | {
200- * recording = !* recording;
201- if * recording {
202- messages. write ( RecordScreen :: Start ) ;
203- } else {
204- messages. write ( RecordScreen :: Stop ) ;
205- }
206- } )
207- . run_if ( input_just_pressed ( self . toggle ) ) ,
208- {
209- let tx = tx. clone ( ) ;
210- let preset = self . preset ;
211- let tune = self . tune ;
212- move |mut commands : Commands ,
248+ app. add_message :: < RecordScreen > ( ) . add_systems (
249+ Update ,
250+ (
251+ ( move |mut messages : MessageWriter < RecordScreen > ,
252+ mut recording : Local < bool > | {
253+ * recording = !* recording;
254+ if * recording {
255+ messages. write ( RecordScreen :: Start ) ;
256+ } else {
257+ messages. write ( RecordScreen :: Stop ) ;
258+ }
259+ } )
260+ . run_if ( input_just_pressed ( self . toggle ) ) ,
261+ {
262+ let tx = tx. clone ( ) ;
263+ let preset = self . preset ;
264+ let tune = self . tune ;
265+ move |mut commands : Commands ,
213266 mut recording : Local < bool > ,
214267 mut messages : MessageReader < RecordScreen > ,
215268 window : Single < & Window , With < PrimaryWindow > > ,
@@ -251,9 +304,10 @@ impl Plugin for EasyScreenRecordPlugin {
251304 ) ;
252305 }
253306 }
254- } ,
255- )
256- . chain ( ) ,
257- ) ;
307+ } ,
308+ )
309+ . chain ( ) ,
310+ ) ;
311+ }
258312 }
259313}
0 commit comments