// 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
