### Start FFmpeg Job Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/scheduler/ffmpeg_scheduler/struct.FfmpegScheduler.html?search=std%3A%3Avec Initializes FFmpeg components and transitions the scheduler to the Running state. Returns an error if setup fails. ```rust let scheduler = FfmpegScheduler::new(ffmpeg_context); let running_scheduler = scheduler.start().expect("Failed to start FFmpeg"); ``` -------------------------------- ### Basic FFmpeg Context Setup Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/index.html?search=std%3A%3Avec Demonstrates the basic steps to build an FFmpeg context with an input, a filter description, and an output. This is the starting point for most FFmpeg operations using ez_ffmpeg. ```rust use ez_ffmpeg::FfmpegContext; use ez_ffmpeg::FfmpegScheduler; fn main() -> Result<(), Box> { // 1. Build the FFmpeg context let context = FfmpegContext::builder() .input("input.mp4") .filter_desc("hue=s=0") // Example filter: desaturate .output("output.mov") .build()?; ``` -------------------------------- ### Start FFmpeg Scheduler Initialization Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html?search=u32+-%3E+bool Initializes FFmpeg components and transitions the scheduler from Initialization to Running. Returns an error if setup fails. ```rust pub fn start(mut self) -> crate::error::Result> { let packet_pool = match ObjPool::new(64, new_packet, unref_packet, packet_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let frame_pool = match ObjPool::new(64, new_frame, unref_frame, frame_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let scheduler_status = self.status.clone(); scheduler_status.store(STATUS_RUN, Ordering::Release); let thread_sync = self.thread_sync.clone(); let scheduler_result = self.result.clone(); let demux_nodes = self.ffmpeg_context.demuxs.iter().map(|demux| demux.node.clone()).collect::>(); let mux_stream_nodes = self.ffmpeg_context.muxs.iter().flat_map(|mux| mux.mux_stream_nodes.clone()).collect::>(); let input_controller = InputController::new(demux_nodes, mux_stream_nodes); let input_controller = Arc::new(input_controller); // Muxer for (mux_idx, mux) in self.ffmpeg_context.muxs.iter_mut().enumerate() { // Even if it's not ready here, it's going to be ready later, so it locks first thread_sync.thread_start(); if mux.is_ready() { if let Err(e) = mux_init( mux_idx, mux, packet_pool.clone(), input_controller.clone(), mux.mux_stream_nodes.clone(), scheduler_status.clone(), thread_sync.clone(), scheduler_result.clone(), ) { Self::cleanup(&scheduler_status, &self.ffmpeg_context); return Err(e); } } } // Placeholder for the rest of the start function logic unimplemented!() } ``` -------------------------------- ### Start FFmpeg job initialization Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Initializes FFmpeg components and transitions the scheduler to the 'Running' state. Returns an error if setup fails, cleaning up resources. ```rust pub fn start(mut self) -> crate::error::Result> { let packet_pool = match ObjPool::new(64, new_packet, unref_packet, packet_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let frame_pool = match ObjPool::new(64, new_frame, unref_frame, frame_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let scheduler_status = self.status.clone(); scheduler_status.store(STATUS_RUN, Ordering::Release); let thread_sync = self.thread_sync.clone(); let scheduler_result = self.result.clone(); let demux_nodes = self.ffmpeg_context.demuxs.iter().map(|demux| demux.node.clone()).collect::>(); let mux_stream_nodes = self.ffmpeg_context.muxs.iter().flat_map(|mux| mux.mux_stream_nodes.clone()).collect::>(); let input_controller = InputController::new(demux_nodes, mux_stream_nodes); let input_controller = Arc::new(input_controller); // Muxer for (mux_idx, mux) in self.ffmpeg_context.muxs.iter_mut().enumerate() { // Even if it's not ready here, it's going to be ready later, so it locks first thread_sync.thread_start(); if mux.is_ready() { if let Err(e) = mux_init( mux_idx, mux, packet_pool.clone(), input_controller.clone(), mux.mux_stream_nodes.clone(), scheduler_status.clone(), thread_sync.clone(), scheduler_result.clone(), ) { Self::cleanup(&scheduler_status, &self.ffmpeg_context); return Err(e); } } } // TODO: Add demuxer initialization here // TODO: Add filter initialization here // TODO: Add encoder initialization here Ok(FfmpegScheduler { ffmpeg_context: self.ffmpeg_context, state: Running { packet_pool, frame_pool, input_controller }, thread_sync, status: scheduler_status, result: scheduler_result, _guard: None, }) } ``` -------------------------------- ### Start FFmpeg Scheduler Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html?search=std%3A%3Avec Initializes FFmpeg components and transitions the scheduler to the 'Running' state. This method returns an error if setup fails and cleans up resources. It requires the scheduler to be in the 'Initialization' state. ```rust pub fn start(mut self) -> crate::error::Result> { let packet_pool = match ObjPool::new(64, new_packet, unref_packet, packet_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let frame_pool = match ObjPool::new(64, new_frame, unref_frame, frame_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let scheduler_status = self.status.clone(); scheduler_status.store(STATUS_RUN, Ordering::Release); let thread_sync = self.thread_sync.clone(); let scheduler_result = self.result.clone(); let demux_nodes = self.ffmpeg_context.demuxs.iter().map(|demux| demux.node.clone()).collect::>(); let mux_stream_nodes = self.ffmpeg_context.muxs.iter().flat_map(|mux| mux.mux_stream_nodes.clone()).collect::>(); let input_controller = InputController::new(demux_nodes, mux_stream_nodes); let input_controller = Arc::new(input_controller); // Muxer for (mux_idx, mux) in self.ffmpeg_context.muxs.iter_mut().enumerate() { // Even if it's not ready here, it's going to be ready later, so it locks first thread_sync.thread_start(); if mux.is_ready() { if let Err(e) = mux_init( mux_idx, mux, packet_pool.clone(), input_controller.clone(), mux.mux_stream_nodes.clone(), scheduler_status.clone(), thread_sync.clone(), scheduler_result.clone(), ) { Self::cleanup(&scheduler_status, &self.ffmpeg_context); return Err(e); } } } // ... rest of the function unimplemented!() } ``` -------------------------------- ### Start FfmpegScheduler Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/scheduler/ffmpeg_scheduler/struct.FfmpegScheduler.html Initializes FFmpeg components and transitions the scheduler to the Running state. Returns an error if setup fails. ```rust let scheduler = FfmpegScheduler::new(ffmpeg_context); let running_scheduler = scheduler.start().expect("Failed to start FFmpeg"); // Now it's in Running state, you can wait or pause/abort, etc. ``` -------------------------------- ### Start FFmpeg Scheduler Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html Initializes FFmpeg components and transitions the scheduler to the 'Running' state. Returns an error if setup fails. Call this after creating the scheduler to begin processing. ```rust pub fn start(mut self) -> crate::error::Result> { let packet_pool = match ObjPool::new(64, new_packet, unref_packet, packet_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let frame_pool = match ObjPool::new(64, new_frame, unref_frame, frame_is_null) { Ok(pool) => pool, Err(e) => { Self::cleanup(&self.status, &self.ffmpeg_context); return Err(e); } }; let scheduler_status = self.status.clone(); scheduler_status.store(STATUS_RUN, Ordering::Release); let thread_sync = self.thread_sync.clone(); let scheduler_result = self.result.clone(); let demux_nodes = self.ffmpeg_context.demuxs.iter().map(|demux| demux.node.clone()).collect::>(); let mux_stream_nodes = self.ffmpeg_context.muxs.iter().flat_map(|mux| mux.mux_stream_nodes.clone()).collect::>(); let input_controller = InputController::new(demux_nodes, mux_stream_nodes); let input_controller = Arc::new(input_controller); // Muxer for (mux_idx, mux) in self.ffmpeg_context.muxs.iter_mut().enumerate() { // Even if it's not ready here, it's going to be ready later, so it locks first thread_sync.thread_start(); if mux.is_ready() { if let Err(e) = mux_init( mux_idx, mux, packet_pool.clone(), input_controller.clone(), mux.mux_stream_nodes.clone(), scheduler_status.clone(), thread_sync.clone(), scheduler_result.clone(), ) { ``` -------------------------------- ### StreamBuilder Example Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/rtmp/embed_rtmp_server/struct.StreamBuilder.html?search=u32+-%3E+bool Demonstrates how to use StreamBuilder to configure and start an RTMP stream. It sets the server address, application name, stream key, and input file. The readrate defaults to realtime if not specified. ```rust use ez_ffmpeg::rtmp::embed_rtmp_server::EmbedRtmpServer; let handle = EmbedRtmpServer::stream_builder() .address("localhost:1935") .app_name("live") .stream_key("stream1") .input_file("video.mp4") // readrate defaults to 1.0 (realtime) .start()?; ``` -------------------------------- ### Get Available Audio Input Devices Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/device/fn.get_input_audio_devices.html?search= Retrieves a list of available audio input devices on the system. This example iterates through the returned device names and prints them. ```rust let audio_devices = get_input_audio_devices()?; for device in audio_devices { println!("Available audio device: {}", device); } ``` -------------------------------- ### Start RTMP Server with Manual Configuration Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/rtmp/embed_rtmp_server/struct.EmbedRtmpServer.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E For complex scenarios, this demonstrates starting an EmbedRtmpServer and manually creating an RTMP input, allowing full control over the server and FFmpeg context. ```rust let server = EmbedRtmpServer::new("localhost:1935").start()?; let output = server.create_rtmp_input("app", "stream")?; // ... configure Input and FfmpegContext manually ``` -------------------------------- ### Start Embedded RTMP Server and Stream to FFmpeg Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/mod.rs.html?search= This example demonstrates how to create and start an embedded RTMP server, define an RTMP input for FFmpeg to push data to, and then configure and run an FFmpeg context to stream a local file to the RTMP server. It includes optional read rate limiting for the input file and error handling for the FFmpeg process. The RTMP server can be stopped using `embed_rtmp_server.stop()` when no longer needed. ```rust let embed_rtmp_server = EmbedRtmpServer::new("localhost:1935") .start() .unwrap(); let output = embed_rtmp_server .create_rtmp_input("my-app", "my-stream") .unwrap(); let input = Input::from("test.mp4") .set_readrate(1.0); let result = FfmpegContext::builder() .input(input) .output(output) .build().unwrap() .start().unwrap() .wait(); if let Err(e) = result { eprintln!("FFmpeg encountered an error: {:?}", e); } ``` -------------------------------- ### Building and Starting an FFmpeg Context for RTMP Streaming Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/embed_rtmp_server.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E This snippet demonstrates how to configure and start an FFmpeg context to stream a local file to an RTMP server. It includes setting up input and output, and handling potential errors during the build and start process. ```rust let output = server .create_rtmp_input(&app_name, &stream_key) .map_err(StreamError::Ffmpeg)?; // Create the input with optional readrate let input_path = input_file.to_string_lossy().to_string(); let mut input = Input::from(input_path); if let Some(rate) = self.readrate { input = input.set_readrate(rate); } // Build and start the FFmpeg context let scheduler = FfmpegContext::builder() .input(input) .output(output) .build() .map_err(StreamError::Ffmpeg)? .start() .map_err(StreamError::Ffmpeg)?; Ok(StreamHandle { _server: server, scheduler: Some(scheduler), }) ``` -------------------------------- ### Start FFmpeg Job and Get Scheduler Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/context/ffmpeg_context/struct.FfmpegContext.html Consumes the FfmpegContext to start an FFmpeg job. Returns an FfmpegScheduler to manage the running process. The example shows starting the job and optionally waiting for its completion. ```rust let context = FfmpegContext::builder() .input("input.mp4") .output("output.mp4") .build() .unwrap(); // Start the FFmpeg job and get a scheduler to manage it let scheduler = context.start().expect("Failed to start Ffmpeg job"); // Optionally, wait for it to finish let result = scheduler.wait(); assert!(result.is_ok()); ``` -------------------------------- ### FFmpeg Hardware Acceleration Setup Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html Initializes FFmpeg context with input and output files for hardware acceleration testing. Includes logger configuration. ```rust let _ = env_logger::builder() .filter_level(log::LevelFilter::Trace) .is_test(true) .try_init(); let input: Input = "test.mp4".into(); let output: Output = "output.mp4".into(); ``` -------------------------------- ### Get Is Started Flag Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/muxer.rs.html?search=std%3A%3Avec Returns a clone of the `Arc` that indicates whether the muxer process has started. ```rust pub(crate) fn get_is_started(&self) -> Arc { self.is_started.clone() } ``` -------------------------------- ### Synchronous FFmpeg Wait Operation Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html Shows a basic example of starting an FFmpeg scheduler and waiting for its completion synchronously. ```rust let _ = env_logger::builder() .filter_level(log::LevelFilter::Trace) .is_test(true) .try_init(); let context = FfmpegContext::builder() .input("test.mp4") .filter_desc("hue=s=0") .output("output.mp4") .build() .unwrap(); let result = FfmpegScheduler::new(context) .start() .unwrap() .wait(); assert!(result.is_ok()); ``` -------------------------------- ### FfmpegScheduler::start Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/scheduler/ffmpeg_scheduler/struct.FfmpegScheduler.html?search=u32+-%3E+bool Initializes FFmpeg components and transitions the scheduler to the running state. Returns an error if setup fails. ```APIDOC ## pub fn start(self) -> Result> ### Description Initializes all FFmpeg components (demuxers, encoders, filters, muxers) and transitions the scheduler from **Initialization** to **Running**. If any part of the setup fails (e.g., an invalid codec, a missing file), this method returns an error and cleans up resources. ### Returns * `Ok(FfmpegScheduler)` if initialization succeeded. * `Err(...)` if an error occurred during FFmpeg setup. ### Example ```rust let scheduler = FfmpegScheduler::new(ffmpeg_context); let running_scheduler = scheduler.start().expect("Failed to start FFmpeg"); // Now it's in Running state, you can wait or pause/abort, etc. ``` ``` -------------------------------- ### Manual FFmpeg Context and RTMP Input Creation Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/embed_rtmp_server.rs.html?search=std%3A%3Avec Demonstrates the manual setup of an RTMP server, creation of an RTMP input, and building an FFmpeg context for streaming. This provides more control over the streaming process. ```rust let output = server .create_rtmp_input(&app_name, &stream_key) .map_err(StreamError::Ffmpeg)?; // Create the input with optional readrate let input_path = input_file.to_string_lossy().to_string(); let mut input = Input::from(input_path); if let Some(rate) = self.readrate { input = input.set_readrate(rate); } // Build and start the FFmpeg context let scheduler = FfmpegContext::builder() .input(input) .output(output) .build() .map_err(StreamError::Ffmpeg)? .start() .map_err(StreamError::Ffmpeg)?; Ok(StreamHandle { _server: server, scheduler: Some(scheduler), }) ``` ```rust let embed_rtmp_server = EmbedRtmpServer::new("localhost:1935"); let embed_rtmp_server = embed_rtmp_server.start().unwrap(); let output = embed_rtmp_server .create_rtmp_input("my-app", "my-stream") .unwrap(); let start = current(); let result = FfmpegContext::builder() .input(Input::from("test.mp4") ``` -------------------------------- ### Seek to Start of File Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/demux_task.rs.html Initiates a seek operation to the beginning of the input file. It adjusts the maximum presentation timestamp (max_pts) and calculates the duration based on start and end times. This function is crucial for repositioning the stream after initial setup or seeking. ```rust unsafe fn seek_to_start( demux_parameter: &mut DemuxerParameter, in_fmt_ctx: *mut AVFormatContext, ) -> i32 { let start_time = demux_parameter.start_time_us.unwrap_or(0); let ret = avformat_seek_file(in_fmt_ctx, -1, i64::MIN, start_time, start_time, 0); if ret < 0 { return ret; } if demux_parameter.end_pts.ts != AV_NOPTS_VALUE && demux_parameter.max_pts.ts == AV_NOPTS_VALUE || av_compare_ts( demux_parameter.max_pts.ts, demux_parameter.max_pts.tb, demux_parameter.end_pts.ts, demux_parameter.end_pts.tb, ) < 0 { demux_parameter.max_pts = demux_parameter.end_pts.clone(); } if demux_parameter.max_pts.ts != AV_NOPTS_VALUE { let min_pts = if demux_parameter.min_pts.ts == AV_NOPTS_VALUE { 0 } else { demux_parameter.min_pts.ts }; demux_parameter.duration.ts = demux_parameter.max_pts.ts - av_rescale_q( min_pts, demux_parameter.min_pts.tb, demux_parameter.max_pts.tb, ); } demux_parameter.duration.tb = demux_parameter.max_pts.tb; if demux_parameter.stream_loop > 0 { demux_parameter.stream_loop -= 1; } let loop_status = if demux_parameter.stream_loop > 0 { format!("Remaining loops: {}", demux_parameter.stream_loop) } else if demux_parameter.stream_loop == 0 { "Last loop".to_string() } else { "Infinite loop mode".to_string() }; debug!("Repositioning stream to starting point: position={start_time}μs, {loop_status}"); ret } ``` -------------------------------- ### FfmpegScheduler::start Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/scheduler/ffmpeg_scheduler/struct.FfmpegScheduler.html Initializes FFmpeg components and transitions the scheduler from Initialization to Running state. Returns an error if setup fails. ```APIDOC ## FfmpegScheduler::start ### Description Initializes all FFmpeg components (demuxers, encoders, filters, muxers) and transitions the scheduler from **Initialization** to **Running**. If any part of the setup fails (e.g., an invalid codec, a missing file), this method returns an error and cleans up resources. ### Method ``` pub fn start(self) -> Result> ``` ### Returns * `Ok(FfmpegScheduler)` - If initialization succeeded. * `Err(...)` - If an error occurred during FFmpeg setup. ### Example ```rust let scheduler = FfmpegScheduler::new(ffmpeg_context); let running_scheduler = scheduler.start().expect("Failed to start FFmpeg"); // Now it's in Running state, you can wait or pause/abort, etc. ``` ``` -------------------------------- ### FFmpegScheduler Usage Example Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/ffmpeg_scheduler.rs.html Demonstrates the lifecycle of the FFmpegScheduler: initialization, starting, processing, and stopping. It also includes verification of the output file. ```rust let scheduler = FfmpegScheduler::new(context); let scheduler = scheduler.start().unwrap(); // Let the job process some frames before stopping sleep(Duration::from_millis(500)); // stop() should block until all threads complete scheduler.stop(); // Verify output file exists and has content let metadata = std::fs::metadata("output.mp4").unwrap(); assert!(metadata.len() > 0, "Output file should have content after stop()"); ``` -------------------------------- ### Parse Options and Create Hardware Device Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/hwaccel.rs.html?search= Parses a string of options into FFmpeg dictionary format and then creates a hardware device context. ```rust let mut options = null_mut(); let Ok(v_cstr) = CString::new(v) else { error!("Device creation failed: option:{v} can't convert to CString"); av_buffer_unref(&mut device_ref); return (AVERROR(EINVAL), None); }; let eq_cstr = CString::new("=").unwrap(); let comma_cstr = CString::new(",").unwrap(); let mut err = av_dict_parse_string( &mut options, v_cstr.as_ptr(), eq_cstr.as_ptr(), comma_cstr.as_ptr(), 0, ); if err < 0 { error!("Invalid device specification \"{arg}\": failed to parse options"); av_buffer_unref(&mut device_ref); return (AVERROR(EINVAL), None); } err = av_hwdevice_ctx_create(&mut device_ref, device_type, null(), options, 0); if err < 0 { error!("Device creation failed: {err}."); av_buffer_unref(&mut device_ref); return (err, None); } ``` -------------------------------- ### Setup and Play Request for Multiple Watchers Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/rtmp_scheduler.rs.html?search=std%3A%3Avec Initializes the scheduler, creates a channel with a publisher, and registers multiple watchers by handling play requests. ```rust let mut scheduler = RtmpScheduler::new(10); let stream_key = "test_stream".to_string(); let publisher_connection_id = 1; let watcher1_connection_id = 2; let watcher2_connection_id = 3; let watcher3_connection_id = 4; // Create channel with publisher scheduler.new_channel(stream_key.clone(), publisher_connection_id); // Create multiple watchers for (watcher_id, request_id) in [ (watcher1_connection_id, 1u32), (watcher2_connection_id, 2u32), (watcher3_connection_id, 3u32), ] { let _ = scheduler.bytes_received(watcher_id, &[]); let mut results = Vec::new(); scheduler.handle_play_requested( watcher_id, request_id, "app".to_string(), stream_key.clone(), 1, &mut results, ); } // Verify all watchers are in channel let channel = scheduler.channels.get(&stream_key).unwrap(); assert_eq!(channel.watching_client_ids.len(), 3); ``` -------------------------------- ### Initialize Muxer Task Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/mux_task.rs.html Initializes the muxer task by starting the mux task process. This function is typically called during the setup phase of the scheduler. ```rust pub(crate) fn mux_init( mux_idx: usize, mux: &mut Muxer, packet_pool: ObjPool, input_controller: Arc, mux_stream_nodes: Vec>, scheduler_status: Arc, thread_sync: ThreadSynchronizer, scheduler_result: Arc>>>, ) -> crate::error::Result<()> { let out_fmt_ctx = mux.out_fmt_ctx; mux.out_fmt_ctx = null_mut(); mux_task_start( mux_idx, out_fmt_ctx, mux.is_set_write_callback, mux.take_queue(), mux.start_time_us, mux.recording_time_us, mux.stream_count(), mux.format_opts.clone(), mux.take_src_pre_recvs(), mux.get_is_started(), packet_pool, input_controller, mux_stream_nodes, scheduler_status, thread_sync, scheduler_result, ) } ``` -------------------------------- ### Initialize FFmpeg Context Builder Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/mod.rs.html?search=std%3A%3Avec Shows the initial step of creating an FFmpeg context builder, which is typically done via the `FfmpegContext::builder()` method. This builder is then used to configure inputs, outputs, and filters. ```rust let builder = FfmpegContext::builder(); ``` -------------------------------- ### Basic FFmpeg Pipeline Usage Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/lib.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Demonstrates how to build and run a simple FFmpeg pipeline for transcoding or filtering. This example shows setting an input, applying a filter, setting an output, and running the job synchronously. ```rust use ez_ffmpeg::FfmpegContext; use ez_ffmpeg::FfmpegScheduler; fn main() -> Result<(), Box> { // 1. Build the FFmpeg context let context = FfmpegContext::builder() .input("input.mp4") .filter_desc("hue=s=0") // Example filter: desaturate .output("output.mov") .build()?; // 2. Run it via FfmpegScheduler (sync mode) let result = FfmpegScheduler::new(context) .start()? .wait(); result?; // If any error occurred, propagate it Ok(()) } ``` -------------------------------- ### Initialize and Open FFmpeg Encoder Context Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/scheduler/enc_task.rs.html?search=std%3A%3Avec Sets up the FFmpeg encoder context, handles hardware device setup, opens the codec, and configures stream parameters. Includes error handling for various stages of the encoding process. ```rust unsafe fn open_encoder( stream: *mut libffmpeg_sys_next::AVStream, enc: *mut libffmpeg_sys_next::AVCodec, ready_sender: Option>, ) -> Result<(), OpenEncoderOperationError> { let enc_ctx = (*stream).codecpar; let mut enc_ctx: *mut AVCodecContext = avcodec_alloc_context3(enc); if enc_ctx.is_null() { return Err(OpenEncoder(OpenEncoderOperationError::AllocError)); } // Copy subtitle header if present if !frame_box.frame_data.subtitle_header.is_null() { if (*enc_ctx).subtitle_header.is_null() { (*enc_ctx).subtitle_header = av_malloc(frame_box.frame_data.subtitle_header_size as usize) as *mut u8; if (*enc_ctx).subtitle_header.is_null() { error!("Failed to allocate memory for subtitle header"); return Err(OpenEncoder(OpenEncoderOperationError::OutOfMemory)); } } std::ptr::copy_nonoverlapping( frame_box.frame_data.subtitle_header as *const u8, (*enc_ctx).subtitle_header, frame_box.frame_data.subtitle_header_size as usize, ); (*enc_ctx).subtitle_header_size = frame_box.frame_data.subtitle_header_size; } if (*enc_ctx).capabilities as u32 & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE != 0 { (*enc_ctx).flags |= AV_CODEC_FLAG_COPY_OPAQUE as i32; } (*enc_ctx).flags |= AV_CODEC_FLAG_FRAME_DURATION as i32; let frames_ref = if (*enc_ctx).codec_type == AVMEDIA_TYPE_VIDEO || (*enc_ctx).codec_type == AVMEDIA_TYPE_AUDIO { (*frame).hw_frames_ctx } else { null_mut() }; let ret = hw_device_setup_for_encode(enc_ctx, frames_ref); if ret < 0 { error!("Encoding hardware device setup failed"); return Err(OpenEncoder(OpenEncoderOperationError::HwSetupError( OpenEncoderError::OutOfMemory, ))); } let ret = avcodec_open2(enc_ctx, enc, null_mut()); if ret < 0 { if ret != AVERROR_EXPERIMENTAL { error!("Error while opening encoder - maybe incorrect parameters such as bit_rate, rate, width or height."); } return Err(OpenEncoder(OpenEncoderOperationError::CodecOpenError( OpenEncoderError::OutOfMemory, ))); } if (*enc_ctx).bit_rate != 0 && (*enc_ctx).bit_rate < 1000 && (*enc_ctx).codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */ { warn!("The bitrate parameter is set too low. It takes bits/s as argument, not kbits/s"); } let ret = avcodec_parameters_from_context((*stream).codecpar, enc_ctx); if ret < 0 { error!("Error initializing the output stream codec context."); return Err(OpenEncoder( OpenEncoderOperationError::CodecParametersError(OpenEncoderError::OutOfMemory), )); } // copy timebase while removing common factors if (*stream).time_base.num <= 0 || (*stream).time_base.den <= 0 { (*stream).time_base = av_add_q((*enc_ctx).time_base, AVRational { num: 0, den: 1 }); } if let Some(ready_sender) = ready_sender { let _ = ready_sender.send((*stream).index); } Ok(()) } ``` -------------------------------- ### FFmpeg Context Builder with Input and Output Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/embed_rtmp_server.rs.html Illustrates building an FFmpeg context by providing an input file and an RTMP output. This is part of the manual setup for streaming. ```rust let input_path = input_file.to_string_lossy().to_string(); let mut input = Input::from(input_path); if let Some(rate) = self.readrate { input = input.set_readrate(rate); } let scheduler = FfmpegContext::builder() .input(input) .output(output) .build() .map_err(StreamError::Ffmpeg)? .start() .map_err(StreamError::Ffmpeg)?; ``` -------------------------------- ### Get Input Video Devices Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/device/fn.get_input_video_devices.html?search= Retrieves a list of available video input devices. This example iterates through the returned devices and prints their names. It requires the `get_input_video_devices` function to be available and the `Result` type to be handled. ```rust let video_devices = get_input_video_devices()?; for device in video_devices { println!("Available video device: {}", device); } ``` -------------------------------- ### Initialize FFmpeg Context with Filters Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search=std%3A%3Avec Demonstrates the creation of an FFmpegContext with various filter configurations. It shows how to specify input files, filter chains, and output files, including advanced output mapping. ```rust let _ = env_logger::builder() .filter_level(log::LevelFilter::Debug) .is_test(true) .try_init(); let _ffmpeg_context = FfmpegContext::new( vec!["test.mp4".to_string().into()], vec!["hue=s=0".to_string().into()], vec!["output.mp4".to_string().into()], ) .unwrap(); let _ffmpeg_context = FfmpegContext::new( vec!["test.mp4".into()], vec!["[0:v]hue=s=0".into()], vec!["output.mp4".to_string().into()], ) .unwrap(); let _ffmpeg_context = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0[my-out]".into()], vec![Output::from("output.mp4").add_stream_map("my-out")], ) .unwrap(); let result = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0".into()], vec![Output::from("output.mp4").add_stream_map("0:v?")], ); assert!(result.is_err()); let result = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0".into()], ``` -------------------------------- ### Embed RTMP Server and FFmpeg Integration Example Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/mod.rs.html?search=std%3A%3Avec This snippet shows how to create and start an embedded RTMP server, define an RTMP input for FFmpeg to push data to, and then configure and run an FFmpeg context to stream a local file to the RTMP server. It includes error handling for the FFmpeg process. ```rust let embed_rtmp_server = EmbedRtmpServer::new("localhost:1935") .start() .unwrap(); let output = embed_rtmp_server .create_rtmp_input("my-app", "my-stream") .unwrap(); let input = Input::from("test.mp4") .set_readrate(1.0); // optional: limit reading speed let result = FfmpegContext::builder() .input(input) .output(output) .build().unwrap() .start().unwrap() .wait(); if let Err(e) = result { eprintln!("FFmpeg encountered an error: {:?}", e); } ``` -------------------------------- ### Basic FFmpeg Pipeline Execution Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/mod.rs.html?search= Demonstrates how to build an FFmpeg context with an input file, apply a simple video filter, specify an output file, and then execute the pipeline using a scheduler. ```rust fn main() -> Result<(), Box> { // 1. Build an FfmpegContext with an input, a simple filter, and an output let context = FfmpegContext::builder() .input("test.mp4") .filter_desc("hue=s=0") // Example: desaturate video .output("output.mp4") .build()?; // 2. Create a scheduler and start the job let scheduler = FfmpegScheduler::new(context).start()?; // 3. Block until it's finished scheduler.wait()?; Ok(()) } ``` -------------------------------- ### Adjust Timestamp with Stream Start Time Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search= Calculates the effective start timestamp by adding the stream's start time to the input's specified start time. Handles cases where the stream start time is not set. ```rust let mut timestamp = input.start_time_us.unwrap_or(0); /* add the stream start time */ if (*in_fmt_ctx).start_time != ffmpeg_sys_next::AV_NOPTS_VALUE { timestamp += (*in_fmt_ctx).start_time; } ``` -------------------------------- ### FFmpegContext Initialization with Stream Mapping Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html Illustrates creating an FfmpegContext with specific input, filter, and output configurations, including stream mapping. This example shows scenarios that result in an error due to incorrect stream mapping. ```rust let result = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0[fg-out]".into()], vec![ Output::from("output.mp4").add_stream_map("my-out?"), Output::from("output.mp4").add_stream_map("fg-out"), ], ); assert!(result.is_err()); ``` ```rust let result = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0".into()], vec![Output::from("output.mp4").add_stream_map_with_copy("1:v")], ); assert!(result.is_err()); ``` ```rust let result = FfmpegContext::new( vec!["test.mp4".into()], vec!["hue=s=0[fg-out]".into()], vec![Output::from("output.mp4").add_stream_map("fg-out?")], ); assert!(result.is_err()); ``` -------------------------------- ### Creating Input Sources in ez-ffmpeg Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/context/input/index.html?search=std%3A%3Avec Demonstrates how to create an Input struct for a file or network URL using the .into() method, and how to create a custom input using a read callback. ```rust use ez_ffmpeg::core::context::input::Input; // Basic file or network URL: let file_input: Input = "example.mp4".into(); // Or a custom read callback: let custom_input = Input::new_by_read_callback(|buf| { // Fill `buf` with data from your source // Return the number of bytes read, or negative for errors 0 }); ``` -------------------------------- ### Calculating and Applying Start Time Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search=std%3A%3Avec Calculates the effective start timestamp by adding the input's start time to the stream's start time if available. This is used for seeking. ```rust let mut timestamp = input.start_time_us.unwrap_or(0); /* add the stream start time */ if (*in_fmt_ctx).start_time != ffmpeg_sys_next::AV_NOPTS_VALUE { timestamp += (*in_fmt_ctx).start_time; } /* if seeking requested, we execute it */ if let Some(start_time_us) = input.start_time_us { let mut seek_timestamp = timestamp; if (*(*in_fmt_ctx).iformat).flags & ffmpeg_sys_next::AVFMT_SEEK_TO_PTS == 0 { let mut dts_heuristic = false; let stream_count = (*in_fmt_ctx).nb_streams; for i in 0..stream_count { let stream = *(*in_fmt_ctx).streams.add(i as usize); let par = (*stream).codecpar; } } } ``` -------------------------------- ### Calculate and Apply Start Time Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search=u32+-%3E+bool Calculates the effective start timestamp by adding the input's start time to the FFmpeg context's start time. This is used when seeking is requested. ```rust let mut timestamp = input.start_time_us.unwrap_or(0); /* add the stream start time */ if (*in_fmt_ctx).start_time != ffmpeg_sys_next::AV_NOPTS_VALUE { timestamp += (*in_fmt_ctx).start_time; } /* if seeking requested, we execute it */ if let Some(start_time_us) = input.start_time_us { let mut seek_timestamp = timestamp; if (*(*in_fmt_ctx).iformat).flags & ffmpeg_sys_next::AVFMT_SEEK_TO_PTS == 0 { let mut dts_heuristic = false; let stream_count = (*in_fmt_ctx).nb_streams; for i in 0..stream_count { let stream = *(*in_fmt_ctx).streams.add(i as usize); let par = (*stream).codecpar; ``` -------------------------------- ### List FFmpeg Encoders and Decoders Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/codec/index.html This example demonstrates how to retrieve and print the names of all available FFmpeg encoders and decoders. Ensure FFmpeg is correctly configured and accessible. ```rust use ez_ffmpeg::codec::{get_decoders, get_encoders}; fn main() { // List all available encoders let encoders = get_encoders(); for enc in &encoders { println!("Encoder: {} - {}", enc.codec_name, enc.codec_long_name); } // List all available decoders let decoders = get_decoders(); for dec in &decoders { println!("Decoder: {} - {}", dec.codec_name, dec.codec_long_name); } } ``` -------------------------------- ### Calculating and Applying Stream Start Time Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Calculates the effective start time for the input stream by adding the stream's start time to the requested start time. This ensures accurate playback positioning. ```rust let mut timestamp = input.start_time_us.unwrap_or(0); /* add the stream start time */ if (*in_fmt_ctx).start_time != ffmpeg_sys_next::AV_NOPTS_VALUE { timestamp += (*in_fmt_ctx).start_time; } ``` -------------------------------- ### Basic File/URL and Custom Write Callback Output Source: https://docs.rs/ez-ffmpeg/latest/ez_ffmpeg/core/context/output/index.html Demonstrates creating an Output struct for a file path or URL, and for a custom write callback. The custom callback example also shows how to set the container format. ```rust use ez_ffmpeg::core::context::output::Output; // Basic file/URL output: let file_output: Output = "output.mp4".into(); // Or a custom write callback: let custom_output = Output::new_by_write_callback(|encoded_data| { // Write `encoded_data` somewhere encoded_data.len() as i32 }).set_format("mp4"); ``` -------------------------------- ### Calculate and Apply Start Time for FFmpeg Input Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html Calculates the effective start time for an FFmpeg input, considering both the provided start time and the stream's native start time, and prepares for seeking. ```rust let mut timestamp = input.start_time_us.unwrap_or(0); /* add the stream start time */ if (*in_fmt_ctx).start_time != ffmpeg_sys_next::AV_NOPTS_VALUE { timestamp += (*in_fmt_ctx).start_time; } /* if seeking requested, we execute it */ if let Some(start_time_us) = input.start_time_us { let mut seek_timestamp = timestamp; if (*(*in_fmt_ctx).iformat).flags & ffmpeg_sys_next::AVFMT_SEEK_TO_PTS == 0 { let mut dts_heuristic = false; let stream_count = (*in_fmt_ctx).nb_streams; for i in 0..stream_count { let stream = *(*in_fmt_ctx).streams.add(i as usize); let par = (*stream).codecpar; ``` -------------------------------- ### Start Embedded RTMP Server and Stream to it Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/rtmp/mod.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Demonstrates how to create and start an embedded RTMP server, define an RTMP input for FFmpeg to push data to, and then use FFmpeg to stream a local file to this server. This is useful for setting up a local RTMP ingest point for testing or production. ```rust use ez_ffmpeg::rtmp::embed_rtmp_server::EmbedRtmpServer; use ez_ffmpeg::input::Input; use ez_ffmpeg::FfmpegContext; // 1. Create and start an embedded RTMP server on "localhost:1935" let embed_rtmp_server = EmbedRtmpServer::new("localhost:1935") .start() .unwrap(); // 2. Create an RTMP "input": (app_name="my-app", stream_key="my-stream") // This returns an `Output` for FFmpeg to push data into. let output = embed_rtmp_server .create_rtmp_input("my-app", "my-stream") .unwrap(); // 3. Prepare an `Input` using builder pattern let input = Input::from("test.mp4") .set_readrate(1.0); // optional: limit reading speed // 4. Build and run the FFmpeg context let result = FfmpegContext::builder() .input(input) .output(output) .build().unwrap() .start().unwrap() .wait(); if let Err(e) = result { eprintln!("FFmpeg encountered an error: {:?}", e); } // When done, you can stop the server: `embed_rtmp_server.stop();` ``` -------------------------------- ### Video StreamInfo Example Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/stream_info.rs.html Illustrates the creation of a video StreamInfo object, showcasing its properties like resolution, frame rate, and codec. This is useful for representing video stream metadata. ```rust let video = StreamInfo::Video { index: 5, time_base: AVRational { num: 1, den: 30 }, start_time: 0, duration: 100, nb_frames: 100, r_frame_rate: AVRational { num: 30, den: 1 }, sample_aspect_ratio: AVRational { num: 1, den: 1 }, avg_frame_rate: AVRational { num: 30, den: 1 }, width: 1920, height: 1080, bit_rate: 0, pixel_format: 0, video_delay: 0, fps: 30.0, rotate: 0, codec_id: AVCodecID::AV_CODEC_ID_H264, codec_name: "h264".to_string(), metadata: HashMap::new(), }; ``` -------------------------------- ### Correcting Input Stream Start Times Source: https://docs.rs/ez-ffmpeg/latest/src/ez_ffmpeg/core/context/ffmpeg_context.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Adjusts the effective start time of an input stream based on its constituent streams' start times. Handles timestamp copying and zero-based alignment. ```rust let diff = new_start_time - (*is).start_time; if diff != 0 { debug!("Correcting start time of Input #{} by {diff}us.", i); demux.start_time_effective = new_start_time; if copy_ts && START_AT_ZERO { demux.ts_offset = -new_start_time; } else if !copy_ts { let abs_start_seek = (*is).start_time + demux.start_time_us.unwrap_or(0); demux.ts_offset = if abs_start_seek > new_start_time { -abs_start_seek } else { -new_start_time }; } else if copy_ts { demux.ts_offset = 0; } // demux.ts_offset += demux.input_ts_offset; } ```