1use super::{owned::*, AVResult};
2use crate::ffi::{AVCodecID::*, AVFieldOrder::*, AVMediaType::*, AVPixelFormat::*, *};
3use std::convert::TryInto;
4use std::fmt::Debug;
5use std::ops::Deref;
6use std::path::{Path, PathBuf};
7use std::time::{Duration, Instant};
8
9pub trait MediaDesc {
11 fn codec_id(&self) -> AVCodecID {
13 Default::default()
14 }
15
16 fn as_audio_desc(&self) -> Option<&AudioDesc> {
18 None
19 }
20
21 fn as_video_desc(&self) -> Option<&VideoDesc> {
23 None
24 }
25}
26
27impl Debug for &dyn MediaDesc {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "MediaDesc {{ codec_id: {:?} }}", self.codec_id())
30 }
31}
32
33impl Debug for Box<dyn MediaDesc> {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "MediaDesc {{ codec_id: {:?} }}", self.codec_id())
36 }
37}
38
39pub trait Writer {
41 fn write_header(&mut self) -> AVResult<()>;
43
44 fn write_bytes(
52 &mut self,
53 bytes: &[u8],
54 pts: i64,
55 duration: i64,
56 is_key_frame: bool,
57 stream_index: usize,
58 ) -> AVResult<()>;
59
60 fn write_trailer(&mut self) -> AVResult<()>;
62
63 fn close(&mut self);
65
66 fn flush(&mut self);
68
69 fn size(&self) -> u64;
71}
72
73impl Debug for &dyn Writer {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "Writer @ 0x{:p}", self)
76 }
77}
78
79impl Debug for Box<dyn Writer> {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "Writer @ 0x{:p}", self)
82 }
83}
84
85#[derive(Copy, Clone, Debug, Default)]
87pub struct AudioDesc {
88 pub codec_id: AVCodecID,
89 pub sample_fmt: AVSampleFormat,
90 pub bit_rate: i64,
91 pub sample_rate: usize,
92 pub channels: usize,
93}
94
95impl MediaDesc for AudioDesc {
96 fn codec_id(&self) -> AVCodecID {
97 self.codec_id
98 }
99}
100
101impl AudioDesc {
102 pub fn new() -> Self {
103 Default::default()
104 }
105}
106
107#[derive(Copy, Clone, Debug, Default)]
109pub struct VideoDesc {
110 pub codec_id: AVCodecID,
111 pub width: i32,
112 pub height: i32,
113 pub bit_rate: i64,
114 pub time_base: AVRational,
115 pub gop_size: i32,
116 pub pix_fmt: AVPixelFormat,
117}
118
119impl MediaDesc for VideoDesc {
120 fn codec_id(&self) -> AVCodecID {
121 self.codec_id
122 }
123 fn as_video_desc(&self) -> Option<&VideoDesc> {
124 Some(self)
125 }
126}
127
128impl VideoDesc {
129 pub fn new() -> Self {
130 Default::default()
131 }
132
133 pub fn with_h264(width: i32, height: i32, bit_rate: i64, time_unit: i32) -> Self {
134 Self {
135 codec_id: AV_CODEC_ID_H264,
136 width,
137 height,
138 bit_rate,
139 time_base: AVRational::with_normalize(time_unit),
140 gop_size: 12,
141 pix_fmt: AV_PIX_FMT_YUV420P,
142 }
143 }
144
145 pub fn with_h265(width: i32, height: i32, bit_rate: i64, time_unit: i32) -> Self {
146 Self {
147 codec_id: AV_CODEC_ID_HEVC,
148 width,
149 height,
150 bit_rate,
151 time_base: AVRational::with_normalize(time_unit),
152 gop_size: 12,
153 pix_fmt: AV_PIX_FMT_YUV420P,
154 }
155 }
156}
157
158#[derive(Debug)]
160pub struct Stream {
161 stream: AVStreamOwned,
162 in_time_base: AVRational,
163}
164
165#[derive(Debug)]
167pub struct SimpleWriter {
168 ctx: AVFormatContextOwned,
169 format_options: String,
170 streams: Vec<Stream>,
171 header_writed: bool,
172 trailer_writed: bool,
173}
174
175impl Drop for SimpleWriter {
176 fn drop(&mut self) {
177 self.close();
178 }
179}
180
181impl Writer for SimpleWriter {
182 fn write_header(&mut self) -> AVResult<()> {
184 Ok(())
185 }
186
187 fn write_bytes(
195 &mut self,
196 bytes: &[u8],
197 pts: i64,
198 duration: i64,
199 is_key_frame: bool,
200 stream_index: usize,
201 ) -> AVResult<()> {
202 if !self.header_writed {
203 self.ctx.write_header(Some(&self.format_options))?;
204 self.header_writed = true;
205 }
206 unsafe {
207 let stm = self.streams.get(stream_index).unwrap();
208 let in_time_base = stm.in_time_base;
209 let out_time_base = stm.stream.time_base;
210 let mut pkt = AVPacket::default();
211 let pts = av_rescale_q_rnd(
212 pts,
213 in_time_base,
214 out_time_base,
215 AVRounding::new().near_inf().pass_min_max(),
216 );
217 pkt.pts = pts;
218 pkt.dts = pts;
219 pkt.data = bytes.as_ptr() as *mut u8;
220 pkt.size = bytes.len().try_into()?;
221 pkt.stream_index = stream_index.try_into()?;
222 pkt.flags = if is_key_frame { AV_PKT_FLAG_KEY } else { 0 };
223 pkt.duration = av_rescale_q(duration, in_time_base, out_time_base);
224 pkt.pos = -1;
225 self.ctx.write_frame_interleaved(&mut pkt)?;
226 self.ctx.flush();
227 Ok(())
228 }
229 }
230
231 fn write_trailer(&mut self) -> AVResult<()> {
233 if self.header_writed && !self.trailer_writed {
234 self.ctx.write_trailer()?;
235 self.trailer_writed = true;
236 self.flush();
237 }
238 Ok(())
239 }
240
241 fn close(&mut self) {
243 self.write_trailer().unwrap();
244 self.ctx.flush();
245 }
246
247 fn flush(&mut self) {
249 self.ctx.flush();
250 }
251
252 fn size(&self) -> u64 {
254 self.ctx.size()
255 }
256}
257
258impl SimpleWriter {
259 pub fn new<P>(
266 path: P,
267 descs: &[&dyn MediaDesc],
268 format: Option<&str>,
269 format_options: Option<&str>,
270 ) -> AVResult<Self>
271 where
272 P: AsRef<Path> + Sized,
273 {
274 let mut ctx = AVFormatContextOwned::with_output(path, format, None)?;
275 let mut streams: Vec<Stream> = vec![];
276 for desc in descs {
277 let codec_id = desc.codec_id();
278 match codec_id {
279 AV_CODEC_ID_H264 | AV_CODEC_ID_HEVC => {
280 let desc = desc.as_video_desc().unwrap();
281 let mut st = ctx.new_stream(codec_id)?;
282 if let Some(par) = st.codecpar_mut() {
284 par.codec_type = AVMEDIA_TYPE_VIDEO;
285 par.codec_id = codec_id;
286 par.bit_rate = desc.bit_rate;
287 par.width = desc.width;
288 par.height = desc.height;
289 par.field_order = AV_FIELD_UNKNOWN;
290 par.sample_aspect_ratio = AVRational::new(0, 1);
291 par.profile = FF_PROFILE_UNKNOWN;
292 par.level = FF_LEVEL_UNKNOWN;
293 }
294 streams.push(Stream {
295 stream: st,
296 in_time_base: desc.time_base,
297 });
298 }
299 _ => {}
300 }
301 }
302 Ok(Self {
303 ctx,
304 format_options: format_options.unwrap_or("").to_owned(),
305 streams,
306 header_writed: false,
307 trailer_writed: false,
308 })
309 }
310}
311
312pub type FormatLocationCallback = dyn Fn(usize) -> String;
316
317pub type SplitNotifier = dyn Fn(usize);
321
322#[derive(Default)]
324pub struct SplitOptions {
325 output_path: Option<PathBuf>,
326 format_location: Option<Box<FormatLocationCallback>>,
327 before_split: Option<Box<SplitNotifier>>,
328 after_split: Option<Box<SplitNotifier>>,
329 max_files: Option<usize>,
330 max_size_bytes: Option<u64>,
331 max_size_time: Option<u64>,
332 max_overhead: Option<f32>,
333 split_at_keyframe: Option<bool>,
334 start_index: Option<usize>,
335}
336
337impl Debug for SplitOptions {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339 f.debug_struct("SplitOptions")
340 .field("output_path", &self.output_path)
341 .field("max_files", &self.max_files)
342 .field("max_size_bytes", &self.max_size_bytes)
343 .field("max_size_time", &self.max_size_time)
344 .field("max_overhead", &self.max_overhead)
345 .field("split_at_keyframe", &self.split_at_keyframe)
346 .field("start_index", &self.start_index)
347 .finish()
348 }
349}
350
351pub struct SplitWriter {
353 medias: Vec<Box<dyn MediaDesc>>,
355 format: Option<String>,
357 format_options: Option<String>,
359 writer: Option<Box<dyn Writer>>,
361 output_path: PathBuf,
363 format_location: Option<Box<FormatLocationCallback>>,
365 before_split: Option<Box<SplitNotifier>>,
367 after_split: Option<Box<SplitNotifier>>,
369 max_files: usize,
372 max_size_bytes: u64,
374 max_size_time: u64,
376 max_overhead: f32,
378 split_at_keyframe: bool,
380 start_index: usize,
382 current_index: usize,
384 start_time: Instant,
386 started: bool,
388 need_key_frame: bool,
390 split_wait_for_key_frame: bool,
391}
392
393impl Debug for SplitWriter {
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395 write!(f, "SplitWriter @ 0x{:p}", self)
396 }
397}
398
399impl Writer for SplitWriter {
400 fn write_header(&mut self) -> AVResult<()> {
401 if let Some(writer) = &mut self.writer {
402 writer.write_header()
403 } else {
404 Err("The underly writer does not ready".into())
405 }
406 }
407
408 fn write_bytes(
409 &mut self,
410 bytes: &[u8],
411 pts: i64,
412 duration: i64,
413 is_key_frame: bool,
414 stream_index: usize,
415 ) -> AVResult<()> {
416 if self.can_split_now(is_key_frame, stream_index) {
417 self.split_now();
418 }
419
420 if self.writer.is_none() {
421 let writer = SimpleWriter::new(
422 self.format_location(self.current_index).to_str().unwrap(),
423 &self
424 .medias
425 .iter()
426 .map(Deref::deref)
427 .collect::<Vec<&dyn MediaDesc>>(),
428 self.format.as_deref(),
429 self.format_options.as_deref(),
430 )?;
431 self.writer = Some(Box::new(writer));
432 self.start_time = Instant::now();
433 self.started = true;
434 }
435
436 if let Some(ref mut writer) = self.writer {
437 writer.write_bytes(bytes, pts, duration, is_key_frame, stream_index)?;
438 }
439
440 Ok(())
441 }
442
443 fn write_trailer(&mut self) -> AVResult<()> {
444 if let Some(writer) = &mut self.writer {
445 writer.write_trailer()
446 } else {
447 Err("The underly writer does not ready".into())
448 }
449 }
450
451 fn close(&mut self) {
452 if let Some(writer) = &mut self.writer {
453 writer.close();
454 }
455 }
456
457 fn flush(&mut self) {
458 if let Some(writer) = &mut self.writer {
459 writer.flush();
460 }
461 }
462
463 fn size(&self) -> u64 {
464 if let Some(writer) = &self.writer {
465 writer.size()
466 } else {
467 0
468 }
469 }
470}
471
472impl SplitWriter {
473 pub fn new(
482 descs: Vec<Box<dyn MediaDesc>>,
483 format: Option<&str>,
484 format_options: Option<&str>,
485 split_options: SplitOptions,
486 ) -> AVResult<Self> {
487 let mut need_key_frame = false;
488 for d in descs.iter() {
489 if d.codec_id().has_gop() {
490 need_key_frame = true;
491 }
492 }
493 Ok(Self {
494 medias: descs,
495 format: format.map(String::from),
496 format_options: format_options.map(String::from),
497 writer: None,
498 output_path: split_options.output_path.unwrap(),
499 format_location: split_options.format_location,
500 before_split: split_options.before_split,
501 after_split: split_options.after_split,
502 max_files: split_options.max_files.unwrap_or(0),
503 max_size_bytes: split_options.max_size_bytes.unwrap_or(0),
504 max_size_time: split_options.max_size_time.unwrap_or(0),
505 max_overhead: split_options.max_overhead.unwrap_or(0.1f32),
506 split_at_keyframe: split_options.split_at_keyframe.unwrap_or(true),
507 start_index: split_options.start_index.unwrap_or(0),
508 current_index: split_options.start_index.unwrap_or(0),
509 start_time: Instant::now(),
510 started: false,
511 need_key_frame,
512 split_wait_for_key_frame: false,
513 })
514 }
515
516 pub(crate) fn is_bytes_overrun(&mut self) -> bool {
518 let mut exceeded = false;
519 if let Some(ref writer) = self.writer {
520 if self.max_size_bytes > 0 && writer.size() >= self.max_size_bytes {
521 exceeded = true
522 }
523 }
524 exceeded
525 }
526
527 pub(crate) fn is_bytes_overflow(&mut self) -> bool {
529 let mut exceeded = false;
530 if let Some(ref writer) = self.writer {
531 let overhead_bytes = self.max_size_bytes * (self.max_overhead * 100.0) as u64 / 100;
532 if self.max_size_bytes > 0 && writer.size() >= self.max_size_bytes + overhead_bytes {
533 exceeded = true
534 }
535 }
536 exceeded
537 }
538
539 pub(crate) fn is_time_overrun(&mut self) -> bool {
541 self.max_size_time > 0
542 && self.start_time.elapsed() >= Duration::from_nanos(self.max_size_time)
543 }
544
545 pub(crate) fn is_time_overflow(&mut self) -> bool {
547 let overhead_time = self.max_size_time * (self.max_overhead * 100.0) as u64 / 100;
548 self.max_size_time > 0
549 && self.start_time.elapsed() >= Duration::from_nanos(self.max_size_time + overhead_time)
550 }
551
552 pub fn can_split_now(&mut self, is_key_frame: bool, stream_index: usize) -> bool {
554 let mut split_now: bool = false;
555 if self.split_wait_for_key_frame {
556 split_now = self.stream_has_key_frame(stream_index) && is_key_frame;
557 self.split_wait_for_key_frame = false;
558 } else {
559 let overrun = self.is_bytes_overrun() || self.is_time_overrun();
560 if overrun && self.split_at_keyframe && self.need_key_frame {
561 self.split_wait_for_key_frame = true;
562 } else {
563 split_now = overrun;
564 }
565 }
566 let overflow = self.is_bytes_overflow() || self.is_time_overflow();
567 split_now || overflow
568 }
569
570 pub fn clean_files(&self) {
572 if self.max_files > 0 && (self.current_index - self.start_index) >= self.max_files - 1 {
573 let index = self.current_index - (self.max_files - 1);
574 if index >= self.start_index {
575 let old_file = self.format_location(index);
576 std::fs::remove_file(old_file).unwrap();
577 }
578 }
579 }
580
581 pub fn ext_of_format(format: Option<&str>) -> &'static str {
583 format
584 .map(|s| match s {
585 "mp4" => ".mp4",
586 "mpegts" => ".ts",
587 _ => "dat",
588 })
589 .unwrap_or("dat")
590 }
591
592 pub fn format_location(&self, index: usize) -> PathBuf {
594 let loc = if let Some(ref cb) = self.format_location {
595 cb(index)
596 } else {
597 format!(
598 "MED{:06}{}",
599 index,
600 Self::ext_of_format(self.format.as_deref())
601 )
602 };
603 let path = self.output_path.join(loc);
604 if let Some(parent) = path.parent() {
605 std::fs::create_dir_all(parent).unwrap();
606 }
607 path
608 }
609
610 pub fn split_now(&mut self) {
612 if let Some(ref cb) = self.before_split {
613 cb(self.current_index);
614 }
615 let _ = self.writer.take();
616 self.clean_files();
617 self.current_index += 1;
618 if let Some(ref cb) = self.after_split {
619 cb(self.current_index);
620 }
621 }
622
623 pub fn stream_has_key_frame(&self, stream_index: usize) -> bool {
625 self.medias[stream_index].codec_id().has_gop()
626 }
627}
628
629#[derive(Default)]
631pub struct OpenOptions {
632 medias: Vec<Box<dyn MediaDesc>>,
633 format: Option<String>,
634 format_options: Option<String>,
635 format_location: Option<Box<FormatLocationCallback>>,
636 before_split: Option<Box<SplitNotifier>>,
637 after_split: Option<Box<SplitNotifier>>,
638 max_files: Option<usize>,
639 max_size_bytes: Option<u64>,
640 max_size_time: Option<u64>,
641 max_overhead: Option<f32>,
642 split_at_keyframe: Option<bool>,
643 start_index: Option<usize>,
644}
645
646impl Debug for OpenOptions {
647 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
648 write!(f, "OpenOptions @ 0x{:p}", self)
649 }
650}
651
652impl OpenOptions {
653 pub fn new() -> Self {
655 Default::default()
656 }
657
658 pub fn media<T>(mut self, media: T) -> Self
660 where
661 T: MediaDesc + Sized + 'static,
662 {
663 self.medias.push(Box::new(media));
664 self
665 }
666
667 pub fn format<S>(mut self, format: S) -> Self
669 where
670 S: Into<String>,
671 {
672 self.format = Some(format.into());
673 self
674 }
675
676 pub fn format_options<S>(mut self, format_options: S) -> Self
678 where
679 S: Into<String>,
680 {
681 self.format_options = Some(format_options.into());
682 self
683 }
684
685 pub fn format_location<F>(mut self, format_location: F) -> Self
687 where
688 F: Fn(usize) -> String + 'static,
689 {
690 self.format_location = Some(Box::new(format_location));
691 self
692 }
693
694 pub fn before_split<F>(mut self, before_split: F) -> Self
696 where
697 F: Fn(usize) + 'static,
698 {
699 self.before_split = Some(Box::new(before_split));
700 self
701 }
702
703 pub fn after_split<F>(mut self, after_split: F) -> Self
705 where
706 F: Fn(usize) + 'static,
707 {
708 self.after_split = Some(Box::new(after_split));
709 self
710 }
711
712 pub fn max_files(mut self, max_files: usize) -> Self {
714 self.max_files = Some(max_files);
715 self
716 }
717
718 pub fn max_size_bytes(mut self, max_size_bytes: u64) -> Self {
720 self.max_size_bytes = Some(max_size_bytes);
721 self
722 }
723
724 pub fn max_size_time(mut self, max_size_time: u64) -> Self {
726 self.max_size_time = Some(max_size_time);
727 self
728 }
729
730 pub fn max_overhead(mut self, max_overhead: f32) -> Self {
732 self.max_overhead = Some(max_overhead);
733 self
734 }
735
736 pub fn split_at_keyframe(mut self, split_at_keyframe: bool) -> Self {
740 self.split_at_keyframe = Some(split_at_keyframe);
741 self
742 }
743
744 pub fn start_index(mut self, start_index: usize) -> Self {
746 self.start_index = Some(start_index);
747 self
748 }
749
750 pub fn open<P>(self, path: P) -> AVResult<Box<dyn Writer>>
752 where
753 P: AsRef<Path> + Sized,
754 {
755 if self.format_location.is_some() || self.max_files.is_some() {
756 let split_options = SplitOptions {
757 output_path: Some(AsRef::<Path>::as_ref(&path).to_path_buf()),
758 format_location: self.format_location,
759 before_split: self.before_split,
760 after_split: self.after_split,
761 max_files: self.max_files,
762 max_size_bytes: self.max_size_bytes,
763 max_size_time: self.max_size_time,
764 max_overhead: self.max_overhead,
765 split_at_keyframe: self.split_at_keyframe,
766 start_index: self.start_index,
767 };
768 let writer = SplitWriter::new(
769 self.medias,
770 self.format.as_deref(),
771 self.format_options.as_deref(),
772 split_options,
773 )?;
774 Ok(Box::new(writer))
775 } else {
776 let medias: Vec<&dyn MediaDesc> = self.medias.iter().map(Deref::deref).collect();
777 let writer = SimpleWriter::new(
778 path,
779 &medias[..],
780 self.format.as_deref(),
781 self.format_options.as_deref(),
782 )?;
783 Ok(Box::new(writer))
784 }
785 }
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791
792 #[test]
793 fn test_simple_writer() {
794 let a_desc = AudioDesc::new();
795 let v_desc = VideoDesc::with_h264(352, 288, 4000, 1000000);
796 let example_bytes = include_bytes!("../../examples/envivio-352x288.264.framed");
797 for _ in 0..100 {
798 let mut mp4_writer = SimpleWriter::new(
799 "/tmp/envivio-352x288.264.mp4",
800 &[&a_desc, &v_desc],
801 None,
802 Some("movflags=frag_keyframe"),
803 )
804 .unwrap();
805 let mut ts_writer = SimpleWriter::new(
806 "/tmp/envivio-352x288.264.ts",
807 &[&a_desc, &v_desc],
808 Some("mpegts"),
809 Some("mpegts_copyts=1"),
810 )
811 .unwrap();
812 let mut offset: usize = 0;
813 let mut pts = 0;
814 while offset + 4 < example_bytes.len() {
815 let size_bytes = &example_bytes[offset..offset + 4];
816 let frame_size = i32::from_be_bytes(size_bytes.try_into().unwrap()) as usize;
817 offset += 4;
818 let frame_bytes = &example_bytes[offset..offset + frame_size];
819 offset += frame_size;
820 mp4_writer
821 .write_bytes(frame_bytes, pts, 40000, false, 0)
822 .unwrap();
823 ts_writer
824 .write_bytes(frame_bytes, pts, 40000, false, 0)
825 .unwrap();
826 pts += 40000;
827 }
828 }
829 }
830}