22b0845686
Fix warnings exposed by addition of -Wshorten-64-32 compiler flag in webmts sources. Change-Id: I3104e7be2bf991d7e108b652857ecca8e82c5ef9
218 lines
7.6 KiB
C++
218 lines
7.6 KiB
C++
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree. An additional intellectual property rights grant can be found
|
|
// in the file PATENTS. All contributing project authors may
|
|
// be found in the AUTHORS file in the root of the source tree.
|
|
#include "m2ts/vpxpes2ts.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
namespace libwebm {
|
|
// TODO(tomfinegan): Dedupe this and PesHeaderField.
|
|
// Stores a value and its size in bits for writing into a MPEG2 TS Header.
|
|
// Maximum size is 64 bits. Users may call the Check() method to perform minimal
|
|
// validation (size > 0 and <= 64).
|
|
struct TsHeaderField {
|
|
TsHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
|
|
std::uint8_t byte_index, std::uint8_t bits_to_shift)
|
|
: bits(value),
|
|
num_bits(size_in_bits),
|
|
index(byte_index),
|
|
shift(bits_to_shift) {}
|
|
TsHeaderField() = delete;
|
|
TsHeaderField(const TsHeaderField&) = default;
|
|
TsHeaderField(TsHeaderField&&) = default;
|
|
~TsHeaderField() = default;
|
|
bool Check() const {
|
|
return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
|
|
}
|
|
|
|
// Value to be stored in the field.
|
|
std::uint64_t bits;
|
|
|
|
// Number of bits in the value.
|
|
const int num_bits;
|
|
|
|
// Index into the header for the byte in which |bits| will be written.
|
|
const std::uint8_t index;
|
|
|
|
// Number of bits to left shift value before or'ing. Ignored for whole bytes.
|
|
const int shift;
|
|
};
|
|
|
|
// Data storage for MPEG2 Transport Stream headers.
|
|
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet
|
|
struct TsHeader {
|
|
TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter)
|
|
: is_payload_start(payload_start),
|
|
has_adaptation(adaptation_flag),
|
|
counter_value(counter) {}
|
|
TsHeader() = delete;
|
|
TsHeader(const TsHeader&) = default;
|
|
TsHeader(TsHeader&&) = default;
|
|
~TsHeader() = default;
|
|
|
|
void Write(PacketDataBuffer* buffer) const;
|
|
|
|
// Indicates the packet is the beginning of a new fragmented payload.
|
|
const bool is_payload_start;
|
|
|
|
// Indicates the packet contains an adaptation field.
|
|
const bool has_adaptation;
|
|
|
|
// The sync byte is the bit pattern of 0x47 (ASCII char 'G').
|
|
const std::uint8_t kTsHeaderSyncByte = 0x47;
|
|
const std::uint8_t sync_byte = kTsHeaderSyncByte;
|
|
|
|
// Value for |continuity_counter|. Used to detect gaps when demuxing.
|
|
const std::uint8_t counter_value;
|
|
|
|
// Set when FEC is impossible. Always 0.
|
|
const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7);
|
|
|
|
// This MPEG2 TS header is the start of a new payload (aka PES packet).
|
|
const TsHeaderField payload_unit_start_indicator =
|
|
TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6);
|
|
|
|
// Set when the current packet has a higher priority than other packets with
|
|
// the same PID. Always 0 for VPX.
|
|
const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5);
|
|
|
|
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29
|
|
// 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary
|
|
// streams and other data tables.
|
|
// Note: Though we hard code to 0x20, this value is actually 13 bits-- the
|
|
// buffer for the header is always set to 0, so it doesn't matter in practice.
|
|
const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0);
|
|
|
|
// Indicates scrambling key. Unused; always 0.
|
|
const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6);
|
|
|
|
// Adaptation field flag. Unused; always 0.
|
|
// TODO(tomfinegan): Not sure this is OK. Might need to add support for
|
|
// writing the Adaptation Field.
|
|
const TsHeaderField adaptation_field_flag =
|
|
TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5);
|
|
|
|
// Payload flag. All output packets created here have payloads. Always 1.
|
|
const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4);
|
|
|
|
// Continuity counter. Two bit field that is incremented for every packet.
|
|
const TsHeaderField continuity_counter =
|
|
TsHeaderField(counter_value, 4, 3, 3);
|
|
};
|
|
|
|
void TsHeader::Write(PacketDataBuffer* buffer) const {
|
|
std::uint8_t* byte = &(*buffer)[0];
|
|
*byte = sync_byte;
|
|
|
|
*++byte = 0;
|
|
*byte |= transport_error_indicator.bits << transport_error_indicator.shift;
|
|
*byte |= payload_unit_start_indicator.bits
|
|
<< payload_unit_start_indicator.shift;
|
|
*byte |= transport_priority.bits << transport_priority.shift;
|
|
|
|
*++byte = pid.bits & 0xff;
|
|
|
|
*++byte = 0;
|
|
*byte |= scrambling_control.bits << scrambling_control.shift;
|
|
*byte |= adaptation_field_flag.bits << adaptation_field_flag.shift;
|
|
*byte |= payload_flag.bits << payload_flag.shift;
|
|
*byte |= continuity_counter.bits; // last 4 bits.
|
|
}
|
|
|
|
bool VpxPes2Ts::ConvertToFile() {
|
|
output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
|
|
if (output_file_ == nullptr) {
|
|
std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n",
|
|
output_file_name_.c_str());
|
|
return false;
|
|
}
|
|
pes_converter_.reset(new Webm2Pes(input_file_name_, this));
|
|
if (pes_converter_ == nullptr) {
|
|
std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n");
|
|
return false;
|
|
}
|
|
return pes_converter_->ConvertToPacketReceiver();
|
|
}
|
|
|
|
bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) {
|
|
const int kTsHeaderSize = 4;
|
|
const int kTsPayloadSize = 184;
|
|
const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize;
|
|
int bytes_to_packetize = static_cast<int>(packet_data.size());
|
|
std::uint8_t continuity_counter = 0;
|
|
std::size_t read_pos = 0;
|
|
|
|
ts_buffer_.reserve(kTsPacketSize);
|
|
|
|
while (bytes_to_packetize > 0) {
|
|
if (continuity_counter > 0xf)
|
|
continuity_counter = 0;
|
|
|
|
// Calculate payload size (need to know if we'll have to pad with an empty
|
|
// adaptation field).
|
|
int payload_size = std::min(bytes_to_packetize, kTsPayloadSize);
|
|
|
|
// Write the TS header.
|
|
const TsHeader header(
|
|
bytes_to_packetize == static_cast<int>(packet_data.size()),
|
|
payload_size != kTsPayloadSize, continuity_counter);
|
|
header.Write(&ts_buffer_);
|
|
int write_pos = kTsHeaderSize;
|
|
|
|
// (pre)Pad payload with an empty adaptation field. All packets must be
|
|
// |kTsPacketSize| (188).
|
|
if (payload_size < kTsPayloadSize) {
|
|
// We need at least 2 bytes to write an empty adaptation field.
|
|
if (payload_size == (kTsPayloadSize - 1)) {
|
|
payload_size--;
|
|
}
|
|
|
|
// Padding adaptation field:
|
|
// 8 bits: number of adaptation field bytes following this byte.
|
|
// 8 bits: unused (in this program) flags.
|
|
// This is followed by a run of 0xff to reach |kTsPayloadSize| (184)
|
|
// bytes.
|
|
const int pad_size = kTsPayloadSize - payload_size - 1 - 1;
|
|
ts_buffer_[write_pos++] = pad_size + 1;
|
|
ts_buffer_[write_pos++] = 0;
|
|
|
|
const std::uint8_t kStuffingByte = 0xff;
|
|
for (int i = 0; i < pad_size; ++i) {
|
|
ts_buffer_[write_pos++] = kStuffingByte;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < payload_size; ++i) {
|
|
ts_buffer_[write_pos++] = packet_data[read_pos++];
|
|
}
|
|
|
|
bytes_to_packetize -= payload_size;
|
|
continuity_counter++;
|
|
|
|
if (write_pos != kTsPacketSize) {
|
|
fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n");
|
|
return false;
|
|
}
|
|
|
|
// Write contents of |ts_buffer_| to |output_file_|.
|
|
// TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient...
|
|
// Fix me.
|
|
if (static_cast<int>(std::fwrite(&ts_buffer_[0], 1, kTsPacketSize,
|
|
output_file_.get())) != kTsPacketSize) {
|
|
std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace libwebm
|