blob: ebefe538ff2f92a223ac1a2d8f1daba6053540d5 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// [Modified from the code in SRS2 (src/kernel/srs_kernel_ts.cpp)]
// The MIT License (MIT)
// Copyright (c) 2013-2015 SRS(ossrs)
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "brpc/log.h"
#include "brpc/policy/rtmp_protocol.h"
#include "brpc/ts.h"
namespace brpc {
/*-
* Copyright (c) 2008 Joerg Sonnenberger
* Copyright (c) 2009-2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
static const uint32_t crctab[] = {
0x0,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
uint32_t crc32_ts(const void* data, size_t len) {
const uint8_t* p = (const uint8_t*)data;
uint32_t crc = 0xffffffff;
for (size_t i = 0; i < len; ++i) {
crc = (crc << 8) ^ crctab[(crc >> 24) ^ *p++];
}
return crc;
}
// Transport Stream packets are 188 bytes in length.
static const size_t TS_PACKET_SIZE = 188;
static const int TS_PMT_NUMBER = 1;
// Table 2-3 - PID table, hls-mpeg-ts-iso13818-1.pdf, page 37
// NOTE - The transport packets with PID values 0x0000, 0x0001, and
// 0x0010-0x1FFE are allowed to carry a PCR.
// Program Association Table(see Table 2-25)
static const TsPid TS_PID_PAT = (TsPid)0x00;
// Conditional Access Table (see Table 2-27)
//static const TsPid TS_PID_CAT = (TsPid)0x01;
// Transport Stream Description Table
//static const TsPid TS_PID_TSDT = (TsPid)0x02;
//static const TsPid TS_PID_RESERVED_START = (TsPid)0x03;
// static const TsPid TS_PID_RESERVED_END = (TsPid)0x0f;
// May be assigned as network_PID; Program_map_PID; elementary_PID; or for
// other purposes
//static const TsPid TS_PID_APP_START = (TsPid)0x10;
static const TsPid TS_PID_VIDEO_AVC = (TsPid)0x100;
static const TsPid TS_PID_AUDIO_AAC = (TsPid)0x101;
static const TsPid TS_PID_AUDIO_MP3 = (TsPid)0x102;
static const TsPid TS_PID_PMT = (TsPid)0x1001;
//static const TsPid TS_PID_APP_END = (TsPid)0x1ffe;
static const TsPid TS_PID_NULL = (TsPid)0x01FFF;// null packets (see Table 2-3)
// The sync_byte is a fixed 8-bit field whose value is '0100 0111' (0x47).
// Sync_byte emulation in the choice of values for other regularly
// occurring fields, such as PID, should be avoided.
static const int8_t TS_SYNC_BYTE = 0x47; // 8 bits
const char* TsStream2Str(TsStream stream) {
switch (stream) {
case TS_STREAM_RESERVED: return "Reserved";
case TS_STREAM_AUDIO_MP3: return "MP3";
case TS_STREAM_AUDIO_AAC: return "AAC";
case TS_STREAM_AUDIO_AC3: return "AC3";
case TS_STREAM_AUDIO_DTS: return "AudioDTS";
case TS_STREAM_VIDEO_H264: return "H.264";
case TS_STREAM_VIDEO_MPEG4: return "MP4";
case TS_STREAM_AUDIO_MPEG4: return "MP4A";
}
return "Other";
}
// the media audio/video message parsed from PES packet.
struct TsWriter::TsMessage {
public:
TsMessage()
: write_pcr(false)
, is_discontinuity(false)
, dts(0)
, pts(0)
, sid(TS_PES_STREAM_ID_UNKNOWN)
, PES_packet_length(0)
, continuity_counter(0) {
}
// whether this message with pcr(program_clock_reference)
// generally, the video IDR(keyframe) carries the pcr info.
bool write_pcr;
// whether got discontinuity ts, for example, sequence header changed.
// TODO: always false right now.
bool is_discontinuity;
// the timestamp in 90khz
int64_t dts;
int64_t pts;
// the id of pes stream to indicate the payload codec.
TsPESStreamId sid;
// size of payload, 0 indicates the length() of payload.
uint16_t PES_packet_length;
uint8_t continuity_counter;
butil::IOBuf payload;
};
// whether the sid indicates the elementary stream audio.
inline bool is_audio(TsPESStreamId sid) {
return ((sid >> 5) & 0x07) == 0x06/*0b110*/;
}
// whether the sid indicates the elementary stream video.
inline bool is_video(TsPESStreamId sid) {
return ((sid >> 4) & 0x0f) == 0x0e/*0b1110*/;
}
TsChannelGroup::TsChannelGroup() {}
TsChannelGroup::~TsChannelGroup() {}
TsChannel* TsChannelGroup::get(TsPid pid) {
return &_pids[pid];
// std::map<TsPid, TsChannel>::iterator it = _pids.find(pid);
// if (it != _pids.end()) {
// return &it->second;
// }
// return NULL;
}
TsChannel* TsChannelGroup::set(TsPid pid) {
std::map<TsPid, TsChannel>::iterator it = _pids.find(pid);
if (it == _pids.end()) {
return &_pids[pid];
} else {
return &it->second;
}
}
TsPacket::TsPacket(TsChannelGroup* g)
: _modified(false)
, _transport_error_indicator(0)
, _payload_unit_start_indicator(0)
, _transport_priority(0)
, _pid(TS_PID_PAT)
, _transport_scrambling_control(TS_SCRAMBLED_DISABLED)
, _adaptation_field_control(TS_AF_RESERVED)
, _continuity_counter(0)
, _adaptation_field(NULL)
, _payload(NULL)
, _tschan_group(g) {
}
TsPacket::~TsPacket() {
delete _adaptation_field;
delete _payload;
}
void TsPacket::Reset() {
delete _payload;
_payload = NULL;
delete _adaptation_field;
_adaptation_field = NULL;
_transport_error_indicator = 0;
_payload_unit_start_indicator = 0;
_transport_priority = 0;
_pid = TS_PID_PAT;
_transport_scrambling_control = TS_SCRAMBLED_DISABLED;
_adaptation_field_control = TS_AF_RESERVED;
_continuity_counter = 0;
_payload = NULL;
_modified = false;
}
TsAdaptationField* TsPacket::CreateAdaptationField() {
if (_adaptation_field != NULL) {
LOG(ERROR) << "_adaptation_field is not NULL";
return _adaptation_field;
}
_adaptation_field = new TsAdaptationField;
if (_adaptation_field_control == TS_AF_RESERVED) {
_adaptation_field_control = TS_AF_ADAPTATION_ONLY;
} else if (_adaptation_field_control == TS_AF_PAYLOAD_ONLY) {
_adaptation_field_control = TS_AF_BOTH;
} else {
LOG(ERROR) << "Invalid _adaptation_field_control="
<< _adaptation_field_control;
}
return _adaptation_field;
}
size_t TsPacket::ByteSize() const {
size_t sz = 4;
if (_adaptation_field) {
sz +=_adaptation_field->ByteSize();
}
if (_payload) {
sz += _payload->ByteSize();
}
return sz;
}
int TsPacket::Encode(void* data) const {
char* p = (char*)data;
policy::Write1Byte(&p, TS_SYNC_BYTE);
int16_t pidv = _pid & 0x1FFF;
pidv |= (_transport_priority << 13) & 0x2000;
pidv |= (_transport_error_indicator << 15) & 0x8000;
pidv |= (_payload_unit_start_indicator << 14) & 0x4000;
policy::WriteBigEndian2Bytes(&p, pidv);
int8_t ccv = _continuity_counter & 0x0F;
ccv |= (_transport_scrambling_control << 6) & 0xC0;
TsAdaptationFieldType af_control = _adaptation_field_control;
if (af_control == TS_AF_RESERVED) {
// In the case of a null packet, af_control shall be set to '01'.
af_control = TS_AF_PAYLOAD_ONLY;
}
ccv |= (af_control << 4) & 0x30;
policy::Write1Byte(&p, ccv);
if (_adaptation_field) {
if (_adaptation_field->Encode(p, af_control) != 0) {
LOG(ERROR) << "Fail to encode _adaptation_field";
return -1;
}
p += _adaptation_field->ByteSize();
}
if (_payload) {
if (_payload->Encode(p) != 0) {
LOG(ERROR) << "Fail to encode _payload";
return -1;
}
p += _payload->ByteSize();
}
return 0;
}
void TsPacket::AddPadding(size_t num_stuffings) {
const bool no_af_before = (_adaptation_field == NULL);
TsAdaptationField* af = mutable_adaptation_field();
if (no_af_before) {
const size_t sz = af->ByteSize();
if (num_stuffings > sz) {
af->nb_af_reserved = num_stuffings - sz;
}
} else {
af->nb_af_reserved += num_stuffings;
}
}
void TsPacket::CreateAsPAT(int16_t pmt_number, TsPid pmt_pid) {
if (_modified) {
Reset();
}
_pid = TS_PID_PAT;
_payload_unit_start_indicator = 1;
_adaptation_field_control = TS_AF_PAYLOAD_ONLY;
TsPayloadPAT* pat = new TsPayloadPAT(this);
pat->pointer_field = 0;
pat->table_id = TS_PSI_ID_PAS;
pat->section_syntax_indicator = 1;
pat->transport_stream_id = 1;
pat->version_number = 0;
pat->current_next_indicator = 1;
pat->section_number = 0;
pat->last_section_number = 0;
pat->programs.push_back(TsPayloadPATProgram(pmt_number, pmt_pid));
_payload = pat;
}
int TsPacket::CreateAsPMT(int16_t pmt_number, TsPid pmt_pid,
TsPid vpid, TsStream vs,
TsPid apid, TsStream as) {
if (vs != TS_STREAM_VIDEO_H264 &&
as != TS_STREAM_AUDIO_AAC && as != TS_STREAM_AUDIO_MP3) {
LOG(ERROR) << "Unsupported video_stream=" << vs << " audio_stream=" << as;
return -1;
}
if (_modified) {
Reset();
}
_pid = pmt_pid;
_payload_unit_start_indicator = 1;
_adaptation_field_control = TS_AF_PAYLOAD_ONLY;
TsPayloadPMT* pmt = new TsPayloadPMT(this);
pmt->pointer_field = 0;
pmt->table_id = TS_PSI_ID_PMS;
pmt->section_syntax_indicator = 1;
pmt->program_number = pmt_number;
pmt->version_number = 0;
pmt->current_next_indicator = 1;
pmt->section_number = 0;
pmt->last_section_number = 0;
pmt->program_info_length = 0;
if (as == TS_STREAM_AUDIO_AAC || as == TS_STREAM_AUDIO_MP3) {
// use audio to carry pcr by default.
// for hls, there must be at least one audio channel.
pmt->PCR_PID = apid;
pmt->infos.push_back(new TsPayloadPMTESInfo(as, apid));
}
if (vs == TS_STREAM_VIDEO_H264) {
// if h.264 specified, use video to carry pcr.
pmt->PCR_PID = vpid;
pmt->infos.push_back(new TsPayloadPMTESInfo(vs, vpid));
}
_payload = pmt;
return 0;
}
void TsPacket::CreateAsPESFirst(TsPid pid, TsPESStreamId sid,
uint8_t continuity_counter, bool discontinuity,
int64_t pcr, int64_t dts, int64_t pts, int size) {
if (_modified) {
Reset();
}
_pid = pid;
_payload_unit_start_indicator = 1;
_adaptation_field_control = TS_AF_PAYLOAD_ONLY;
_continuity_counter = continuity_counter;
TsPayloadPES* pes = new TsPayloadPES(this);
pes->stream_id = sid;
pes->PES_packet_length = (size > 0xFFFF ? 0 : size);
pes->PES_scrambling_control = 0;
pes->PES_priority = 0;
pes->data_alignment_indicator = 0;
pes->copyright = 0;
pes->original_or_copy = 0;
pes->PTS_DTS_flags = (dts == pts)? 0x02:0x03;
pes->ESCR_flag = 0;
pes->ES_rate_flag = 0;
pes->DSM_trick_mode_flag = 0;
pes->additional_copy_info_flag = 0;
pes->PES_CRC_flag = 0;
pes->PES_extension_flag = 0;
pes->pts = pts;
pes->dts = dts;
_payload = pes;
if (pcr >= 0) {
TsAdaptationField* af = mutable_adaptation_field();
af->discontinuity_indicator = discontinuity;
af->random_access_indicator = 0;
af->elementary_stream_priority_indicator = 0;
af->PCR_flag = 1;
af->OPCR_flag = 0;
af->splicing_point_flag = 0;
af->transport_private_data_flag = 0;
af->adaptation_field_extension_flag = 0;
af->program_clock_reference_base = pcr;
af->program_clock_reference_extension = 0;
}
}
void TsPacket::CreateAsPESContinue(TsPid pid, uint8_t continuity_counter) {
if (_modified) {
Reset();
}
_pid = pid;
_adaptation_field_control = TS_AF_PAYLOAD_ONLY;
_continuity_counter = continuity_counter;
}
TsAdaptationField::TsAdaptationField()
: discontinuity_indicator(0)
, random_access_indicator(0)
, elementary_stream_priority_indicator(0)
, PCR_flag(0)
, OPCR_flag(0)
, splicing_point_flag(0)
, transport_private_data_flag(0)
, adaptation_field_extension_flag(0)
, program_clock_reference_base(0)
, program_clock_reference_extension(0)
, original_program_clock_reference_base(0)
, original_program_clock_reference_extension(0)
, splice_countdown(0)
, transport_private_data_length(0)
, transport_private_data(NULL)
, adaptation_field_extension_length(0)
, ltw_flag(0)
, piecewise_rate_flag(0)
, seamless_splice_flag(0)
, ltw_valid_flag(0)
, ltw_offset(0)
, piecewise_rate(0)
, splice_type(0)
, DTS_next_AU0(0)
, marker_bit0(0)
, DTS_next_AU1(0)
, marker_bit1(0)
, DTS_next_AU2(0)
, marker_bit2(0)
, nb_af_ext_reserved(0)
, nb_af_reserved(0) {
}
TsAdaptationField::~TsAdaptationField() {
delete [] transport_private_data;
}
size_t TsAdaptationField::ByteSize() const {
size_t sz = 2;
if (PCR_flag) { sz += 6; }
if (OPCR_flag) { sz += 6; }
if (splicing_point_flag) { ++sz; }
if (transport_private_data_flag) {
sz += 1 + transport_private_data_length;
}
if (adaptation_field_extension_flag) {
sz += 2 + adaptation_field_extension_length;
}
sz += nb_af_ext_reserved;
sz += nb_af_reserved;
return sz;
}
int TsAdaptationField::Encode(
void* data, TsAdaptationFieldType adaptation_field_control) const {
char* p = (char*)data;
const size_t af_length = adaptation_field_length();
policy::Write1Byte(&p, af_length);
if (adaptation_field_control == TS_AF_BOTH) {
if (af_length > 182) {
LOG(ERROR) << "Invalid af_length=" << af_length;
return -1;
}
} else if (adaptation_field_control == TS_AF_ADAPTATION_ONLY) {
if (af_length != 183) {
LOG(ERROR) << "Invalid af_length=" << af_length;
return -1;
}
}
// no adaptation field.
if (af_length == 0) {
return 0;
}
int8_t tmpv = adaptation_field_extension_flag & 0x01;
tmpv |= (discontinuity_indicator << 7) & 0x80;
tmpv |= (random_access_indicator << 6) & 0x40;
tmpv |= (elementary_stream_priority_indicator << 5) & 0x20;
tmpv |= (PCR_flag << 4) & 0x10;
tmpv |= (OPCR_flag << 3) & 0x08;
tmpv |= (splicing_point_flag << 2) & 0x04;
tmpv |= (transport_private_data_flag << 1) & 0x02;
policy::Write1Byte(&p, tmpv);
if (PCR_flag) {
// @remark, use pcr base and ignore the extension
// @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370
int64_t pcrv = program_clock_reference_extension & 0x1ff;
pcrv |= (const1_value0 << 9) & 0x7E00;
pcrv |= (program_clock_reference_base << 15) & 0x1FFFFFFFF000000LL;
const char* pp = (const char*)&pcrv;
*p++ = pp[5];
*p++ = pp[4];
*p++ = pp[3];
*p++ = pp[2];
*p++ = pp[1];
*p++ = pp[0];
}
if (OPCR_flag) { p += 6; } // Ignore OPCR
if (splicing_point_flag) {
policy::Write1Byte(&p, splice_countdown);
}
if (transport_private_data_flag) {
policy::Write1Byte(&p, transport_private_data_length);
if (transport_private_data_length > 0) {
memcpy(p, transport_private_data, transport_private_data_length);
p += transport_private_data_length;
}
}
if (adaptation_field_extension_flag) {
policy::Write1Byte(&p, adaptation_field_extension_length);
int8_t ltwfv = const1_value1 & 0x1F;
ltwfv |= (ltw_flag << 7) & 0x80;
ltwfv |= (piecewise_rate_flag << 6) & 0x40;
ltwfv |= (seamless_splice_flag << 5) & 0x20;
policy::Write1Byte(&p, ltwfv);
const char* const saved_p = p;
if (ltw_flag) { p += 2; } // Ignore ltw
if (piecewise_rate_flag) { p += 3; } // Ignore piecewise_rate
if (seamless_splice_flag) { p += 5; } // Ignore seamless_splice
p += nb_af_ext_reserved;
if (adaptation_field_extension_length != p - saved_p) {
LOG(ERROR) << "af_extension_length="
<< adaptation_field_extension_length
<< " does not match other fields";
return -1;
}
}
p += nb_af_reserved;
return 0;
}
TsPayload::TsPayload(const TsPacket* p) : _packet(p) {}
TsPayload::~TsPayload() {}
TsPayloadPES::TsPayloadPES(const TsPacket* p) : TsPayload(p) {
_PES_header_data_length = -1;
PES_private_data = NULL;
pack_field = NULL;
PES_extension_field = NULL;
nb_stuffings = 0;
nb_bytes = 0;
nb_paddings = 0;
}
TsPayloadPES::~TsPayloadPES() {
delete[] PES_private_data;
delete[] pack_field;
delete[] PES_extension_field;
}
size_t TsPayloadPES::ByteSize() const {
size_t sz = 0;
_PES_header_data_length = 0;
const TsPESStreamId sid = stream_id;
if (sid != TS_PES_STREAM_ID_PROGRAM_STREAM_MAP
&& sid != TS_PES_STREAM_ID_PADDING_STREAM
&& sid != TS_PES_STREAM_ID_PRIVATE_STREAM2
&& sid != TS_PES_STREAM_ID_ECM_STREAM
&& sid != TS_PES_STREAM_ID_EMM_STREAM
&& sid != TS_PES_STREAM_ID_PROGRAM_STREAM_DIRECTORY
&& sid != TS_PES_STREAM_ID_DSMC_STREAM
&& sid != TS_PES_STREAM_ID_H2221TYPE_E) {
sz += 6;
sz += 3;
const size_t old_sz = sz;
if (PTS_DTS_flags == 0x2) { sz += 5; }
else if (PTS_DTS_flags == 0x3) { sz += 10; }
if (ESCR_flag) { sz += 6; }
if (ES_rate_flag) { sz += 3; }
if (DSM_trick_mode_flag) { sz += 1; }
if (additional_copy_info_flag) { sz += 1; }
if (PES_CRC_flag) { sz += 2; }
if (PES_extension_flag) {
sz += 1;
if (PES_private_data_flag) { sz += 16; }
if (pack_header_field_flag) { sz += 1 + pack_field_length; }
if (program_packet_sequence_counter_flag) { sz += 2; }
if (P_STD_buffer_flag) { sz += 2; }
if (PES_extension_flag_2) { sz += 1 + PES_extension_field_length; }
}
_PES_header_data_length = sz - old_sz;
sz += nb_stuffings;
// packet bytes
} else if (sid == TS_PES_STREAM_ID_PROGRAM_STREAM_MAP
|| sid == TS_PES_STREAM_ID_PRIVATE_STREAM2
|| sid == TS_PES_STREAM_ID_ECM_STREAM
|| sid == TS_PES_STREAM_ID_EMM_STREAM
|| sid == TS_PES_STREAM_ID_PROGRAM_STREAM_DIRECTORY
|| sid == TS_PES_STREAM_ID_DSMC_STREAM
|| sid == TS_PES_STREAM_ID_H2221TYPE_E
) {
// packet bytes
} else {
// nb_drop
}
return sz;
}
int TsPayloadPES::Encode(void* data) const {
if (_PES_header_data_length < 0) {
(void)ByteSize();
CHECK_GE(_PES_header_data_length, 0);
}
char* p = (char*)data;
// 3B
// Together with the stream_id that follows it constitutes a packet start
// code that identifies the beginning of a packet.
policy::WriteBigEndian3Bytes(&p, 0x1);
// 1B
policy::Write1Byte(&p, stream_id);
// 2B
// the PES_packet_length is the actual bytes size, the pplv write to ts
// is the actual bytes plus the header size.
int32_t pplv = 0;
if (PES_packet_length > 0) {
pplv = PES_packet_length + 3 + _PES_header_data_length;
pplv = (pplv > 0xFFFF)? 0 : pplv;
}
policy::WriteBigEndian2Bytes(&p, pplv);
int8_t oocv = original_or_copy & 0x01;
oocv |= (const2bits << 6) & 0xC0;
oocv |= (PES_scrambling_control << 4) & 0x30;
oocv |= (PES_priority << 3) & 0x08;
oocv |= (data_alignment_indicator << 2) & 0x04;
oocv |= (copyright << 1) & 0x02;
policy::Write1Byte(&p, oocv);
int8_t pefv = PES_extension_flag & 0x01;
pefv |= (PTS_DTS_flags << 6) & 0xC0;
pefv |= (ESCR_flag << 5) & 0x20;
pefv |= (ES_rate_flag << 4) & 0x10;
pefv |= (DSM_trick_mode_flag << 3) & 0x08;
pefv |= (additional_copy_info_flag << 2) & 0x04;
pefv |= (PES_CRC_flag << 1) & 0x02;
policy::Write1Byte(&p, pefv);
policy::Write1Byte(&p, _PES_header_data_length);
if (PTS_DTS_flags == 0x2) { // 5B
encode_33bits_dts_pts(&p, 0x02, pts);
} else if (PTS_DTS_flags == 0x3) { // 10B
encode_33bits_dts_pts(&p, 0x03, pts);
encode_33bits_dts_pts(&p, 0x01, dts);
// the diff of dts and pts should never be greater than 1s.
if (labs(dts - pts) > 90000) {
LOG(WARNING) << "Diff between dts=" << dts << " and pts=" << pts
<< " is greater than 1 second";
}
}
if (ESCR_flag) { p += 6; }
if (ES_rate_flag) { p += 3; }
if (DSM_trick_mode_flag) { p += 1; }
if (additional_copy_info_flag) { p += 1; }
if (PES_CRC_flag) { p += 2; }
if (PES_extension_flag) {
int8_t efv = PES_extension_flag_2 & 0x01;
efv |= (PES_private_data_flag << 7) & 0x80;
efv |= (pack_header_field_flag << 6) & 0x40;
efv |= (program_packet_sequence_counter_flag << 5) & 0x20;
efv |= (P_STD_buffer_flag << 4) & 0x10;
efv |= (const1_value0 << 1) & 0xE0;
policy::Write1Byte(&p, efv);
if (PES_private_data_flag) { p += 16; }
if (pack_header_field_flag) { p += 1 + pack_field_length; }
if (program_packet_sequence_counter_flag) { p += 2; }
if (P_STD_buffer_flag) { p += 2; }
if (PES_extension_flag_2) { p += 1 + PES_extension_field_length; }
}
// stuffing_byte
p += nb_stuffings;
return 0;
}
void TsPayloadPES::encode_33bits_dts_pts(
char** data, uint8_t fb, int64_t v) const {
char* p = *data;
int32_t val = (fb << 4) | (((v >> 30) & 0x07) << 1) | 1;
*p++ = val;
val = (((v >> 15) & 0x7fff) << 1) | 1;
*p++ = (val >> 8);
*p++ = val;
val = (((v) & 0x7fff) << 1) | 1;
*p++ = (val >> 8);
*p++ = val;
*data = p;
}
TsPayloadPSI::TsPayloadPSI(const TsPacket* p)
: TsPayload(p)
, _section_length(-1)
, pointer_field(0)
, section_syntax_indicator(1)
, table_id(TS_PSI_ID_PAS) {
}
TsPayloadPSI::~TsPayloadPSI() {}
size_t TsPayloadPSI::ByteSize() const {
size_t sz = 0;
// section size is the sl plus the crc32
_section_length = PsiByteSize() + 4;
if (packet()->payload_unit_start_indicator()) { sz += 1; }
sz += 3;
sz += _section_length;
return sz;
}
int TsPayloadPSI::Encode(void* data) const {
char* p = (char*)data;
if (_section_length < 0) {
(void)ByteSize();
CHECK_GE(_section_length, 0);
}
if (packet()->payload_unit_start_indicator()) {
policy::Write1Byte(&p, pointer_field);
}
const char* const section_start = p; // to calc the crc32
policy::Write1Byte(&p, table_id);
int16_t slv = _section_length & 0x0FFF;
slv |= (section_syntax_indicator << 15) & 0x8000;
slv |= (const0_value << 14) & 0x4000;
slv |= (const1_value << 12) & 0x3000;
policy::WriteBigEndian2Bytes(&p, slv);
if (_section_length == 0) {
return 0;
}
if (PsiEncode(p) != 0) {
LOG(ERROR) << "Fail to TsPayloadPSI.PsiEncode";
return -1;
}
p += _section_length - 4;
const uint32_t crc32 = crc32_ts(section_start, p - section_start);
policy::WriteBigEndian4Bytes(&p, crc32);
return 0;
}
TsPayloadPATProgram::TsPayloadPATProgram(int16_t number, TsPid pid2)
: program_number(number)
, pid(pid2) {
}
TsPayloadPATProgram::~TsPayloadPATProgram() {}
int TsPayloadPATProgram::Encode(void* data) const {
char* p = (char*)data;
int tmpv = pid & 0x1FFF;
tmpv |= (program_number << 16) & 0xFFFF0000;
tmpv |= (const1_value << 13) & 0xE000;
policy::WriteBigEndian4Bytes(&p, tmpv);
return 0;
}
TsPayloadPAT::TsPayloadPAT(const TsPacket* p)
: TsPayloadPSI(p)
, transport_stream_id(0)
, version_number(0)
, current_next_indicator(0)
, section_number(0)
, last_section_number(0) {
}
TsPayloadPAT::~TsPayloadPAT() {}
size_t TsPayloadPAT::PsiByteSize() const {
size_t sz = 5;
for (size_t i = 0; i < programs.size(); ++i) {
sz += programs[i].ByteSize();
}
return sz;
}
int TsPayloadPAT::PsiEncode(void* data) const {
char* p = (char*)data;
policy::WriteBigEndian2Bytes(&p, transport_stream_id);
int8_t cniv = current_next_indicator & 0x01;
cniv |= (version_number << 1) & 0x3E;
cniv |= (const1_value << 6) & 0xC0;
policy::Write1Byte(&p, cniv);
policy::Write1Byte(&p, section_number);
policy::Write1Byte(&p, last_section_number);
for (size_t i = 0; i < programs.size(); ++i) {
if (programs[i].Encode(p) != 0) {
LOG(ERROR) << "Fail to encode TsPayloadPAT.programs[" << i << ']';
return -1;
}
p += programs[i].ByteSize();
// update the apply pid table.
packet()->channel_group()->set(programs[i].pid);
}
// update the apply pid table.
packet()->channel_group()->set(packet()->pid());
return 0;
}
TsPayloadPMTESInfo::TsPayloadPMTESInfo(TsStream st, TsPid epid)
: stream_type(st)
, elementary_PID(epid)
, ES_info_length(0)
, ES_info(NULL) {
}
TsPayloadPMTESInfo::~TsPayloadPMTESInfo() {
delete [] ES_info;
}
size_t TsPayloadPMTESInfo::ByteSize() const {
return 5 + ES_info_length;
}
int TsPayloadPMTESInfo::Encode(void* data) const {
char* p = (char*)data;
policy::Write1Byte(&p, stream_type);
int16_t epv = elementary_PID & 0x1FFF;
epv |= (const1_value0 << 13) & 0xE000;
policy::WriteBigEndian2Bytes(&p, epv);
int16_t eilv = ES_info_length & 0x0FFF;
eilv |= (const1_value1 << 12) & 0xF000;
policy::WriteBigEndian2Bytes(&p, eilv);
if (ES_info_length > 0) {
memcpy(p, ES_info, ES_info_length);
p += ES_info_length;
}
return 0;
}
TsPayloadPMT::TsPayloadPMT(const TsPacket* p)
: TsPayloadPSI(p)
, program_info_length(0)
, program_info_desc(NULL) {
}
TsPayloadPMT::~TsPayloadPMT() {
delete [] program_info_desc;
for (std::vector<TsPayloadPMTESInfo*>::iterator it = infos.begin();
it != infos.end(); ++it) {
delete *it;
}
infos.clear();
}
size_t TsPayloadPMT::PsiByteSize() const {
size_t sz = 9;
sz += program_info_length;
for (size_t i = 0; i < infos.size(); ++i) {
sz += infos[i]->ByteSize();
}
return sz;
}
int TsPayloadPMT::PsiEncode(void* data) const {
char* p = (char*)data;
policy::WriteBigEndian2Bytes(&p, program_number);
int8_t cniv = current_next_indicator & 0x01;
cniv |= (const1_value0 << 6) & 0xC0;
cniv |= (version_number << 1) & 0xFE;
policy::Write1Byte(&p, cniv);
policy::Write1Byte(&p, section_number);
policy::Write1Byte(&p, last_section_number);
int16_t ppv = PCR_PID & 0x1FFF;
ppv |= (const1_value1 << 13) & 0xE000;
policy::WriteBigEndian2Bytes(&p, ppv);
int16_t pilv = program_info_length & 0xFFF;
pilv |= (const1_value2 << 12) & 0xF000;
policy::WriteBigEndian2Bytes(&p, pilv);
if (program_info_length > 0) {
memcpy(p, program_info_desc, program_info_length);
p += program_info_length;
}
for (size_t i = 0; i < infos.size(); ++i) {
TsPayloadPMTESInfo* info = infos[i];
if (info->Encode(p) != 0) {
LOG(ERROR) << "Fail to encode TsPayloadPMT.infos[" << i << ']';
return -1;
}
p += info->ByteSize();
// update the apply pid table
switch (info->stream_type) {
case TS_STREAM_VIDEO_H264:
case TS_STREAM_VIDEO_MPEG4:
packet()->channel_group()->set(info->elementary_PID);
break;
case TS_STREAM_AUDIO_AAC:
case TS_STREAM_AUDIO_AC3:
case TS_STREAM_AUDIO_DTS:
case TS_STREAM_AUDIO_MP3:
packet()->channel_group()->set(info->elementary_PID);
break;
default:
LOG(WARNING) << "Drop pid=" << info->elementary_PID
<< " stream=" << info->stream_type;
break;
}
}
// update the apply pid table.
packet()->channel_group()->set(packet()->pid());
return 0;
}
TsStream FlvVideoCodec2TsStream(FlvVideoCodec codec, TsPid* pid) {
switch (codec) {
case FLV_VIDEO_AVC:
if (pid) {
*pid = TS_PID_VIDEO_AVC;
}
return TS_STREAM_VIDEO_H264;
case FLV_VIDEO_JPEG:
case FLV_VIDEO_SORENSON_H263:
case FLV_VIDEO_SCREEN_VIDEO:
case FLV_VIDEO_ON2_VP6:
case FLV_VIDEO_ON2_VP6_WITH_ALPHA_CHANNEL:
case FLV_VIDEO_SCREEN_VIDEO_V2:
case FLV_VIDEO_HEVC:
return TS_STREAM_RESERVED;
}
return TS_STREAM_RESERVED;
}
TsStream FlvAudioCodec2TsStream(FlvAudioCodec codec, TsPid* pid) {
switch (codec) {
case FLV_AUDIO_AAC:
if (pid) {
*pid = TS_PID_AUDIO_AAC;
}
return TS_STREAM_AUDIO_AAC;
case FLV_AUDIO_MP3:
if (pid) {
*pid = TS_PID_AUDIO_MP3;
}
return TS_STREAM_AUDIO_MP3;
case FLV_AUDIO_LINEAR_PCM_PLATFORM_ENDIAN:
case FLV_AUDIO_ADPCM:
case FLV_AUDIO_LINEAR_PCM_LITTLE_ENDIAN:
case FLV_AUDIO_NELLYMOSER_16KHZ_MONO:
case FLV_AUDIO_NELLYMOSER_8KHZ_MONO:
case FLV_AUDIO_NELLYMOSER:
case FLV_AUDIO_G711_ALAW_LOGARITHMIC_PCM:
case FLV_AUDIO_G711_MULAW_LOGARITHMIC_PCM:
case FLV_AUDIO_RESERVED:
case FLV_AUDIO_SPEEX:
case FLV_AUDIO_MP3_8KHZ:
case FLV_AUDIO_DEVICE_SPECIFIC_SOUND:
return TS_STREAM_RESERVED;
}
return TS_STREAM_RESERVED;
}
AACProfile AACObjectType2Profile(AACObjectType object_type) {
switch (object_type) {
case AAC_OBJECT_MAIN:
return AAC_PROFILE_MAIN;
case AAC_OBJECT_HE:
case AAC_OBJECT_HEV2:
case AAC_OBJECT_LC:
return AAC_PROFILE_LC;
case AAC_OBJECT_SSR:
return AAC_PROFILE_SSR;
}
return AAC_PROFILE_UNKNOWN;
}
TsWriter::TsWriter(butil::IOBuf* outbuf)
: _outbuf(outbuf)
, _nalu_format(AVC_NALU_FORMAT_UNKNOWN)
, _has_avc_seq_header(false)
, _has_aac_seq_header(false)
, _encoded_pat_pmt(false)
, _last_video_stream(TS_STREAM_VIDEO_H264)
, _last_video_pid(TS_PID_VIDEO_AVC)
, _last_audio_stream(TS_STREAM_AUDIO_AAC)
, _last_audio_pid(TS_PID_AUDIO_AAC)
, _discontinuity_counter(0) {
}
TsWriter::~TsWriter() {
}
butil::Status TsWriter::Write(const RtmpAudioMessage& msg) {
// ts support audio codec: aac/mp3
if (msg.codec != FLV_AUDIO_AAC && msg.codec != FLV_AUDIO_MP3) {
return butil::Status(EINVAL, "Unsupported codec=%s",
FlvAudioCodec2Str(msg.codec));
}
const int64_t dts = static_cast<int64_t>(msg.timestamp) * 90;
TsMessage tsmsg;
tsmsg.write_pcr = false;
tsmsg.dts = dts;
tsmsg.pts = dts;
tsmsg.sid = TS_PES_STREAM_ID_AUDIO_COMMON;
if (msg.codec == FLV_AUDIO_AAC) {
RtmpAACMessage aac_msg;
butil::Status st = aac_msg.Create(msg);
if (!st.ok()) {
return st;
}
// ignore sequence header
if (aac_msg.packet_type == FLV_AAC_PACKET_SEQUENCE_HEADER) {
butil::Status st2 = _aac_seq_header.Create(aac_msg.data);
if (!st2.ok()) {
return st2;
}
_has_aac_seq_header = true;
++_discontinuity_counter;
return butil::Status::OK();
}
if (!_has_aac_seq_header) {
return butil::Status(EINVAL, "Lack of AAC sequence header");
}
if (aac_msg.data.size() > 0x1fff) {
return butil::Status(EINVAL, "Invalid AAC data_size=%" PRIu64,
(uint64_t)aac_msg.data.size());
}
// the frame length is the AAC raw data plus the adts header size.
const int32_t frame_length = aac_msg.data.size() + 7;
// AAC-ADTS
// 6.2 Audio Data Transport Stream, ADTS
// in aac-iso-13818-7.pdf, page 26.
// fixed 7bytes header
uint8_t adts_header[7] = {0xff, 0xf9, 0x00, 0x00, 0x00, 0x0f, 0xfc};
// profile, 2bits
const AACProfile aac_profile =
AACObjectType2Profile(_aac_seq_header.aac_object);
if (aac_profile == AAC_PROFILE_UNKNOWN) {
return butil::Status(EINVAL, "Invalid aac_object=%d",
(int)_aac_seq_header.aac_object);
}
adts_header[2] = (aac_profile << 6) & 0xc0;
// sampling_frequency_index 4bits
adts_header[2] |= (_aac_seq_header.aac_sample_rate << 2) & 0x3c;
// channel_configuration 3bits
adts_header[2] |= (_aac_seq_header.aac_channels >> 2) & 0x01;
adts_header[3] = (_aac_seq_header.aac_channels << 6) & 0xc0;
// frame_length 13bits
adts_header[3] |= (frame_length >> 11) & 0x03;
adts_header[4] = (frame_length >> 3) & 0xff;
adts_header[5] = ((frame_length << 5) & 0xe0);
// adts_buffer_fullness; //11bits
adts_header[5] |= 0x1f;
tsmsg.payload.append(adts_header, sizeof(adts_header));
tsmsg.payload.append(aac_msg.data);
} else if (msg.codec == FLV_AUDIO_MP3) {
tsmsg.payload.append(msg.data);
}
TsPid apid = TS_PID_NULL;
TsStream as = FlvAudioCodec2TsStream(msg.codec, &apid);
if (as == TS_STREAM_RESERVED) {
return butil::Status(EINVAL, "Unsupported audio codec=%s",
FlvAudioCodec2Str(msg.codec));
}
return Encode(&tsmsg, as, apid);
}
// mux the samples in annexb format,
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324.
// 00 00 00 01 // header
// xxxxxxx // data bytes
// 00 00 01 // continue header
// xxxxxxx // data bytes.
//
// nal_unit_type specifies the type of RBSP data structure contained in
// the NAL unit as specified in Table 7-1.
// Table 7-1 - NAL unit type codes, syntax element categories, and NAL
// unit type classes. H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
// 1, Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
// 2, Coded slice data partition A slice_data_partition_a_layer_rbsp( )
// 3, Coded slice data partition B slice_data_partition_b_layer_rbsp( )
// 4, Coded slice data partition C slice_data_partition_c_layer_rbsp( )
// 5, Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
// 6, Supplemental enhancement information (SEI) sei_rbsp( )
// 7, Sequence parameter set seq_parameter_set_rbsp( )
// 8, Picture parameter set pic_parameter_set_rbsp( )
// 9, Access unit delimiter access_unit_delimiter_rbsp( )
// 10, End of sequence end_of_seq_rbsp( )
// 11, End of stream end_of_stream_rbsp( )
// 12, Filler data filler_data_rbsp( )
// 13, Sequence parameter set extension seq_parameter_set_extension_rbsp( )
// 14, Prefix NAL unit prefix_nal_unit_rbsp( )
// 15, Subset sequence parameter set subset_seq_parameter_set_rbsp( )
// 19, Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
// 20, Coded slice extension slice_layer_extension_rbsp( )
// the first ts message of apple sample:
// annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
// annexb 4B header, 19B sps(nal_unit_type:7)
// annexb 3B header, 4B pps(nal_unit_type:8)
// annexb 3B header, 12B nalu(nal_unit_type:6)
// annexb 3B header, 21B nalu(nal_unit_type:6)
// annexb 3B header, 2762B nalu(nal_unit_type:5)
// annexb 3B header, 3535B nalu(nal_unit_type:5)
// the second ts message of apple ts sample:
// annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
// annexb 3B header, 21B nalu(nal_unit_type:6)
// annexb 3B header, 379B nalu(nal_unit_type:1)
// annexb 3B header, 406B nalu(nal_unit_type:1)
static const uint8_t fresh_nalu_header[] = { 0x00, 0x00, 0x00, 0x01 };
static const uint8_t cont_nalu_header[] = { 0x00, 0x00, 0x01 };
// the aud(access unit delimiter) before each frame.
// 7.3.2.4 Access unit delimiter RBSP syntax
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 66.
// primary_pic_type u(3), the first 3bits, primary_pic_type indicates
// that the slice_type values for all slices of the primary coded picture
// are members of the set listed in Table 7-5 for the given value of
// primary_pic_type.
// 0, slice_type 2, 7
// 1, slice_type 0, 2, 5, 7
// 2, slice_type 0, 1, 2, 5, 6, 7
// 3, slice_type 4, 9
// 4, slice_type 3, 4, 8, 9
// 5, slice_type 2, 4, 7, 9
// 6, slice_type 0, 2, 3, 4, 5, 7, 8, 9
// 7, slice_type 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
// 7.4.2.4 Access unit delimiter RBSP semantics
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 102.
//
// slice_type specifies the coding type of the slice according to Table 7-6.
// 0, P (P slice)
// 1, B (B slice)
// 2, I (I slice)
// 3, SP (SP slice)
// 4, SI (SI slice)
// 5, P (P slice)
// 6, B (B slice)
// 7, I (I slice)
// 8, SP (SP slice)
// 9, SI (SI slice)
// H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 105.
//static const uint8_t aud_nalu_7[] = { 0x09, 0xf0};
static const uint8_t fresh_nalu_header_and_aud_nalu_7[] =
{ 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0};
butil::Status TsWriter::Write(const RtmpVideoMessage& msg) {
if (msg.frame_type == FLV_VIDEO_FRAME_INFOFRAME) {
// Ignore info frame.
return butil::Status::OK();
}
if (msg.codec != FLV_VIDEO_AVC) {
return butil::Status(EINVAL, "video_codec=%s is not AVC",
FlvVideoCodec2Str(msg.codec));
}
RtmpAVCMessage avc_msg;
butil::Status st = avc_msg.Create(msg);
if (!st.ok()) {
return st;
}
// ignore sequence header
if (avc_msg.frame_type == FLV_VIDEO_FRAME_KEYFRAME &&
avc_msg.packet_type == FLV_AVC_PACKET_SEQUENCE_HEADER) {
butil::Status st2 = _avc_seq_header.Create(avc_msg.data);
if (!st2.ok()) {
return st2;
}
_has_avc_seq_header = true;
++_discontinuity_counter;
return butil::Status::OK();
}
if (!_has_avc_seq_header) {
return butil::Status(EINVAL, "Lack of AVC sequence header");
}
const int64_t dts = static_cast<int64_t>(avc_msg.timestamp) * 90;
TsMessage tsmsg;
tsmsg.write_pcr = (msg.frame_type == FLV_VIDEO_FRAME_KEYFRAME);
tsmsg.dts = dts;
tsmsg.pts = dts + static_cast<int64_t>(avc_msg.composition_time) * 90;
tsmsg.sid = TS_PES_STREAM_ID_VIDEO_COMMON;
// always append a aud nalu for each frame.
tsmsg.payload.append(fresh_nalu_header_and_aud_nalu_7,
arraysize(fresh_nalu_header_and_aud_nalu_7));
butil::IOBuf nalus;
bool has_idr = false;
for (AVCNaluIterator it(&avc_msg.data, _avc_seq_header.length_size_minus1,
&_nalu_format); it != NULL; ++it) {
// ignore SPS/PPS/AUD
switch (it.nalu_type()) {
case AVC_NALU_IDR:
has_idr = true;
break;
case AVC_NALU_SPS:
case AVC_NALU_PPS:
case AVC_NALU_ACCESSUNITDELIMITER:
continue;
default:
break;
}
// append cont nalu header before NAL units.
nalus.append(cont_nalu_header, 3);
nalus.append(*it);
}
// when ts message(samples) contains IDR, insert sps+pps.
if (has_idr) {
bool first = true;
for (size_t i = 0; i < _avc_seq_header.sps_list.size(); ++i) {
if (first) {
tsmsg.payload.append(fresh_nalu_header, arraysize(fresh_nalu_header));
first = false;
} else {
tsmsg.payload.append(cont_nalu_header, arraysize(cont_nalu_header));
}
tsmsg.payload.append(_avc_seq_header.sps_list[i]);
RPC_VLOG << "Append sps[" << i << "]=" << _avc_seq_header.sps_list[i].size();
}
for (size_t i = 0; i < _avc_seq_header.pps_list.size(); ++i) {
if (first) {
tsmsg.payload.append(fresh_nalu_header, arraysize(fresh_nalu_header));
first = false;
} else {
tsmsg.payload.append(cont_nalu_header, arraysize(cont_nalu_header));
}
tsmsg.payload.append(_avc_seq_header.pps_list[i]);
RPC_VLOG << "Append pps[" << i << "]=" << _avc_seq_header.pps_list[i].size();
}
}
tsmsg.payload.append(nalus);
TsPid vpid = TS_PID_PAT;
TsStream vs = FlvVideoCodec2TsStream(msg.codec, &vpid);
if (vs == TS_STREAM_RESERVED) {
return butil::Status(EINVAL, "Unsupported video codec=%s",
FlvVideoCodec2Str(msg.codec));
}
return Encode(&tsmsg, vs, vpid);
}
butil::Status
TsWriter::EncodePATPMT(TsStream vs, TsPid vpid, TsStream as, TsPid apid) {
char buf[TS_PACKET_SIZE];
TsPacket pat(&_tschan_group);
pat.CreateAsPAT(TS_PMT_NUMBER, TS_PID_PMT);
// set the left bytes with 0xFF.
const size_t size1 = pat.ByteSize();
CHECK_LT(size1, TS_PACKET_SIZE);
memset(buf, 0xFF, TS_PACKET_SIZE);
if (pat.Encode(buf) != 0) {
return butil::Status(EINVAL, "Fail to encode PAT");
}
_outbuf->append(buf, TS_PACKET_SIZE);
TsPacket pmt(&_tschan_group);
if (pmt.CreateAsPMT(TS_PMT_NUMBER, TS_PID_PMT, vpid, vs, apid, as) != 0) {
return butil::Status(EINVAL, "Fail to CreateAsPMT");
}
// set the left bytes with 0xFF.
const size_t size2 = pmt.ByteSize();
CHECK_LT(size2, TS_PACKET_SIZE);
memset(buf, 0xFF, TS_PACKET_SIZE);
if (pmt.Encode(buf) != 0) {
return butil::Status(EINVAL, "Fail to encode PMT");
}
_outbuf->append(buf, TS_PACKET_SIZE);
return butil::Status::OK();
}
butil::Status TsWriter::Encode(TsMessage* msg, TsStream stream, TsPid pid) {
if (stream == TS_STREAM_RESERVED) {
return butil::Status(EINVAL, "Invalid stream=%d", (int)stream);
}
// Encode the media frame to PES packets over TS.
bool add_pat_pmt = false;
if (is_audio(msg->sid)) {
if (stream != _last_audio_stream) {
_last_audio_stream = stream;
_last_audio_pid = pid;
add_pat_pmt = true;
}
} else if (is_video(msg->sid)) {
if (stream != _last_video_stream) {
_last_video_stream = stream;
_last_video_pid = pid;
add_pat_pmt = true;
}
} else {
return butil::Status(EINVAL, "Unknown stream_id=%d", (int)msg->sid);
}
if (!_encoded_pat_pmt) {
_encoded_pat_pmt = true;
add_pat_pmt = true;
}
if (add_pat_pmt) {
butil::Status st = EncodePATPMT(_last_video_stream, _last_video_pid,
_last_audio_stream, _last_audio_pid);
if (!st.ok()) {
return st;
}
}
return EncodePES(msg, stream, pid,
(_last_video_stream == TS_STREAM_RESERVED));
}
butil::Status TsWriter::EncodePES(TsMessage* msg, TsStream sid, TsPid pid,
bool pure_audio) {
if (msg->payload.empty()) {
return butil::Status::OK();
}
if (sid != TS_STREAM_VIDEO_H264 &&
sid != TS_STREAM_AUDIO_MP3 &&
sid != TS_STREAM_AUDIO_AAC) {
LOG(WARNING) << "Ignore unknown stream_id=" << sid;
return butil::Status::OK();
}
TsChannel* channel = _tschan_group.get(pid);
if (channel == NULL) {
return butil::Status(EINVAL, "Fail to get channel on pid=%d", (int)pid);
}
bool first_msg = true;
while (!msg->payload.empty()) {
TsPacket pkt(&_tschan_group);
if (first_msg) {
first_msg = false;
bool write_pcr = msg->write_pcr;
// for pure audio, always write pcr.
// TODO: maybe only need to write at begin and end of ts.
if (pure_audio && is_audio(msg->sid)) {
write_pcr = true;
}
// it's ok to set pcr equals to dts,
int64_t pcr = write_pcr ? msg->dts : -1;
// TODO: FIXME: figure out why use discontinuity of msg
pkt.CreateAsPESFirst(pid, msg->sid, channel->continuity_counter++,
msg->is_discontinuity, pcr, msg->dts,
msg->pts, msg->payload.size());
} else {
pkt.CreateAsPESContinue(pid, channel->continuity_counter++);
}
char buf[TS_PACKET_SIZE];
// set the left bytes with 0xFF.
size_t pkt_size = pkt.ByteSize();
CHECK_LT(pkt_size, TS_PACKET_SIZE);
size_t left = std::min(msg->payload.size(), TS_PACKET_SIZE - pkt_size);
const size_t nb_stuffings = TS_PACKET_SIZE - pkt_size - left;
if (nb_stuffings > 0) {
// set all bytes to stuffings.
memset(buf, 0xFF, TS_PACKET_SIZE);
pkt.AddPadding(nb_stuffings);
pkt_size = pkt.ByteSize(); // size changed, recalculate.
CHECK_LT(pkt_size, TS_PACKET_SIZE);
left = std::min(msg->payload.size(), TS_PACKET_SIZE - pkt_size);
if (TS_PACKET_SIZE != pkt_size + left) {
LOG(ERROR) << "pkt_size=" << pkt_size << " left=" << left
<< " stuffing=" << nb_stuffings << " payload="
<< msg->payload.size();
}
}
msg->payload.cutn(buf + pkt_size, left);
if (pkt.Encode(buf) != 0) {
return butil::Status(EINVAL, "Fail to encode PES");
}
_outbuf->append(buf, TS_PACKET_SIZE);
}
return butil::Status::OK();
}
} // namespace brpc