1#![allow(clippy::too_many_arguments)]
2
3use blit_compositor::PixelData;
4#[cfg(unix)]
5use blit_remote::SURFACE_FRAME_CODEC_H265;
6use blit_remote::{
7 CODEC_SUPPORT_AV1, CODEC_SUPPORT_H264, CODEC_SUPPORT_H265, SURFACE_FRAME_CODEC_AV1,
8 SURFACE_FRAME_CODEC_H264,
9};
10use openh264::encoder::Encoder as OpenH264Encoder;
11use openh264::formats::YUVBuffer;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum SurfaceEncoderPreference {
15 H264Software,
16 H264Vaapi,
17 H265Vaapi,
18 NvencH264,
19 NvencH265,
20 NvencAV1,
21 AV1,
22}
23
24pub type SurfaceH264EncoderPreference = SurfaceEncoderPreference;
26
27const H264_MAX_WIDTH: u16 = 3840;
29const H264_MAX_HEIGHT: u16 = 2160;
30
31impl SurfaceEncoderPreference {
32 pub fn parse(value: &str) -> Option<Self> {
33 match value.trim() {
34 "h264-software" | "software" => Some(Self::H264Software),
35 "h264-vaapi" | "vaapi" => Some(Self::H264Vaapi),
36 "h265-vaapi" | "hevc-vaapi" => Some(Self::H265Vaapi),
37 "nvenc-h264" | "h264-nvenc" => Some(Self::NvencH264),
38 "nvenc-h265" | "h265-nvenc" | "nvenc-hevc" | "hevc-nvenc" => Some(Self::NvencH265),
39 "nvenc-av1" | "av1-nvenc" => Some(Self::NvencAV1),
40 "av1" => Some(Self::AV1),
41 _ => None,
42 }
43 }
44
45 pub fn parse_list(value: &str) -> Result<Vec<Self>, String> {
47 let mut result = Vec::new();
48 for item in value.split(',') {
49 let item = item.trim();
50 if item.is_empty() {
51 continue;
52 }
53 result.push(Self::parse(item).ok_or_else(|| format!("unknown encoder: {item}"))?);
54 }
55 Ok(result)
56 }
57
58 pub fn defaults() -> Vec<Self> {
63 if let Some(list) = std::env::var("BLIT_SURFACE_ENCODERS")
64 .ok()
65 .and_then(|v| Self::parse_list(&v).ok())
66 {
67 return list;
68 }
69 vec![
70 Self::NvencH265,
71 Self::H265Vaapi,
72 Self::NvencAV1,
73 Self::NvencH264,
74 Self::H264Vaapi,
75 Self::H264Software,
76 Self::AV1,
77 ]
78 }
79
80 pub fn supported_by_client(self, codec_support: u8) -> bool {
83 if codec_support == 0 {
84 return true;
85 }
86 match self {
87 Self::H264Software | Self::H264Vaapi | Self::NvencH264 => {
88 codec_support & CODEC_SUPPORT_H264 != 0
89 }
90 Self::H265Vaapi | Self::NvencH265 => codec_support & CODEC_SUPPORT_H265 != 0,
91 Self::AV1 | Self::NvencAV1 => codec_support & CODEC_SUPPORT_AV1 != 0,
92 }
93 }
94
95 pub fn max_dimensions(self) -> Option<(u16, u16)> {
98 match self {
99 Self::H264Software | Self::H264Vaapi | Self::NvencH264 => {
100 Some((H264_MAX_WIDTH, H264_MAX_HEIGHT))
101 }
102 Self::H265Vaapi | Self::NvencH265 | Self::NvencAV1 | Self::AV1 => None,
103 }
104 }
105
106 pub fn max_dimensions_for_list(prefs: &[Self]) -> Option<(u16, u16)> {
108 let mut result: Option<(u16, u16)> = None;
109 for p in prefs {
110 if let Some((w, h)) = p.max_dimensions() {
111 result = Some(match result {
112 Some((rw, rh)) => (rw.min(w), rh.min(h)),
113 None => (w, h),
114 });
115 }
116 }
117 result
118 }
119}
120
121#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
130pub enum SurfaceQuality {
131 Low,
132 #[default]
133 Medium,
134 High,
135 Lossless,
136}
137
138impl SurfaceQuality {
139 pub fn parse(value: &str) -> Option<Self> {
140 match value {
141 "low" => Some(Self::Low),
142 "medium" => Some(Self::Medium),
143 "high" => Some(Self::High),
144 "lossless" => Some(Self::Lossless),
145 _ => None,
146 }
147 }
148
149 fn av1_speed(self) -> u8 {
151 match self {
152 Self::Low => 10,
153 Self::Medium => 10,
154 Self::High => 8,
155 Self::Lossless => 6,
156 }
157 }
158
159 fn av1_quantizer(self) -> usize {
161 match self {
162 Self::Low => 180,
163 Self::Medium => 120,
164 Self::High => 80,
165 Self::Lossless => 40,
166 }
167 }
168
169 fn av1_min_quantizer(self) -> u8 {
171 match self {
172 Self::Low => 120,
173 Self::Medium => 80,
174 Self::High => 40,
175 Self::Lossless => 0,
176 }
177 }
178}
179
180pub struct SurfaceEncoder {
181 width: u32,
183 height: u32,
184 source_width: u32,
186 source_height: u32,
187 kind: SurfaceEncoderKind,
188}
189
190enum SurfaceEncoderKind {
191 H264Software(Box<SoftwareH264Encoder>),
192 NvencH264(Box<crate::nvenc_encode::NvencDirectEncoder>),
193 NvencH265(Box<crate::nvenc_encode::NvencDirectEncoder>),
194 NvencAV1(Box<crate::nvenc_encode::NvencDirectEncoder>),
195 #[cfg(unix)]
196 H264Vaapi(Box<crate::vaapi_encode::VaapiDirectEncoder>),
197 #[cfg(unix)]
198 H265Vaapi(Box<crate::vaapi_encode::VaapiHevcEncoder>),
199 AV1Software(Box<SoftwareAV1Encoder>),
200}
201
202impl SurfaceEncoder {
203 pub fn new(
207 preferences: &[SurfaceEncoderPreference],
208 width: u32,
209 height: u32,
210 vaapi_device: &str,
211 quality: SurfaceQuality,
212 verbose: bool,
213 codec_support: u8,
214 ) -> Result<Self, String> {
215 let source_width = width;
216 let source_height = height;
217 let mut last_err = String::from("no encoders configured");
218
219 for &pref in preferences {
220 if !pref.supported_by_client(codec_support) {
221 continue;
222 }
223 match Self::try_one(
224 pref,
225 width,
226 height,
227 source_width,
228 source_height,
229 vaapi_device,
230 quality,
231 ) {
232 Ok(enc) => {
233 if verbose {
234 eprintln!(
235 "[surface-encoder] using {:?} for {source_width}x{source_height}",
236 pref
237 );
238 }
239 return Ok(enc);
240 }
241 Err(err) => {
242 if verbose {
243 eprintln!(
244 "[surface-encoder] {:?} unavailable for {source_width}x{source_height}: {err}",
245 pref
246 );
247 }
248 last_err = err;
249 }
250 }
251 }
252 Err(last_err)
253 }
254
255 fn try_one(
256 pref: SurfaceEncoderPreference,
257 width: u32,
258 height: u32,
259 source_width: u32,
260 source_height: u32,
261 vaapi_device: &str,
262 quality: SurfaceQuality,
263 ) -> Result<Self, String> {
264 let _ = vaapi_device;
265 validate_surface_dimensions(width, height, pref)?;
266
267 match pref {
268 SurfaceEncoderPreference::NvencH264 => {
269 let (width, height) = ((width + 1) & !1, (height + 1) & !1);
270 Ok(Self {
271 width,
272 height,
273 source_width,
274 source_height,
275 kind: SurfaceEncoderKind::NvencH264(Box::new(
276 crate::nvenc_encode::NvencDirectEncoder::try_new("h264", width, height)?,
277 )),
278 })
279 }
280 SurfaceEncoderPreference::NvencH265 => {
281 let (width, height) = ((width + 1) & !1, (height + 1) & !1);
282 Ok(Self {
283 width,
284 height,
285 source_width,
286 source_height,
287 kind: SurfaceEncoderKind::NvencH265(Box::new(
288 crate::nvenc_encode::NvencDirectEncoder::try_new("h265", width, height)?,
289 )),
290 })
291 }
292 SurfaceEncoderPreference::NvencAV1 => Ok(Self {
293 width,
294 height,
295 source_width,
296 source_height,
297 kind: SurfaceEncoderKind::NvencAV1(Box::new(
298 crate::nvenc_encode::NvencDirectEncoder::try_new("av1", width, height)?,
299 )),
300 }),
301 #[cfg(unix)]
302 SurfaceEncoderPreference::H264Vaapi => {
303 let (width, height) = ((width + 1) & !1, (height + 1) & !1);
304 Ok(Self {
305 width,
306 height,
307 source_width,
308 source_height,
309 kind: SurfaceEncoderKind::H264Vaapi(Box::new(
310 crate::vaapi_encode::VaapiDirectEncoder::try_new(
311 width,
312 height,
313 vaapi_device,
314 )?,
315 )),
316 })
317 }
318 #[cfg(unix)]
319 SurfaceEncoderPreference::H265Vaapi => {
320 let (width, height) = ((width + 1) & !1, (height + 1) & !1);
321 Ok(Self {
322 width,
323 height,
324 source_width,
325 source_height,
326 kind: SurfaceEncoderKind::H265Vaapi(Box::new(
327 crate::vaapi_encode::VaapiHevcEncoder::try_new(
328 width,
329 height,
330 vaapi_device,
331 )?,
332 )),
333 })
334 }
335 #[cfg(not(unix))]
336 SurfaceEncoderPreference::H264Vaapi | SurfaceEncoderPreference::H265Vaapi => {
337 Err("VA-API is only available on Unix".into())
338 }
339 SurfaceEncoderPreference::AV1 => Ok(Self {
340 width,
341 height,
342 source_width,
343 source_height,
344 kind: SurfaceEncoderKind::AV1Software(Box::new(SoftwareAV1Encoder::new(
345 width, height, quality,
346 )?)),
347 }),
348 SurfaceEncoderPreference::H264Software => {
349 let (width, height) = ((width + 1) & !1, (height + 1) & !1);
350 Ok(Self {
351 width,
352 height,
353 source_width,
354 source_height,
355 kind: SurfaceEncoderKind::H264Software(Box::new(SoftwareH264Encoder::new()?)),
356 })
357 }
358 }
359 }
360
361 #[allow(dead_code)]
362 pub fn dimensions(&self) -> (u32, u32) {
363 (self.width, self.height)
364 }
365
366 pub fn source_dimensions(&self) -> (u32, u32) {
368 (self.source_width, self.source_height)
369 }
370
371 #[allow(dead_code)]
372 pub fn kind_name(&self) -> &'static str {
373 match &self.kind {
374 SurfaceEncoderKind::H264Software(_) => "h264-software",
375 SurfaceEncoderKind::NvencH264(_) => "nvenc-h264",
376 SurfaceEncoderKind::NvencH265(_) => "nvenc-h265",
377 SurfaceEncoderKind::NvencAV1(_) => "nvenc-av1",
378 #[cfg(unix)]
379 SurfaceEncoderKind::H264Vaapi(_) => "h264-vaapi",
380 #[cfg(unix)]
381 SurfaceEncoderKind::H265Vaapi(_) => "h265-vaapi",
382 SurfaceEncoderKind::AV1Software(_) => "av1-software",
383 }
384 }
385
386 pub fn codec_flag(&self) -> u8 {
387 match &self.kind {
388 SurfaceEncoderKind::H264Software(_) => SURFACE_FRAME_CODEC_H264,
389 #[cfg(unix)]
390 SurfaceEncoderKind::H264Vaapi(_) => SURFACE_FRAME_CODEC_H264,
391 #[cfg(unix)]
392 SurfaceEncoderKind::H265Vaapi(_) => SURFACE_FRAME_CODEC_H265,
393 SurfaceEncoderKind::NvencH264(enc)
394 | SurfaceEncoderKind::NvencH265(enc)
395 | SurfaceEncoderKind::NvencAV1(enc) => enc.codec_flag(),
396 SurfaceEncoderKind::AV1Software(_) => SURFACE_FRAME_CODEC_AV1,
397 }
398 }
399
400 pub fn request_keyframe(&mut self) {
401 match &mut self.kind {
402 SurfaceEncoderKind::H264Software(enc) => enc.request_keyframe(),
403 SurfaceEncoderKind::NvencH264(enc)
404 | SurfaceEncoderKind::NvencH265(enc)
405 | SurfaceEncoderKind::NvencAV1(enc) => enc.request_keyframe(),
406 #[cfg(unix)]
407 SurfaceEncoderKind::H264Vaapi(enc) => enc.request_keyframe(),
408 #[cfg(unix)]
409 SurfaceEncoderKind::H265Vaapi(enc) => enc.request_keyframe(),
410 SurfaceEncoderKind::AV1Software(enc) => enc.request_keyframe(),
411 }
412 }
413
414 pub fn encode(&mut self, rgba: &[u8]) -> Option<(Vec<u8>, bool)> {
415 let enc_len = expected_rgba_len(self.width, self.height);
416 let enc_len = match enc_len {
417 Some(v) => v,
418 None => {
419 eprintln!(
420 "[surface-encoder] expected_rgba_len overflow {}x{}",
421 self.width, self.height
422 );
423 return None;
424 }
425 };
426 let rgba = if rgba.len() == enc_len {
427 std::borrow::Cow::Borrowed(rgba)
428 } else {
429 let total_px = rgba.len() / 4;
433 if total_px == 0 {
434 return None;
435 }
436 let src_w = [self.width as usize, (self.width - 1) as usize]
438 .into_iter()
439 .find(|&w| w > 0 && total_px.is_multiple_of(w))?;
440 let src_h = total_px / src_w;
441 if src_h == 0 {
442 return None;
443 }
444 let dst_w = self.width as usize;
445 let dst_h = self.height as usize;
446 let mut padded = vec![0u8; enc_len];
447 for row in 0..dst_h {
448 let src_row = row.min(src_h - 1);
449 for col in 0..dst_w {
450 let src_col = col.min(src_w - 1);
451 let si = (src_row * src_w + src_col) * 4;
452 let di = (row * dst_w + col) * 4;
453 padded[di..di + 4].copy_from_slice(&rgba[si..si + 4]);
454 }
455 }
456 std::borrow::Cow::Owned(padded)
457 };
458
459 match &mut self.kind {
460 SurfaceEncoderKind::H264Software(encoder) => {
461 encoder.encode(&rgba, self.width, self.height)
462 }
463 SurfaceEncoderKind::NvencH264(enc)
464 | SurfaceEncoderKind::NvencH265(enc)
465 | SurfaceEncoderKind::NvencAV1(enc) => {
466 let mut bgra = rgba.into_owned();
467 for px in bgra.chunks_exact_mut(4) {
468 px.swap(0, 2);
469 }
470 enc.encode_bgra(&bgra)
471 }
472 #[cfg(unix)]
473 SurfaceEncoderKind::H264Vaapi(enc) => {
474 let mut bgra = rgba.into_owned();
475 for px in bgra.chunks_exact_mut(4) {
476 px.swap(0, 2);
477 }
478 let (sw, sh) = (self.source_width as usize, self.source_height as usize);
479 enc.encode_bgra_padded(&bgra, sw, sh)
480 }
481 #[cfg(unix)]
482 SurfaceEncoderKind::H265Vaapi(enc) => {
483 let mut bgra = rgba.into_owned();
484 for px in bgra.chunks_exact_mut(4) {
485 px.swap(0, 2);
486 }
487 let (sw, sh) = (self.source_width as usize, self.source_height as usize);
488 enc.encode_bgra_padded(&bgra, sw, sh)
489 }
490 SurfaceEncoderKind::AV1Software(encoder) => encoder.encode(&rgba),
491 }
492 }
493
494 #[cfg(test)]
498 fn encode_keyframe(&mut self, rgba: &[u8]) -> Option<(Vec<u8>, bool)> {
499 self.request_keyframe();
500 let first = self.encode(rgba);
501 if first.is_some() {
502 return first;
503 }
504 self.flush_keyframe(rgba)
506 }
507
508 fn flush_keyframe(&mut self, rgba: &[u8]) -> Option<(Vec<u8>, bool)> {
510 match &mut self.kind {
511 SurfaceEncoderKind::AV1Software(enc) => enc.flush_encode(rgba),
512 _ => self.encode(rgba),
513 }
514 }
515
516 pub fn encode_pixels(&mut self, pixels: &PixelData) -> Option<(Vec<u8>, bool)> {
519 match pixels {
520 PixelData::Nv12 {
521 data,
522 y_stride,
523 uv_stride,
524 } => self.encode_nv12(data, *y_stride, *uv_stride),
525 PixelData::Bgra(bgra) => self.encode_bgra(bgra),
526 PixelData::Rgba(rgba) => self.encode(rgba),
527 #[cfg(unix)]
528 PixelData::DmaBuf {
529 fd,
530 fourcc,
531 modifier,
532 stride,
533 offset,
534 } => self.encode_dmabuf(fd, *fourcc, *modifier, *stride, *offset),
535 #[cfg(not(unix))]
536 PixelData::DmaBuf { .. } => None,
537 }
538 }
539
540 pub fn encode_keyframe_pixels(&mut self, pixels: &PixelData) -> Option<(Vec<u8>, bool)> {
542 self.request_keyframe();
543 let first = self.encode_pixels(pixels);
544 if first.is_some() {
545 return first;
546 }
547 match pixels {
549 PixelData::Rgba(rgba) => self.flush_keyframe(rgba),
550 PixelData::Bgra(bgra) => self.flush_keyframe_bgra(bgra),
551 PixelData::Nv12 {
552 data,
553 y_stride,
554 uv_stride,
555 } => self.flush_keyframe_nv12(data, *y_stride, *uv_stride),
556 #[cfg(unix)]
557 PixelData::DmaBuf {
558 fd,
559 fourcc,
560 modifier,
561 stride,
562 offset,
563 } => {
564 self.encode_dmabuf(fd, *fourcc, *modifier, *stride, *offset)
566 }
567 #[cfg(not(unix))]
568 PixelData::DmaBuf { .. } => None,
569 }
570 }
571
572 #[cfg(unix)]
575 fn encode_dmabuf(
576 &mut self,
577 fd: &std::os::fd::OwnedFd,
578 fourcc: u32,
579 _modifier: u64,
580 stride: u32,
581 offset: u32,
582 ) -> Option<(Vec<u8>, bool)> {
583 self.encode_dmabuf_cpu_fallback(fd, fourcc, stride, offset)
590 }
591
592 #[cfg(unix)]
595 fn encode_dmabuf_cpu_fallback(
596 &mut self,
597 fd: &std::os::fd::OwnedFd,
598 fourcc: u32,
599 stride: u32,
600 _offset: u32,
601 ) -> Option<(Vec<u8>, bool)> {
602 use std::os::fd::AsRawFd;
603
604 let w = self.source_width as usize;
605 let h = self.source_height as usize;
606 let stride = stride as usize;
607 let raw_fd = fd.as_raw_fd();
608
609 let file_size = unsafe { libc::lseek(raw_fd, 0, libc::SEEK_END) };
611 if file_size <= 0 {
612 return None;
613 }
614 let map_len = file_size as usize;
615
616 #[repr(C)]
618 struct DmaBufSync {
619 flags: u64,
620 }
621 const DMA_BUF_SYNC_READ: u64 = 1;
622 const DMA_BUF_SYNC_START: u64 = 0;
623 const DMA_BUF_SYNC_END: u64 = 4;
624 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
628
629 let sync_start = DmaBufSync {
630 flags: DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ,
631 };
632 unsafe {
633 libc::ioctl(raw_fd, DMA_BUF_IOCTL_SYNC as _, &sync_start);
634 }
635
636 let ptr = unsafe {
638 libc::mmap(
639 std::ptr::null_mut(),
640 map_len,
641 libc::PROT_READ,
642 libc::MAP_SHARED,
643 raw_fd,
644 0,
645 )
646 };
647 if ptr == libc::MAP_FAILED {
648 let sync_end = DmaBufSync {
649 flags: DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ,
650 };
651 unsafe {
652 libc::ioctl(raw_fd, DMA_BUF_IOCTL_SYNC as _, &sync_end);
653 }
654 return None;
655 }
656 let plane_data = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_len) };
657
658 let result = if fourcc == blit_compositor::drm_fourcc::ARGB8888
659 || fourcc == blit_compositor::drm_fourcc::XRGB8888
660 {
661 if stride == w * 4 && map_len >= w * h * 4 {
663 self.encode_bgra(&plane_data[..w * h * 4])
664 } else {
665 let mut packed = Vec::with_capacity(w * h * 4);
667 for row in 0..h {
668 let start = row * stride;
669 let end = start + w * 4;
670 if end <= plane_data.len() {
671 packed.extend_from_slice(&plane_data[start..end]);
672 }
673 }
674 self.encode_bgra(&packed)
675 }
676 } else if fourcc == blit_compositor::drm_fourcc::ABGR8888
677 || fourcc == blit_compositor::drm_fourcc::XBGR8888
678 {
679 if stride == w * 4 && map_len >= w * h * 4 {
681 self.encode(&plane_data[..w * h * 4])
682 } else {
683 let mut packed = Vec::with_capacity(w * h * 4);
684 for row in 0..h {
685 let start = row * stride;
686 let end = start + w * 4;
687 if end <= plane_data.len() {
688 packed.extend_from_slice(&plane_data[start..end]);
689 }
690 }
691 self.encode(&packed)
692 }
693 } else {
694 None
695 };
696
697 unsafe {
699 libc::munmap(ptr, map_len);
700 }
701 let sync_end = DmaBufSync {
702 flags: DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ,
703 };
704 unsafe {
705 libc::ioctl(raw_fd, DMA_BUF_IOCTL_SYNC as _, &sync_end);
706 }
707
708 result
709 }
710
711 fn encode_bgra(&mut self, bgra: &[u8]) -> Option<(Vec<u8>, bool)> {
713 let enc_w = self.width as usize;
714 let enc_h = self.height as usize;
715 let src_w = self.source_width as usize;
716 let src_h = self.source_height as usize;
717
718 let mut result = match &mut self.kind {
719 SurfaceEncoderKind::H264Software(encoder) => {
720 let yuv = bgra_to_yuv420_padded(bgra, src_w, src_h, enc_w, enc_h);
721 let yuv_buf = YUVBuffer::from_vec(yuv, enc_w, enc_h);
722 encoder.encode_yuv(&yuv_buf, self.width, self.height)
723 }
724 SurfaceEncoderKind::NvencH264(enc)
725 | SurfaceEncoderKind::NvencH265(enc)
726 | SurfaceEncoderKind::NvencAV1(enc) => enc.encode_bgra_padded(bgra, src_w, src_h),
727 #[cfg(unix)]
728 SurfaceEncoderKind::H264Vaapi(enc) => enc.encode_bgra_padded(bgra, src_w, src_h),
729 #[cfg(unix)]
730 SurfaceEncoderKind::H265Vaapi(enc) => enc.encode_bgra_padded(bgra, src_w, src_h),
731 SurfaceEncoderKind::AV1Software(encoder) => {
732 let yuv = bgra_to_yuv420_padded(bgra, src_w, src_h, enc_w, enc_h);
733 encoder.encode_yuv_planes(&yuv)
734 }
735 };
736 if let Some((ref data, ref mut is_key)) = result
740 && !*is_key
741 {
742 *is_key = match &self.kind {
743 SurfaceEncoderKind::NvencH264(_) => h264_stream_contains_idr(data),
744 SurfaceEncoderKind::NvencH265(_) => h265_stream_contains_idr(data),
745 #[cfg(unix)]
746 SurfaceEncoderKind::H264Vaapi(_) => h264_stream_contains_idr(data),
747 #[cfg(unix)]
748 SurfaceEncoderKind::H265Vaapi(_) => h265_stream_contains_idr(data),
749 _ => false,
750 };
751 }
752 result
753 }
754
755 fn encode_nv12(
758 &mut self,
759 data: &[u8],
760 y_stride: usize,
761 uv_stride: usize,
762 ) -> Option<(Vec<u8>, bool)> {
763 let src_w = self.source_width as usize;
765 let src_h = self.source_height as usize;
766
767 match &mut self.kind {
768 SurfaceEncoderKind::H264Software(encoder) => {
769 let enc_w = self.width as usize;
770 let enc_h = self.height as usize;
771 if enc_w == src_w && enc_h == src_h {
772 let yuv = nv12_to_yuv420(data, y_stride, uv_stride, src_w, src_h);
773 let yuv_buf = YUVBuffer::from_vec(yuv, enc_w, enc_h);
774 encoder.encode_yuv(&yuv_buf, self.width, self.height)
775 } else {
776 let pd = PixelData::Nv12 {
777 data: std::sync::Arc::new(data.to_vec()),
778 y_stride,
779 uv_stride,
780 };
781 let rgba = pd.to_rgba(self.source_width, self.source_height);
782 self.encode(&rgba)
783 }
784 }
785 SurfaceEncoderKind::NvencH264(_)
786 | SurfaceEncoderKind::NvencH265(_)
787 | SurfaceEncoderKind::NvencAV1(_) => {
788 let pd = PixelData::Nv12 {
790 data: std::sync::Arc::new(data.to_vec()),
791 y_stride,
792 uv_stride,
793 };
794 let rgba = pd.to_rgba(self.source_width, self.source_height);
795 self.encode(&rgba)
796 }
797 #[cfg(unix)]
798 SurfaceEncoderKind::H264Vaapi(enc) => {
799 let uv_offset = y_stride * src_h;
800 let y_data = &data[..uv_offset];
801 let uv_data = &data[uv_offset..];
802 let mut r = enc.encode_nv12(y_data, uv_data, y_stride, uv_stride);
803 if let Some((ref d, ref mut k)) = r
804 && !*k
805 {
806 *k = h264_stream_contains_idr(d);
807 }
808 r
809 }
810 #[cfg(unix)]
811 SurfaceEncoderKind::H265Vaapi(enc) => {
812 let uv_offset = y_stride * src_h;
813 let y_data = &data[..uv_offset];
814 let uv_data = &data[uv_offset..];
815 let mut r = enc.encode_nv12(y_data, uv_data, y_stride, uv_stride);
816 if let Some((ref d, ref mut k)) = r
817 && !*k
818 {
819 *k = h265_stream_contains_idr(d);
820 }
821 r
822 }
823 SurfaceEncoderKind::AV1Software(encoder) => {
824 encoder.encode_nv12(data, y_stride, uv_stride, src_w, src_h)
825 }
826 }
827 }
828
829 fn flush_keyframe_bgra(&mut self, bgra: &[u8]) -> Option<(Vec<u8>, bool)> {
830 match &mut self.kind {
831 SurfaceEncoderKind::AV1Software(enc) => enc.flush_encode_bgra(bgra),
832 _ => self.encode_bgra(bgra),
833 }
834 }
835
836 fn flush_keyframe_nv12(
837 &mut self,
838 data: &[u8],
839 y_stride: usize,
840 uv_stride: usize,
841 ) -> Option<(Vec<u8>, bool)> {
842 match &mut self.kind {
843 SurfaceEncoderKind::AV1Software(enc) => {
844 let w = self.width as usize;
845 let h = self.height as usize;
846 enc.flush_encode_nv12(data, y_stride, uv_stride, w, h)
847 }
848 _ => self.encode_nv12(data, y_stride, uv_stride),
849 }
850 }
851}
852
853fn validate_surface_dimensions(
854 width: u32,
855 height: u32,
856 _preference: SurfaceEncoderPreference,
857) -> Result<(), String> {
858 if width == 0 || height == 0 {
859 return Err("surface encoder requires non-zero dimensions".into());
860 }
861 let _ = expected_rgba_len(width, height)
864 .ok_or_else(|| format!("surface encoder dimensions overflow for {width}x{height}"))?;
865 Ok(())
866}
867
868fn expected_rgba_len(width: u32, height: u32) -> Option<usize> {
869 (width as usize)
870 .checked_mul(height as usize)?
871 .checked_mul(4)
872}
873
874#[inline(always)]
880fn rgb_to_y(r: i32, g: i32, b: i32) -> u8 {
881 ((66 * r + 129 * g + 25 * b + 128) >> 8)
882 .wrapping_add(16)
883 .clamp(0, 255) as u8
884}
885
886#[inline(always)]
887fn rgb_to_u(r: i32, g: i32, b: i32) -> u8 {
888 ((-38 * r - 74 * g + 112 * b + 128) >> 8)
889 .wrapping_add(128)
890 .clamp(0, 255) as u8
891}
892
893#[inline(always)]
894fn rgb_to_v(r: i32, g: i32, b: i32) -> u8 {
895 ((112 * r - 94 * g - 18 * b + 128) >> 8)
896 .wrapping_add(128)
897 .clamp(0, 255) as u8
898}
899
900#[inline(always)]
909fn compute_y_plane(
910 src: &[u8],
911 width: usize,
912 height: usize,
913 y_plane: &mut [u8],
914 r_off: usize,
915 g_off: usize,
916 b_off: usize,
917) {
918 let total = width * height;
919 for (px, y_out) in y_plane[..total].iter_mut().enumerate() {
920 let i = px * 4;
921 let r = src[i + r_off] as i32;
922 let g = src[i + g_off] as i32;
923 let b = src[i + b_off] as i32;
924 *y_out = rgb_to_y(r, g, b);
925 }
926}
927
928#[inline(always)]
930fn compute_uv_planes(
931 src: &[u8],
932 width: usize,
933 height: usize,
934 u_plane: &mut [u8],
935 v_plane: &mut [u8],
936 r_off: usize,
937 g_off: usize,
938 b_off: usize,
939) {
940 let chroma_w = width / 2;
941 let chroma_h = height / 2;
942 for cy in 0..chroma_h {
943 for cx in 0..chroma_w {
944 let row = cy * 2;
945 let col = cx * 2;
946 let mut u_sum = 0i32;
948 let mut v_sum = 0i32;
949 for dy in 0..2u32 {
950 for dx in 0..2u32 {
951 let i = ((row + dy as usize) * width + col + dx as usize) * 4;
952 let r = src[i + r_off] as i32;
953 let g = src[i + g_off] as i32;
954 let b = src[i + b_off] as i32;
955 u_sum += rgb_to_u(r, g, b) as i32;
956 v_sum += rgb_to_v(r, g, b) as i32;
957 }
958 }
959 let idx = cy * chroma_w + cx;
960 u_plane[idx] = (u_sum / 4) as u8;
961 v_plane[idx] = (v_sum / 4) as u8;
962 }
963 }
964}
965
966#[inline(always)]
969fn compute_y_plane_padded(
970 src: &[u8],
971 src_w: usize,
972 src_h: usize,
973 enc_w: usize,
974 enc_h: usize,
975 y_plane: &mut [u8],
976 r_off: usize,
977 g_off: usize,
978 b_off: usize,
979) {
980 for row in 0..enc_h {
981 let sr = row.min(src_h - 1);
982 for col in 0..enc_w {
983 let sc = col.min(src_w - 1);
984 let i = (sr * src_w + sc) * 4;
985 let r = src[i + r_off] as i32;
986 let g = src[i + g_off] as i32;
987 let b = src[i + b_off] as i32;
988 y_plane[row * enc_w + col] = rgb_to_y(r, g, b);
989 }
990 }
991}
992
993#[inline(always)]
996fn compute_uv_planes_padded(
997 src: &[u8],
998 src_w: usize,
999 src_h: usize,
1000 enc_w: usize,
1001 enc_h: usize,
1002 u_plane: &mut [u8],
1003 v_plane: &mut [u8],
1004 r_off: usize,
1005 g_off: usize,
1006 b_off: usize,
1007) {
1008 let chroma_w = enc_w / 2;
1009 let chroma_h = enc_h / 2;
1010 for cy in 0..chroma_h {
1011 for cx in 0..chroma_w {
1012 let row = cy * 2;
1013 let col = cx * 2;
1014 let mut u_sum = 0i32;
1015 let mut v_sum = 0i32;
1016 for dy in 0..2u32 {
1017 for dx in 0..2u32 {
1018 let sr = (row + dy as usize).min(src_h - 1);
1019 let sc = (col + dx as usize).min(src_w - 1);
1020 let i = (sr * src_w + sc) * 4;
1021 let r = src[i + r_off] as i32;
1022 let g = src[i + g_off] as i32;
1023 let b = src[i + b_off] as i32;
1024 u_sum += rgb_to_u(r, g, b) as i32;
1025 v_sum += rgb_to_v(r, g, b) as i32;
1026 }
1027 }
1028 let idx = cy * chroma_w + cx;
1029 u_plane[idx] = (u_sum / 4) as u8;
1030 v_plane[idx] = (v_sum / 4) as u8;
1031 }
1032 }
1033}
1034
1035fn bgra_to_yuv420(bgra: &[u8], width: usize, height: usize) -> Vec<u8> {
1037 bgra_to_yuv420_padded(bgra, width, height, width, height)
1038}
1039
1040fn bgra_to_yuv420_padded(
1044 bgra: &[u8],
1045 src_w: usize,
1046 src_h: usize,
1047 enc_w: usize,
1048 enc_h: usize,
1049) -> Vec<u8> {
1050 let y_size = enc_w * enc_h;
1051 let uv_w = enc_w / 2;
1052 let uv_size = uv_w * (enc_h / 2);
1053 let mut yuv = vec![0u8; y_size + uv_size * 2];
1054 let (y_plane, uv) = yuv.split_at_mut(y_size);
1055 let (u_plane, v_plane) = uv.split_at_mut(uv_size);
1056 compute_y_plane_padded(bgra, src_w, src_h, enc_w, enc_h, y_plane, 2, 1, 0);
1058 compute_uv_planes_padded(bgra, src_w, src_h, enc_w, enc_h, u_plane, v_plane, 2, 1, 0);
1059 yuv
1060}
1061
1062fn rgba_to_yuv420(rgba: &[u8], width: usize, height: usize) -> Vec<u8> {
1064 let y_size = width * height;
1065 let uv_w = width / 2;
1066 let uv_size = uv_w * (height / 2);
1067 let mut yuv = vec![0u8; y_size + uv_size * 2];
1068 let (y_plane, uv) = yuv.split_at_mut(y_size);
1069 let (u_plane, v_plane) = uv.split_at_mut(uv_size);
1070 compute_y_plane(rgba, width, height, y_plane, 0, 1, 2);
1072 compute_uv_planes(rgba, width, height, u_plane, v_plane, 0, 1, 2);
1073 yuv
1074}
1075
1076#[cfg(test)]
1078fn bgra_to_rgba(bgra: &[u8]) -> Vec<u8> {
1079 let mut rgba = vec![0u8; bgra.len()];
1080 for (src, dst) in bgra.chunks_exact(4).zip(rgba.chunks_exact_mut(4)) {
1081 dst[0] = src[2];
1082 dst[1] = src[1];
1083 dst[2] = src[0];
1084 dst[3] = src[3];
1085 }
1086 rgba
1087}
1088
1089#[cfg(test)]
1091#[inline(always)]
1092fn write_nv12_y_plane_packed(
1093 src: &[u8],
1094 width: usize,
1095 height: usize,
1096 y_plane: &mut [u8],
1097 y_stride: usize,
1098 r_off: usize,
1099 g_off: usize,
1100 b_off: usize,
1101) -> Option<()> {
1102 if y_plane.len() < y_stride.checked_mul(height)? {
1103 return None;
1104 }
1105 for row in 0..height {
1106 let dst_start = row * y_stride;
1107 let src_row = row * width * 4;
1108 for col in 0..width {
1109 let i = src_row + col * 4;
1110 let r = src[i + r_off] as i32;
1111 let g = src[i + g_off] as i32;
1112 let b = src[i + b_off] as i32;
1113 y_plane[dst_start + col] = rgb_to_y(r, g, b);
1114 }
1115 }
1116 Some(())
1117}
1118
1119#[cfg(test)]
1121#[inline(always)]
1122fn write_nv12_uv_plane_packed(
1123 src: &[u8],
1124 width: usize,
1125 height: usize,
1126 uv_plane: &mut [u8],
1127 uv_stride: usize,
1128 r_off: usize,
1129 g_off: usize,
1130 b_off: usize,
1131) -> Option<()> {
1132 if uv_plane.len() < uv_stride.checked_mul(height / 2)? {
1133 return None;
1134 }
1135 let chroma_w = (width / 2) * 2;
1136 let chroma_h = (height / 2) * 2;
1137 for row in (0..chroma_h).step_by(2) {
1138 let dst_start = (row / 2) * uv_stride;
1139 for col in (0..chroma_w).step_by(2) {
1140 let mut u_sum = 0i32;
1141 let mut v_sum = 0i32;
1142 for dy in 0..2usize {
1143 for dx in 0..2usize {
1144 let i = ((row + dy) * width + col + dx) * 4;
1145 let r = src[i + r_off] as i32;
1146 let g = src[i + g_off] as i32;
1147 let b = src[i + b_off] as i32;
1148 u_sum += rgb_to_u(r, g, b) as i32;
1149 v_sum += rgb_to_v(r, g, b) as i32;
1150 }
1151 }
1152 uv_plane[dst_start + col] = (u_sum / 4) as u8;
1153 uv_plane[dst_start + col + 1] = (v_sum / 4) as u8;
1154 }
1155 }
1156 Some(())
1157}
1158
1159fn nv12_to_yuv420(
1163 data: &[u8],
1164 y_stride: usize,
1165 uv_stride: usize,
1166 width: usize,
1167 height: usize,
1168) -> Vec<u8> {
1169 let y_size = width * height;
1170 let uv_w = width / 2;
1171 let uv_h = height / 2;
1172 let uv_size = uv_w * uv_h;
1173 let mut yuv = vec![0u8; y_size + uv_size * 2];
1174 let (y_out, uv_out) = yuv.split_at_mut(y_size);
1175 let (u_out, v_out) = uv_out.split_at_mut(uv_size);
1176
1177 let uv_offset = y_stride * height;
1178
1179 for row in 0..height {
1181 let src = row * y_stride;
1182 let dst = row * width;
1183 y_out[dst..dst + width].copy_from_slice(&data[src..src + width]);
1184 }
1185
1186 for row in 0..uv_h {
1188 let src_start = uv_offset + row * uv_stride;
1189 let dst_start = row * uv_w;
1190 for col in 0..uv_w {
1191 u_out[dst_start + col] = data[src_start + col * 2];
1192 v_out[dst_start + col] = data[src_start + col * 2 + 1];
1193 }
1194 }
1195
1196 yuv
1197}
1198
1199#[cfg(test)]
1200fn write_nv12_y_plane(
1201 rgba: &[u8],
1202 width: usize,
1203 height: usize,
1204 y_plane: &mut [u8],
1205 y_stride: usize,
1206) -> Option<()> {
1207 write_nv12_y_plane_packed(rgba, width, height, y_plane, y_stride, 0, 1, 2)
1208}
1209
1210#[cfg(test)]
1211fn write_nv12_uv_plane(
1212 rgba: &[u8],
1213 width: usize,
1214 height: usize,
1215 uv_plane: &mut [u8],
1216 uv_stride: usize,
1217) -> Option<()> {
1218 write_nv12_uv_plane_packed(rgba, width, height, uv_plane, uv_stride, 0, 1, 2)
1219}
1220
1221#[cfg(test)]
1222fn rgba_to_nv12(
1223 rgba: &[u8],
1224 width: usize,
1225 height: usize,
1226 y_plane: &mut [u8],
1227 y_stride: usize,
1228 uv_plane: &mut [u8],
1229 uv_stride: usize,
1230) -> Option<()> {
1231 write_nv12_y_plane(rgba, width, height, y_plane, y_stride)?;
1232 write_nv12_uv_plane(rgba, width, height, uv_plane, uv_stride)?;
1233 Some(())
1234}
1235
1236fn h264_stream_contains_idr(data: &[u8]) -> bool {
1238 annex_b_contains_nal(data, |byte| (byte & 0x1f) == 5)
1239}
1240
1241fn h265_stream_contains_idr(data: &[u8]) -> bool {
1243 annex_b_contains_nal(data, |byte| {
1244 let nal_type = (byte >> 1) & 0x3f;
1245 nal_type == 19 || nal_type == 20 })
1247}
1248
1249fn annex_b_contains_nal(data: &[u8], pred: impl Fn(u8) -> bool) -> bool {
1251 let mut i = 0usize;
1252 while i < data.len() {
1253 let start_code_len = if data[i..].starts_with(&[0, 0, 0, 1]) {
1254 4
1255 } else if data[i..].starts_with(&[0, 0, 1]) {
1256 3
1257 } else {
1258 i += 1;
1259 continue;
1260 };
1261
1262 let nal_header = i + start_code_len;
1263 if let Some(&byte) = data.get(nal_header)
1264 && pred(byte)
1265 {
1266 return true;
1267 }
1268
1269 i = nal_header.saturating_add(1);
1270 }
1271
1272 false
1273}
1274
1275struct SoftwareH264Encoder {
1276 encoder: OpenH264Encoder,
1277}
1278
1279impl SoftwareH264Encoder {
1280 fn new() -> Result<Self, String> {
1281 let encoder = OpenH264Encoder::new()
1282 .map_err(|err| format!("failed to create OpenH264 encoder: {err:?}"))?;
1283 Ok(Self { encoder })
1284 }
1285
1286 fn request_keyframe(&mut self) {
1287 self.encoder.force_intra_frame();
1288 }
1289
1290 fn encode(&mut self, rgba: &[u8], width: u32, height: u32) -> Option<(Vec<u8>, bool)> {
1291 let yuv = rgba_to_yuv420(rgba, width as usize, height as usize);
1292 let yuv_buf = YUVBuffer::from_vec(yuv, width as usize, height as usize);
1293 self.encode_yuv(&yuv_buf, width, height)
1294 }
1295
1296 fn encode_yuv(
1298 &mut self,
1299 yuv_buf: &YUVBuffer,
1300 width: u32,
1301 height: u32,
1302 ) -> Option<(Vec<u8>, bool)> {
1303 let bitstream = match self.encoder.encode(yuv_buf) {
1304 Ok(bs) => bs,
1305 Err(e) => {
1306 eprintln!("[surface-encoder] openh264 encode failed {width}x{height}: {e:?}");
1307 return None;
1308 }
1309 };
1310 let nal_data = bitstream.to_vec();
1311 if nal_data.is_empty() {
1312 eprintln!("[surface-encoder] openh264 produced empty NAL {width}x{height}");
1313 return None;
1314 }
1315 let is_keyframe = h264_stream_contains_idr(&nal_data);
1316 Some((nal_data, is_keyframe))
1317 }
1318}
1319
1320struct SoftwareAV1Encoder {
1325 ctx: rav1e::Context<u8>,
1326 width: usize,
1327 height: usize,
1328 force_keyframe: bool,
1329}
1330
1331impl SoftwareAV1Encoder {
1332 fn new(width: u32, height: u32, quality: SurfaceQuality) -> Result<Self, String> {
1333 use rav1e::prelude::*;
1334
1335 let mut speed = SpeedSettings::from_preset(quality.av1_speed());
1336 speed.rdo_lookahead_frames = 1;
1337 let enc = EncoderConfig {
1338 width: width as usize,
1339 height: height as usize,
1340 chroma_sampling: ChromaSampling::Cs420,
1341 chroma_sample_position: ChromaSamplePosition::Unknown,
1342 speed_settings: speed,
1343 low_latency: true,
1344 min_key_frame_interval: 0,
1345 max_key_frame_interval: 60,
1346 quantizer: quality.av1_quantizer(),
1347 min_quantizer: quality.av1_min_quantizer(),
1348 bitrate: 0,
1349 ..Default::default()
1350 };
1351 let cfg = Config::new().with_encoder_config(enc);
1352 let ctx = cfg
1353 .new_context()
1354 .map_err(|e| format!("rav1e context creation failed: {e}"))?;
1355 Ok(Self {
1356 ctx,
1357 width: width as usize,
1358 height: height as usize,
1359 force_keyframe: false,
1360 })
1361 }
1362
1363 fn request_keyframe(&mut self) {
1364 self.force_keyframe = true;
1365 }
1366
1367 fn encode(&mut self, rgba: &[u8]) -> Option<(Vec<u8>, bool)> {
1368 let yuv = rgba_to_yuv420(rgba, self.width, self.height);
1369 self.encode_yuv_planes(&yuv)
1370 }
1371
1372 #[allow(dead_code)]
1373 fn encode_bgra(&mut self, bgra: &[u8]) -> Option<(Vec<u8>, bool)> {
1374 let yuv = bgra_to_yuv420(bgra, self.width, self.height);
1375 self.encode_yuv_planes(&yuv)
1376 }
1377
1378 fn encode_nv12(
1379 &mut self,
1380 data: &[u8],
1381 y_stride: usize,
1382 uv_stride: usize,
1383 width: usize,
1384 height: usize,
1385 ) -> Option<(Vec<u8>, bool)> {
1386 let yuv = nv12_to_yuv420(data, y_stride, uv_stride, width, height);
1387 self.encode_yuv_planes(&yuv)
1388 }
1389
1390 fn encode_yuv_planes(&mut self, yuv: &[u8]) -> Option<(Vec<u8>, bool)> {
1392 let width = self.width;
1393 let height = self.height;
1394 let y_size = width * height;
1395 let uv_w = width.div_ceil(2);
1396 let uv_h = height.div_ceil(2);
1397 let uv_size = uv_w * uv_h;
1398
1399 let y_plane = &yuv[..y_size];
1400 let u_plane = &yuv[y_size..y_size + uv_size];
1401 let v_plane = &yuv[y_size + uv_size..];
1402
1403 let mut frame = self.ctx.new_frame();
1404 frame.planes[0].copy_from_raw_u8(y_plane, width, 1);
1405 frame.planes[1].copy_from_raw_u8(u_plane, uv_w, 1);
1406 frame.planes[2].copy_from_raw_u8(v_plane, uv_w, 1);
1407
1408 self.send_and_receive(frame)
1409 }
1410
1411 fn send_and_receive(&mut self, frame: rav1e::Frame<u8>) -> Option<(Vec<u8>, bool)> {
1412 use rav1e::prelude::*;
1413
1414 if self.force_keyframe {
1415 let params = FrameParameters {
1416 frame_type_override: FrameTypeOverride::Key,
1417 ..Default::default()
1418 };
1419 if self.ctx.send_frame((frame, params)).is_ok() {
1420 self.force_keyframe = false;
1421 }
1422 } else {
1423 let _ = self.ctx.send_frame(frame);
1424 }
1425
1426 match self.ctx.receive_packet() {
1427 Ok(packet) => {
1428 let is_key = packet.frame_type == rav1e::prelude::FrameType::KEY;
1429 Some((packet.data, is_key))
1430 }
1431 Err(rav1e::EncoderStatus::Encoded) | Err(rav1e::EncoderStatus::NeedMoreData) => None,
1432 Err(_) => None,
1433 }
1434 }
1435
1436 fn flush_encode(&mut self, rgba: &[u8]) -> Option<(Vec<u8>, bool)> {
1438 let yuv = rgba_to_yuv420(rgba, self.width, self.height);
1439 self.flush_yuv_planes(&yuv)
1440 }
1441
1442 fn flush_encode_bgra(&mut self, bgra: &[u8]) -> Option<(Vec<u8>, bool)> {
1443 let yuv = bgra_to_yuv420(bgra, self.width, self.height);
1444 self.flush_yuv_planes(&yuv)
1445 }
1446
1447 fn flush_encode_nv12(
1448 &mut self,
1449 data: &[u8],
1450 y_stride: usize,
1451 uv_stride: usize,
1452 width: usize,
1453 height: usize,
1454 ) -> Option<(Vec<u8>, bool)> {
1455 let yuv = nv12_to_yuv420(data, y_stride, uv_stride, width, height);
1456 self.flush_yuv_planes(&yuv)
1457 }
1458
1459 fn flush_yuv_planes(&mut self, yuv: &[u8]) -> Option<(Vec<u8>, bool)> {
1460 use rav1e::prelude::*;
1461
1462 let width = self.width;
1463 let height = self.height;
1464 let y_size = width * height;
1465 let uv_w = width.div_ceil(2);
1466 let uv_h = height.div_ceil(2);
1467 let uv_size = uv_w * uv_h;
1468
1469 let y_plane = &yuv[..y_size];
1470 let u_plane = &yuv[y_size..y_size + uv_size];
1471 let v_plane = &yuv[y_size + uv_size..];
1472
1473 let mut frame = self.ctx.new_frame();
1474 frame.planes[0].copy_from_raw_u8(y_plane, width, 1);
1475 frame.planes[1].copy_from_raw_u8(u_plane, uv_w, 1);
1476 frame.planes[2].copy_from_raw_u8(v_plane, uv_w, 1);
1477
1478 let params = FrameParameters {
1479 frame_type_override: FrameTypeOverride::Key,
1480 ..Default::default()
1481 };
1482 if self.ctx.send_frame((frame, params)).is_err() {
1483 return None;
1484 }
1485 self.ctx.flush();
1486
1487 loop {
1488 match self.ctx.receive_packet() {
1489 Ok(packet) => {
1490 let is_key = packet.frame_type == FrameType::KEY;
1491 return Some((packet.data, is_key));
1492 }
1493 Err(rav1e::EncoderStatus::NeedMoreData) | Err(rav1e::EncoderStatus::Encoded) => {
1494 continue;
1495 }
1496 Err(rav1e::EncoderStatus::LimitReached) => return None,
1497 Err(_) => return None,
1498 }
1499 }
1500 }
1501}