~ubuntu-branches/ubuntu/wily/mkvtoolnix/wily

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
   mkvmerge -- utility for splicing together matroska files
   from component media subtypes

   Distributed under the GPL v2
   see the file COPYING for details
   or visit http://www.gnu.org/copyleft/gpl.html

   PCM output module

   Written by Moritz Bunkus <moritz@bunkus.org>.
   Patches by Robert Millan <rmh@aybabtu.com>.
*/

#include "common/common_pch.h"

#include "common/codec.h"
#include "merge/connection_checks.h"
#include "merge/output_control.h"
#include "output/p_pcm.h"

using namespace libmatroska;

pcm_packetizer_c::pcm_packetizer_c(generic_reader_c *p_reader,
                                   track_info_c &p_ti,
                                   int samples_per_sec,
                                   int channels,
                                   int bits_per_sample,
                                   pcm_format_e format)
  : generic_packetizer_c(p_reader, p_ti)
  , m_samples_per_sec(samples_per_sec)
  , m_channels(channels)
  , m_bits_per_sample(bits_per_sample)
  , m_previous_timecode{-1}
  , m_packet_size(0)
  , m_min_packet_size{static_cast<size_t>(samples_to_size(samples_per_sec * 4) / 1000)} // Minimum: 4ms of samples if we should pass it through unmodified
  , m_samples_output(0)
  , m_num_durations_provided{}
  , m_format{format}
  , m_s2tc(1000000000ll, m_samples_per_sec)
{

  int i;
  for (i = 32; 2 < i; i >>= 2)
    if ((m_samples_per_sec % i) == 0)
      break;

  if ((2 == i) && ((m_samples_per_sec % 5) == 0))
    i = 5;

  m_samples_per_packet = samples_per_sec / i;
  m_packet_size        = samples_to_size(m_samples_per_packet);

  set_track_type(track_audio);
  set_track_default_duration((int64_t)(1000000000.0 * m_samples_per_packet / m_samples_per_sec));
}

pcm_packetizer_c::~pcm_packetizer_c() {
}

void
pcm_packetizer_c::set_headers() {
  auto codec_id = ieee_float         == m_format ? MKV_A_PCM_FLOAT
                : big_endian_integer == m_format ? MKV_A_PCM_BE
                :                                  MKV_A_PCM;
  set_codec_id(codec_id);
  set_audio_sampling_freq(static_cast<double>(m_samples_per_sec));
  set_audio_channels(m_channels);
  set_audio_bit_depth(m_bits_per_sample);

  generic_packetizer_c::set_headers();
}

int64_t
pcm_packetizer_c::samples_to_size(int64_t samples)
  const {
  return (samples * m_channels * m_bits_per_sample) / 8;
}

int64_t
pcm_packetizer_c::size_to_samples(int64_t size)
  const {
  return (size * 8) / (m_channels * m_bits_per_sample);
}

int
pcm_packetizer_c::process(packet_cptr packet) {
  if (packet->has_timecode() && (packet->data->get_size() >= m_min_packet_size))
    return process_packaged(packet);

  m_buffer.add(packet->data->get_buffer(), packet->data->get_size());

  while (m_buffer.get_size() >= m_packet_size) {
    add_packet(new packet_t(memory_c::clone(m_buffer.get_buffer(), m_packet_size), m_samples_output * m_s2tc, m_samples_per_packet * m_s2tc));

    m_buffer.remove(m_packet_size);
    m_samples_output += m_samples_per_packet;
  }

  return FILE_STATUS_MOREDATA;
}

int
pcm_packetizer_c::process_packaged(packet_cptr const &packet) {
  auto samples_here = size_to_samples(packet->data->get_size());
  packet->duration  = samples_here * m_s2tc;

  ++m_num_durations_provided;

  if (16 > m_num_durations_provided) {
    ++m_duration_frequency[ packet->duration ];

  } else if (16 == m_num_durations_provided) {
    auto most_common = boost::accumulate(m_duration_frequency, m_duration_frequency.begin()->first,
                                         [&](int64_t accu, std::pair<int64_t,int64_t> const &elt) {
                                           return m_duration_frequency[accu] > elt.second ? accu : elt.first;
                                         });

    m_samples_per_packet = most_common / m_s2tc;
    m_packet_size        = samples_to_size(m_samples_per_packet);

    set_track_default_duration(most_common);
    rerender_track_headers();
  }

  m_previous_timecode = packet->timecode;
  m_samples_output     = packet->timecode / m_s2tc + samples_here;

  add_packet(packet);

  return FILE_STATUS_MOREDATA;
}

void
pcm_packetizer_c::flush_impl() {
  uint32_t size = m_buffer.get_size();
  if (0 >= size)
    return;

  int64_t samples_here = size_to_samples(size);
  add_packet(new packet_t(memory_c::clone(m_buffer.get_buffer(), size), m_samples_output * m_s2tc, samples_here * m_s2tc));

  m_samples_output += samples_here;
  m_buffer.remove(size);
}

connection_result_e
pcm_packetizer_c::can_connect_to(generic_packetizer_c *src,
                                 std::string &error_message) {
  pcm_packetizer_c *psrc = dynamic_cast<pcm_packetizer_c *>(src);
  if (!psrc)
    return CAN_CONNECT_NO_FORMAT;

  connect_check_a_samplerate(m_samples_per_sec, psrc->m_samples_per_sec);
  connect_check_a_channels(m_channels, psrc->m_channels);
  connect_check_a_bitdepth(m_bits_per_sample, psrc->m_bits_per_sample);

  return CAN_CONNECT_YES;
}