summaryrefslogtreecommitdiff
path: root/chromium/media/formats/mp2t/ts_packet.cc
blob: f3a44989a97f15c765b20bcd79802548e2039bc4 (plain)
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/formats/mp2t/ts_packet.h"

#include <memory>

#include "base/logging.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"

namespace media {
namespace mp2t {

static const uint8_t kTsHeaderSyncword = 0x47;

// static
int TsPacket::Sync(const uint8_t* buf, int size) {
  int k = 0;
  for (; k < size; k++) {
    // Verify that we have 4 syncwords in a row when possible,
    // this should improve synchronization robustness.
    // TODO(damienv): Consider the case where there is garbage
    // between TS packets.
    bool is_header = true;
    for (int i = 0; i < 4; i++) {
      int idx = k + i * kPacketSize;
      if (idx >= size)
        break;
      if (buf[idx] != kTsHeaderSyncword) {
        DVLOG(LOG_LEVEL_TS)
            << "ByteSync" << idx << ": "
            << std::hex << static_cast<int>(buf[idx]) << std::dec;
        is_header = false;
        break;
      }
    }
    if (is_header)
      break;
  }

  DVLOG_IF(1, k != 0) << "SYNC: nbytes_skipped=" << k;
  return k;
}

// static
TsPacket* TsPacket::Parse(const uint8_t* buf, int size) {
  if (size < kPacketSize) {
    DVLOG(1) << "Buffer does not hold one full TS packet:"
             << " buffer_size=" << size;
    return NULL;
  }

  DCHECK_EQ(buf[0], kTsHeaderSyncword);
  if (buf[0] != kTsHeaderSyncword) {
    DVLOG(1) << "Not on a TS syncword:"
             << " buf[0]="
             << std::hex << static_cast<int>(buf[0]) << std::dec;
    return NULL;
  }

  std::unique_ptr<TsPacket> ts_packet(new TsPacket());
  bool status = ts_packet->ParseHeader(buf);
  if (!status) {
    DVLOG(1) << "Parsing header failed";
    return NULL;
  }
  return ts_packet.release();
}

TsPacket::TsPacket() {
}

TsPacket::~TsPacket() {
}

bool TsPacket::ParseHeader(const uint8_t* buf) {
  BitReader bit_reader(buf, kPacketSize);
  payload_ = buf;
  payload_size_ = kPacketSize;

  // Read the TS header: 4 bytes.
  int syncword;
  int transport_error_indicator;
  int payload_unit_start_indicator;
  int transport_priority;
  int transport_scrambling_control;
  int adaptation_field_control;
  RCHECK(bit_reader.ReadBits(8, &syncword));
  RCHECK(bit_reader.ReadBits(1, &transport_error_indicator));
  RCHECK(bit_reader.ReadBits(1, &payload_unit_start_indicator));
  RCHECK(bit_reader.ReadBits(1, &transport_priority));
  RCHECK(bit_reader.ReadBits(13, &pid_));
  RCHECK(bit_reader.ReadBits(2, &transport_scrambling_control));
  RCHECK(bit_reader.ReadBits(2, &adaptation_field_control));
  RCHECK(bit_reader.ReadBits(4, &continuity_counter_));
  payload_unit_start_indicator_ = (payload_unit_start_indicator != 0);
  payload_ += 4;
  payload_size_ -= 4;

  // Default values when no adaptation field.
  discontinuity_indicator_ = false;
  random_access_indicator_ = false;

  // Done since no adaptation field.
  if ((adaptation_field_control & 0x2) == 0)
    return true;

  // Read the adaptation field if needed.
  int adaptation_field_length;
  RCHECK(bit_reader.ReadBits(8, &adaptation_field_length));
  DVLOG(LOG_LEVEL_TS) << "adaptation_field_length=" << adaptation_field_length;
  payload_ += 1;
  payload_size_ -= 1;
  if ((adaptation_field_control & 0x1) == 0 &&
       adaptation_field_length != 183) {
    DVLOG(1) << "adaptation_field_length=" << adaptation_field_length;
    return false;
  }
  if ((adaptation_field_control & 0x1) == 1 &&
       adaptation_field_length > 182) {
    DVLOG(1) << "adaptation_field_length=" << adaptation_field_length;
    // This is not allowed by the spec.
    // However, some badly encoded streams are using
    // adaptation_field_length = 183
    return false;
  }

  // adaptation_field_length = '0' is used to insert a single stuffing byte
  // in the adaptation field of a transport stream packet.
  if (adaptation_field_length == 0)
    return true;

  bool status = ParseAdaptationField(&bit_reader, adaptation_field_length);
  payload_ += adaptation_field_length;
  payload_size_ -= adaptation_field_length;
  return status;
}

bool TsPacket::ParseAdaptationField(BitReader* bit_reader,
                                    int adaptation_field_length) {
  DCHECK_GT(adaptation_field_length, 0);
  int adaptation_field_start_marker = bit_reader->bits_available() / 8;

  int discontinuity_indicator;
  int random_access_indicator;
  int elementary_stream_priority_indicator;
  int pcr_flag;
  int opcr_flag;
  int splicing_point_flag;
  int transport_private_data_flag;
  int adaptation_field_extension_flag;
  RCHECK(bit_reader->ReadBits(1, &discontinuity_indicator));
  RCHECK(bit_reader->ReadBits(1, &random_access_indicator));
  RCHECK(bit_reader->ReadBits(1, &elementary_stream_priority_indicator));
  RCHECK(bit_reader->ReadBits(1, &pcr_flag));
  RCHECK(bit_reader->ReadBits(1, &opcr_flag));
  RCHECK(bit_reader->ReadBits(1, &splicing_point_flag));
  RCHECK(bit_reader->ReadBits(1, &transport_private_data_flag));
  RCHECK(bit_reader->ReadBits(1, &adaptation_field_extension_flag));
  discontinuity_indicator_ = (discontinuity_indicator != 0);
  random_access_indicator_ = (random_access_indicator != 0);

  if (pcr_flag) {
    int64_t program_clock_reference_base;
    int reserved;
    int program_clock_reference_extension;
    RCHECK(bit_reader->ReadBits(33, &program_clock_reference_base));
    RCHECK(bit_reader->ReadBits(6, &reserved));
    RCHECK(bit_reader->ReadBits(9, &program_clock_reference_extension));
  }

  if (opcr_flag) {
    int64_t original_program_clock_reference_base;
    int reserved;
    int original_program_clock_reference_extension;
    RCHECK(bit_reader->ReadBits(33, &original_program_clock_reference_base));
    RCHECK(bit_reader->ReadBits(6, &reserved));
    RCHECK(
        bit_reader->ReadBits(9, &original_program_clock_reference_extension));
  }

  if (splicing_point_flag) {
    int splice_countdown;
    RCHECK(bit_reader->ReadBits(8, &splice_countdown));
  }

  if (transport_private_data_flag) {
    int transport_private_data_length;
    RCHECK(bit_reader->ReadBits(8, &transport_private_data_length));
    RCHECK(bit_reader->SkipBits(8 * transport_private_data_length));
  }

  if (adaptation_field_extension_flag) {
    int adaptation_field_extension_length;
    RCHECK(bit_reader->ReadBits(8, &adaptation_field_extension_length));
    RCHECK(bit_reader->SkipBits(8 * adaptation_field_extension_length));
  }

  // The rest of the adaptation field should be stuffing bytes.
  int adaptation_field_remaining_size = adaptation_field_length -
      (adaptation_field_start_marker - bit_reader->bits_available() / 8);
  RCHECK(adaptation_field_remaining_size >= 0);
  for (int k = 0; k < adaptation_field_remaining_size; k++) {
    int stuffing_byte;
    RCHECK(bit_reader->ReadBits(8, &stuffing_byte));
    // Unfortunately, a lot of streams exist in the field that do not fill
    // the remaining of the adaptation field with the expected stuffing value:
    // do not fail if that's the case.
    DVLOG_IF(1, stuffing_byte != 0xff)
        << "Stream not compliant: invalid stuffing byte "
        << std::hex << stuffing_byte;
  }

  DVLOG(LOG_LEVEL_TS) << "random_access_indicator=" << random_access_indicator_;
  return true;
}

}  // namespace mp2t
}  // namespace media