• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nomalab / stainless-ffmpeg / #109

31 Oct 2025 01:09PM UTC coverage: 65.406% (+0.04%) from 65.369%
#109

push

1764 of 2697 relevant lines covered (65.41%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.39
/src/video_decoder.rs
1
use crate::{
2
  format_context::FormatContext,
3
  frame::Frame,
4
  packet::Packet,
5
  tools::{self, rational::Rational},
6
};
7
use ffmpeg_sys_next::*;
8
use std::{ffi::CString, ptr::null_mut};
9

10
#[derive(Debug)]
11
pub struct VideoDecoder {
12
  pub identifier: String,
13
  pub stream_index: isize,
14
  pub codec_context: *mut AVCodecContext,
15
}
16

17
impl VideoDecoder {
18
  pub fn new(
1✔
19
    identifier: String,
20
    format: &FormatContext,
21
    stream_index: isize,
22
  ) -> Result<Self, String> {
23
    unsafe {
24
      let codec = avcodec_find_decoder(format.get_codec_id(stream_index));
2✔
25
      let mut codec_context = avcodec_alloc_context3(codec);
1✔
26

27
      check_result!(
×
28
        avcodec_parameters_to_context(
29
          codec_context,
30
          (**(*format.format_context).streams.offset(stream_index)).codecpar
31
        ),
32
        {
33
          avcodec_free_context(&mut codec_context);
34
        }
35
      );
36
      check_result!(avcodec_open2(codec_context, codec, null_mut()), {
2✔
37
        avcodec_free_context(&mut codec_context);
38
      });
39

40
      (*codec_context).time_base =
2✔
41
        (**(*format.format_context).streams.offset(stream_index)).time_base;
2✔
42

43
      Ok(VideoDecoder {
1✔
44
        identifier,
1✔
45
        stream_index,
46
        codec_context,
1✔
47
      })
48
    }
49
  }
50

51
  pub fn new_with_codec(
×
52
    identifier: String,
53
    codec_name: &str,
54
    width: i32,
55
    height: i32,
56
    stream_index: isize,
57
  ) -> Result<Self, String> {
58
    unsafe {
59
      let cn = CString::new(codec_name).unwrap();
×
60
      let codec = avcodec_find_decoder_by_name(cn.as_ptr());
×
61
      let mut codec_context = avcodec_alloc_context3(codec);
×
62

63
      (*codec_context).width = width;
×
64
      (*codec_context).height = height;
×
65
      check_result!(avcodec_open2(codec_context, codec, null_mut()), {
×
66
        avcodec_free_context(&mut codec_context);
67
      });
68

69
      Ok(VideoDecoder {
×
70
        identifier,
×
71
        stream_index,
72
        codec_context,
×
73
      })
74
    }
75
  }
76

77
  pub fn get_width(&self) -> i32 {
1✔
78
    unsafe { (*self.codec_context).width }
1✔
79
  }
80

81
  pub fn get_height(&self) -> i32 {
1✔
82
    unsafe { (*self.codec_context).height }
1✔
83
  }
84

85
  pub fn get_time_base(&self) -> Rational {
1✔
86
    unsafe {
87
      Rational {
88
        num: (*self.codec_context).time_base.num,
1✔
89
        den: (*self.codec_context).time_base.den,
2✔
90
      }
91
    }
92
  }
93

94
  pub fn get_frame_rate(&self) -> Rational {
×
95
    unsafe {
96
      Rational {
97
        num: (*self.codec_context).framerate.num,
×
98
        den: (*self.codec_context).framerate.den,
×
99
      }
100
    }
101
  }
102

103
  pub fn get_aspect_ratio(&self) -> Rational {
1✔
104
    unsafe {
105
      Rational {
106
        num: (*self.codec_context).sample_aspect_ratio.num,
1✔
107
        den: (*self.codec_context).sample_aspect_ratio.den,
2✔
108
      }
109
    }
110
  }
111

112
  pub fn get_pix_fmt_name(&self) -> String {
1✔
113
    unsafe {
114
      let input_fmt_str = av_get_pix_fmt_name((*self.codec_context).pix_fmt);
1✔
115
      tools::to_string(input_fmt_str)
1✔
116
    }
117
  }
118

119
  pub fn decode(&self, packet: &Packet) -> Result<Frame, String> {
1✔
120
    if packet.get_stream_index() != self.stream_index {
1✔
121
      return Err("bad stream".to_string());
×
122
    }
123
    unsafe {
124
      check_result!(avcodec_send_packet(self.codec_context, packet.packet));
1✔
125

126
      let frame = av_frame_alloc();
1✔
127

128
      check_result!(avcodec_receive_frame(self.codec_context, frame));
1✔
129

130
      Ok(Frame {
1✔
131
        frame,
132
        name: Some(self.identifier.clone()),
1✔
133
        index: self.stream_index as usize,
1✔
134
      })
135
    }
136
  }
137
}
138

139
impl Drop for VideoDecoder {
140
  fn drop(&mut self) {
1✔
141
    unsafe {
142
      if !self.codec_context.is_null() {
1✔
143
        avcodec_close(self.codec_context);
1✔
144
        avcodec_free_context(&mut self.codec_context);
1✔
145
      }
146
    }
147
  }
148
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc