1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::frame::Frame;
use crate::order::output::{ChannelLayout, OutputStream, SampleFormat};
use crate::order::parameters::ParameterValue;
use crate::packet::Packet;
use crate::tools;
use ffmpeg_sys::*;
use std::collections::HashMap;
use std::ptr::null_mut;

#[derive(Debug)]
pub struct AudioEncoder {
  pub identifier: String,
  pub stream_index: isize,
  pub codec_context: *mut AVCodecContext,
  pub codec: *mut AVCodec,
}

impl AudioEncoder {
  pub fn new(
    identifier: String,
    stream_index: isize,
    output_stream: &OutputStream,
  ) -> Result<Self, String> {
    unsafe {
      let codec = tools::get_codec(&output_stream.codec);
      if codec.is_null() {
        return Err(format!("Unable to found codec {}", output_stream.codec));
      }
      let mut codec_context = avcodec_alloc_context3(codec);

      let parameters = &output_stream.parameters;
      if let Some(ParameterValue::Rational(data)) = parameters.get("sample_rate") {
        (*codec_context).time_base = data.clone().invert().into();
        (*codec_context).sample_rate = data.num / data.den;
      }

      if let Some(ParameterValue::String(data)) = parameters.get("sample_fmt") {
        let sample_fmt: SampleFormat = data.parse().unwrap();
        (*codec_context).sample_fmt = sample_fmt.into();
      }

      (*codec_context).channel_layout = AudioEncoder::select_channel_layout(codec, parameters);
      (*codec_context).channels =
        av_get_channel_layout_nb_channels((*codec_context).channel_layout);

      check_result!(avcodec_open2(codec_context, codec, null_mut()), {
        avcodec_free_context(&mut codec_context);
      });

      Ok(AudioEncoder {
        identifier,
        stream_index,
        codec_context,
        codec,
      })
    }
  }

  pub fn encode(&self, frame: &Frame, packet: &Packet) -> Result<bool, String> {
    unsafe {
      check_result!(avcodec_send_frame(self.codec_context, frame.frame));
      let ret = avcodec_receive_packet(self.codec_context, packet.packet as *mut _);

      if ret == AVERROR(EAGAIN) || ret == AVERROR_EOF {
        let mut data = [0; AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, data.as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
        trace!("{}", tools::to_string(data.as_ptr()));
        return Ok(false);
      }

      check_result!(ret);

      trace!(
        "received encoded packet with {} bytes",
        (*packet.packet).size
      );
      Ok(true)
    }
  }

  fn select_channel_layout(
    codec: *mut AVCodec,
    parameters: &HashMap<String, ParameterValue>,
  ) -> u64 {
    unsafe {
      if codec.is_null() || (*codec).channel_layouts.is_null() {
        if let Some(ParameterValue::String(data)) = parameters.get("channel_layout") {
          let layout: ChannelLayout = data.parse().unwrap();
          layout.into()
        } else {
          AV_CH_LAYOUT_STEREO
        }
      } else {
        AV_CH_LAYOUT_STEREO
      }
    }
  }
}

impl Drop for AudioEncoder {
  fn drop(&mut self) {
    unsafe {
      if !self.codec_context.is_null() {
        avcodec_close(self.codec_context);
        avcodec_free_context(&mut self.codec_context);
      }
    }
  }
}