// 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 #include #include #include 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(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(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(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