winlin

for #250, decode the PAT of PSI ts packet.

@@ -486,6 +486,8 @@ Supported operating systems and hardware: @@ -486,6 +486,8 @@ Supported operating systems and hardware:
486 ). 486 ).
487 1. Support HLS(h.264+mp3) streaming, read 487 1. Support HLS(h.264+mp3) streaming, read
488 [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301). 488 [#301](https://github.com/winlinvip/simple-rtmp-server/issues/301).
  489 +1. [dev] Support push MPEG-TS over UDP to SRS, read
  490 +[#250](https://github.com/winlinvip/simple-rtmp-server/issues/250).
489 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). 491 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech).
490 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). 492 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92).
491 1. [no-plan] Support multiple processes, for both origin and edge 493 1. [no-plan] Support multiple processes, for both origin and edge
@@ -35,19 +35,20 @@ using namespace std; @@ -35,19 +35,20 @@ using namespace std;
35 #include <srs_kernel_ts.hpp> 35 #include <srs_kernel_ts.hpp>
36 #include <srs_kernel_stream.hpp> 36 #include <srs_kernel_stream.hpp>
37 #include <srs_kernel_ts.hpp> 37 #include <srs_kernel_ts.hpp>
38 -#include <srs_core_autofree.hpp>  
39 38
40 #ifdef SRS_AUTO_STREAM_CASTER 39 #ifdef SRS_AUTO_STREAM_CASTER
41 40
42 SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) 41 SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c)
43 { 42 {
44 stream = new SrsStream(); 43 stream = new SrsStream();
  44 + context = new SrsTsContext();
45 output = _srs_config->get_stream_caster_output(c); 45 output = _srs_config->get_stream_caster_output(c);
46 } 46 }
47 47
48 SrsMpegtsOverUdp::~SrsMpegtsOverUdp() 48 SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
49 { 49 {
50 srs_freep(stream); 50 srs_freep(stream);
  51 + srs_freep(context);
51 } 52 }
52 53
53 int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) 54 int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf)
@@ -85,10 +86,7 @@ int SrsMpegtsOverUdp::on_ts_packet(SrsStream* stream) @@ -85,10 +86,7 @@ int SrsMpegtsOverUdp::on_ts_packet(SrsStream* stream)
85 { 86 {
86 int ret = ERROR_SUCCESS; 87 int ret = ERROR_SUCCESS;
87 88
88 - SrsTsPacket* packet = new SrsTsPacket();  
89 - SrsAutoFree(SrsTsPacket, packet);  
90 -  
91 - if ((ret = packet->decode(stream)) != ERROR_SUCCESS) { 89 + if ((ret = context->decode(stream)) != ERROR_SUCCESS) {
92 srs_error("mpegts: decode ts packet failed. ret=%d", ret); 90 srs_error("mpegts: decode ts packet failed. ret=%d", ret);
93 return ret; 91 return ret;
94 } 92 }
@@ -34,6 +34,7 @@ class sockaddr_in; @@ -34,6 +34,7 @@ class sockaddr_in;
34 #include <string> 34 #include <string>
35 35
36 class SrsStream; 36 class SrsStream;
  37 +class SrsTsContext;
37 class SrsConfDirective; 38 class SrsConfDirective;
38 39
39 #ifdef SRS_AUTO_STREAM_CASTER 40 #ifdef SRS_AUTO_STREAM_CASTER
@@ -45,6 +46,7 @@ class SrsMpegtsOverUdp @@ -45,6 +46,7 @@ class SrsMpegtsOverUdp
45 { 46 {
46 private: 47 private:
47 SrsStream* stream; 48 SrsStream* stream;
  49 + SrsTsContext* context;
48 std::string output; 50 std::string output;
49 public: 51 public:
50 SrsMpegtsOverUdp(SrsConfDirective* c); 52 SrsMpegtsOverUdp(SrsConfDirective* c);
@@ -39,6 +39,7 @@ using namespace std; @@ -39,6 +39,7 @@ using namespace std;
39 #include <srs_kernel_buffer.hpp> 39 #include <srs_kernel_buffer.hpp>
40 #include <srs_kernel_utility.hpp> 40 #include <srs_kernel_utility.hpp>
41 #include <srs_kernel_stream.hpp> 41 #include <srs_kernel_stream.hpp>
  42 +#include <srs_core_autofree.hpp>
42 43
43 // in ms, for HLS aac sync time. 44 // in ms, for HLS aac sync time.
44 #define SRS_CONF_DEFAULT_AAC_SYNC 100 45 #define SRS_CONF_DEFAULT_AAC_SYNC 100
@@ -401,6 +402,33 @@ SrsMpegtsFrame::SrsMpegtsFrame() @@ -401,6 +402,33 @@ SrsMpegtsFrame::SrsMpegtsFrame()
401 key = false; 402 key = false;
402 } 403 }
403 404
  405 +SrsTsContext::SrsTsContext()
  406 +{
  407 +}
  408 +
  409 +SrsTsContext::~SrsTsContext()
  410 +{
  411 +}
  412 +
  413 +int SrsTsContext::decode(SrsStream* stream)
  414 +{
  415 + int ret = ERROR_SUCCESS;
  416 +
  417 + // parse util EOF of stream.
  418 + // for example, parse multiple times for the PES_packet_length(0) packet.
  419 + while (!stream->empty()) {
  420 + SrsTsPacket* packet = new SrsTsPacket();
  421 + SrsAutoFree(SrsTsPacket, packet);
  422 +
  423 + if ((ret = packet->decode(stream)) != ERROR_SUCCESS) {
  424 + srs_error("mpegts: decode ts packet failed. ret=%d", ret);
  425 + return ret;
  426 + }
  427 + }
  428 +
  429 + return ret;
  430 +}
  431 +
404 SrsTsPacket::SrsTsPacket() 432 SrsTsPacket::SrsTsPacket()
405 { 433 {
406 sync_byte = 0; 434 sync_byte = 0;
@@ -412,11 +440,13 @@ SrsTsPacket::SrsTsPacket() @@ -412,11 +440,13 @@ SrsTsPacket::SrsTsPacket()
412 adaption_field_control = SrsTsAdaptationFieldTypeReserved; 440 adaption_field_control = SrsTsAdaptationFieldTypeReserved;
413 continuity_counter = 0; 441 continuity_counter = 0;
414 adaptation_field = NULL; 442 adaptation_field = NULL;
  443 + payload = NULL;
415 } 444 }
416 445
417 SrsTsPacket::~SrsTsPacket() 446 SrsTsPacket::~SrsTsPacket()
418 { 447 {
419 srs_freep(adaptation_field); 448 srs_freep(adaptation_field);
  449 + srs_freep(payload);
420 } 450 }
421 451
422 int SrsTsPacket::decode(SrsStream* stream) 452 int SrsTsPacket::decode(SrsStream* stream)
@@ -471,6 +501,23 @@ int SrsTsPacket::decode(SrsStream* stream) @@ -471,6 +501,23 @@ int SrsTsPacket::decode(SrsStream* stream)
471 // calc the user defined data size for payload. 501 // calc the user defined data size for payload.
472 int nb_payload = SRS_TS_PACKET_SIZE - (stream->pos() - pos); 502 int nb_payload = SRS_TS_PACKET_SIZE - (stream->pos() - pos);
473 503
  504 + // optional: payload.
  505 + if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) {
  506 + if (pid == SrsTsPidPAT) {
  507 + // 2.4.4.3 Program association Table
  508 + srs_freep(payload);
  509 + payload = new SrsTsPayloadPAT(this);
  510 + } else {
  511 + // left bytes as reserved.
  512 + stream->skip(nb_payload);
  513 + }
  514 +
  515 + if (payload && (ret = payload->decode(stream)) != ERROR_SUCCESS) {
  516 + srs_error("ts: demux payload failed. ret=%d", ret);
  517 + return ret;
  518 + }
  519 + }
  520 +
474 return ret; 521 return ret;
475 } 522 }
476 523
@@ -713,6 +760,145 @@ int SrsTsAdaptationField::decode(SrsStream* stream) @@ -713,6 +760,145 @@ int SrsTsAdaptationField::decode(SrsStream* stream)
713 return ret; 760 return ret;
714 } 761 }
715 762
  763 +SrsTsPayloadPATProgram::SrsTsPayloadPATProgram()
  764 +{
  765 + number = 0;
  766 + pid = 0;
  767 +}
  768 +
  769 +SrsTsPayloadPATProgram::~SrsTsPayloadPATProgram()
  770 +{
  771 +}
  772 +
  773 +SrsTsPayload::SrsTsPayload(SrsTsPacket* p)
  774 +{
  775 + packet = p;
  776 +}
  777 +
  778 +SrsTsPayload::~SrsTsPayload()
  779 +{
  780 +}
  781 +
  782 +SrsTsPayloadPSI::SrsTsPayloadPSI(SrsTsPacket* p) : SrsTsPayload(p)
  783 +{
  784 + pointer_field = 0;
  785 +}
  786 +
  787 +SrsTsPayloadPSI::~SrsTsPayloadPSI()
  788 +{
  789 +}
  790 +
  791 +int SrsTsPayloadPSI::decode(SrsStream* stream)
  792 +{
  793 + int ret = ERROR_SUCCESS;
  794 +
  795 + /**
  796 + * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following
  797 + * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value
  798 + * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the
  799 + * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0',
  800 + * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of
  801 + * stream_type 5 (refer to Table 2-29).
  802 + */
  803 + if (packet->payload_unit_start_indicator) {
  804 + if (!stream->require(1)) {
  805 + ret = ERROR_STREAM_CASTER_TS_AF;
  806 + srs_error("ts: demux PSI failed. ret=%d", ret);
  807 + return ret;
  808 + }
  809 + pointer_field = stream->read_1bytes();
  810 + }
  811 +
  812 + return ret;
  813 +}
  814 +
  815 +SrsTsPayloadPAT::SrsTsPayloadPAT(SrsTsPacket* p) : SrsTsPayloadPSI(p)
  816 +{
  817 + nb_programs = 0;
  818 + programs = NULL;
  819 +}
  820 +
  821 +SrsTsPayloadPAT::~SrsTsPayloadPAT()
  822 +{
  823 + srs_freep(programs);
  824 +}
  825 +
  826 +int SrsTsPayloadPAT::decode(SrsStream* stream)
  827 +{
  828 + int ret = ERROR_SUCCESS;
  829 +
  830 + if ((ret = SrsTsPayloadPSI::decode(stream)) != ERROR_SUCCESS) {
  831 + return ret;
  832 + }
  833 +
  834 + // atleast 8B without programs and crc32
  835 + if (!stream->require(8)) {
  836 + ret = ERROR_STREAM_CASTER_TS_AF;
  837 + srs_error("ts: demux PAT failed. ret=%d", ret);
  838 + return ret;
  839 + }
  840 + // 1B
  841 + table_id = (SrsTsPsiId)stream->read_1bytes();
  842 +
  843 + // 2B
  844 + section_length = stream->read_2bytes();
  845 +
  846 + section_syntax_indicator = (section_length >> 15) & 0x01;
  847 + const0_value = (section_length >> 14) & 0x01;
  848 + section_length &= 0x0FFF;
  849 +
  850 + if (!stream->require(section_length)) {
  851 + ret = ERROR_STREAM_CASTER_TS_AF;
  852 + srs_error("ts: demux PAT section failed. ret=%d", ret);
  853 + return ret;
  854 + }
  855 + int pos = stream->pos();
  856 +
  857 + // 2B
  858 + transport_stream_id = stream->read_2bytes();
  859 +
  860 + // 1B
  861 + current_next_indicator = stream->read_1bytes();
  862 +
  863 + version_number = (current_next_indicator >> 1) & 0x1F;
  864 + current_next_indicator &= 0x01;
  865 +
  866 + // TODO: FIXME: check the indicator.
  867 +
  868 + // 1B
  869 + section_number = stream->read_1bytes();
  870 + // 1B
  871 + last_section_number = stream->read_1bytes();
  872 +
  873 + // multiple 4B program data.
  874 + int program_bytes = section_length - 4 - (stream->pos() - pos);
  875 + nb_programs = program_bytes / 4;
  876 + if (nb_programs > 0) {
  877 + srs_freep(programs);
  878 + programs = new SrsTsPayloadPATProgram[nb_programs];
  879 +
  880 + for (int i = 0; i < nb_programs; i++) {
  881 + SrsTsPayloadPATProgram* program = programs + i;
  882 +
  883 + int tmpv = stream->read_4bytes();
  884 + program->number = (int16_t)((tmpv >> 16) & 0xFFFF);
  885 + program->pid = (int16_t)(tmpv & 0x1FFF);
  886 + }
  887 + }
  888 +
  889 + // 4B
  890 + if (!stream->require(4)) {
  891 + ret = ERROR_STREAM_CASTER_TS_AF;
  892 + srs_error("ts: demux PAT crc32 failed. ret=%d", ret);
  893 + return ret;
  894 + }
  895 + CRC_32 = stream->read_4bytes();
  896 +
  897 + // TODO: FIXME: verfy crc32.
  898 +
  899 + return ret;
  900 +}
  901 +
716 SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w) 902 SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w)
717 { 903 {
718 writer = w; 904 writer = w;
@@ -42,6 +42,7 @@ class SrsAvcAacCodec; @@ -42,6 +42,7 @@ class SrsAvcAacCodec;
42 class SrsCodecSample; 42 class SrsCodecSample;
43 class SrsSimpleBuffer; 43 class SrsSimpleBuffer;
44 class SrsTsAdaptationField; 44 class SrsTsAdaptationField;
  45 +class SrsTsPayload;
45 46
46 // Transport Stream packets are 188 bytes in length. 47 // Transport Stream packets are 188 bytes in length.
47 #define SRS_TS_PACKET_SIZE 188 48 #define SRS_TS_PACKET_SIZE 188
@@ -109,6 +110,22 @@ enum SrsTsAdaptationFieldType @@ -109,6 +110,22 @@ enum SrsTsAdaptationFieldType
109 }; 110 };
110 111
111 /** 112 /**
  113 +* the context of ts, to decode the ts stream.
  114 +*/
  115 +class SrsTsContext
  116 +{
  117 +public:
  118 + SrsTsContext();
  119 + virtual ~SrsTsContext();
  120 +public:
  121 + /**
  122 + * the stream contains only one ts packet.
  123 + * @remark we will consume all bytes in stream.
  124 + */
  125 + virtual int decode(SrsStream* stream);
  126 +};
  127 +
  128 +/**
112 * the packet in ts stream, 129 * the packet in ts stream,
113 * 2.4.3.2 Transport Stream packet layer, hls-mpeg-ts-iso13818-1.pdf, page 36 130 * 2.4.3.2 Transport Stream packet layer, hls-mpeg-ts-iso13818-1.pdf, page 36
114 * Transport Stream packets shall be 188 bytes long. 131 * Transport Stream packets shall be 188 bytes long.
@@ -203,14 +220,11 @@ public: @@ -203,14 +220,11 @@ public:
203 u_int8_t continuity_counter; //4bits 220 u_int8_t continuity_counter; //4bits
204 private: 221 private:
205 SrsTsAdaptationField* adaptation_field; 222 SrsTsAdaptationField* adaptation_field;
  223 + SrsTsPayload* payload;
206 public: 224 public:
207 SrsTsPacket(); 225 SrsTsPacket();
208 virtual ~SrsTsPacket(); 226 virtual ~SrsTsPacket();
209 public: 227 public:
210 - /**  
211 - * the stream contains only one ts packet.  
212 - * @remark we will consume all bytes in stream.  
213 - */  
214 virtual int decode(SrsStream* stream); 228 virtual int decode(SrsStream* stream);
215 }; 229 };
216 230
@@ -516,6 +530,189 @@ public: @@ -516,6 +530,189 @@ public:
516 }; 530 };
517 531
518 /** 532 /**
  533 +* 2.4.4.4 Table_id assignments, hls-mpeg-ts-iso13818-1.pdf, page 62
  534 +* The table_id field identifies the contents of a Transport Stream PSI section as shown in Table 2-26.
  535 +*/
  536 +enum SrsTsPsiId
  537 +{
  538 + // program_association_section
  539 + SrsTsPsiIdPas = 0x00,
  540 + // conditional_access_section (CA_section)
  541 + SrsTsPsiIdCas = 0x01,
  542 + // TS_program_map_section
  543 + SrsTsPsiIdPms = 0x02,
  544 + // TS_description_section
  545 + SrsTsPsiIdDs = 0x03,
  546 + // ISO_IEC_14496_scene_description_section
  547 + SrsTsPsiIdSds = 0x04,
  548 + // ISO_IEC_14496_object_descriptor_section
  549 + SrsTsPsiIdOds = 0x05,
  550 + // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 reserved
  551 + SrsTsPsiIdIso138181Start = 0x06,
  552 + SrsTsPsiIdIso138181End = 0x37,
  553 + // Defined in ISO/IEC 13818-6
  554 + SrsTsPsiIdIso138186Start = 0x38,
  555 + SrsTsPsiIdIso138186End = 0x3F,
  556 + // User private
  557 + SrsTsPsiIdUserStart = 0x40,
  558 + SrsTsPsiIdUserEnd = 0xFE,
  559 + // forbidden
  560 + SrsTsPsiIdForbidden = 0xFF,
  561 +};
  562 +
  563 +/**
  564 +* the program of PAT of PSI ts packet.
  565 +*/
  566 +class SrsTsPayloadPATProgram
  567 +{
  568 +public:
  569 + // 4B
  570 + /**
  571 + * Program_number is a 16-bit field. It specifies the program to which the program_map_PID is
  572 + * applicable. When set to 0x0000, then the following PID reference shall be the network PID. For all other cases the value
  573 + * of this field is user defined. This field shall not take any single value more than once within one version of the Program
  574 + * Association Table.
  575 + */
  576 + int16_t number; // 16bits
  577 + // reserved 3bits
  578 + /**
  579 + * program_map_PID/network_PID 13bits
  580 + * network_PID ¨C The network_PID is a 13-bit field, which is used only in conjunction with the value of the
  581 + * program_number set to 0x0000, specifies the PID of the Transport Stream packets which shall contain the Network
  582 + * Information Table. The value of the network_PID field is defined by the user, but shall only take values as specified in
  583 + * Table 2-3. The presence of the network_PID is optional.
  584 + */
  585 + int16_t pid;
  586 +public:
  587 + SrsTsPayloadPATProgram();
  588 + virtual ~SrsTsPayloadPATProgram();
  589 +};
  590 +
  591 +/**
  592 +* the payload of ts packet, can be PES or PSI payload.
  593 +*/
  594 +class SrsTsPayload
  595 +{
  596 +protected:
  597 + SrsTsPacket* packet;
  598 +public:
  599 + SrsTsPayload(SrsTsPacket* p);
  600 + virtual ~SrsTsPayload();
  601 +public:
  602 + virtual int decode(SrsStream* stream) = 0;
  603 +};
  604 +
  605 +/**
  606 +* the PSI payload of ts packet.
  607 +* 2.4.4 Program specific information, hls-mpeg-ts-iso13818-1.pdf, page 59
  608 +*/
  609 +class SrsTsPayloadPSI : public SrsTsPayload
  610 +{
  611 +public:
  612 + // 1B
  613 + /**
  614 + * This is an 8-bit field whose value shall be the number of bytes, immediately following the pointer_field
  615 + * until the first byte of the first section that is present in the payload of the Transport Stream packet (so a value of 0x00 in
  616 + * the pointer_field indicates that the section starts immediately after the pointer_field). When at least one section begins in
  617 + * a given Transport Stream packet, then the payload_unit_start_indicator (refer to 2.4.3.2) shall be set to 1 and the first
  618 + * byte of the payload of that Transport Stream packet shall contain the pointer. When no section begins in a given
  619 + * Transport Stream packet, then the payload_unit_start_indicator shall be set to 0 and no pointer shall be sent in the
  620 + * payload of that packet.
  621 + */
  622 + int8_t pointer_field;
  623 +public:
  624 + SrsTsPayloadPSI(SrsTsPacket* p);
  625 + virtual ~SrsTsPayloadPSI();
  626 +public:
  627 + virtual int decode(SrsStream* stream);
  628 +};
  629 +
  630 +/**
  631 +* the PAT payload of PSI ts packet.
  632 +* 2.4.4.3 Program association Table, hls-mpeg-ts-iso13818-1.pdf, page 61
  633 +* The Program Association Table provides the correspondence between a program_number and the PID value of the
  634 +* Transport Stream packets which carry the program definition. The program_number is the numeric label associated with
  635 +* a program.
  636 +*/
  637 +class SrsTsPayloadPAT : public SrsTsPayloadPSI
  638 +{
  639 +public:
  640 + // 1B
  641 + /**
  642 + * This is an 8-bit field, which shall be set to 0x00 as shown in Table 2-26.
  643 + */
  644 + SrsTsPsiId table_id; //8bits
  645 +
  646 + // 2B
  647 + /**
  648 + * The section_syntax_indicator is a 1-bit field which shall be set to '1'.
  649 + */
  650 + int8_t section_syntax_indicator; //1bit
  651 + /**
  652 + * const value, must be '0'
  653 + */
  654 + int8_t const0_value; //1bit
  655 + // 2bits reserved.
  656 + /**
  657 + * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number
  658 + * of bytes of the section, starting immediately following the section_length field, and including the CRC. The value in this
  659 + * field shall not exceed 1021 (0x3FD).
  660 + */
  661 + u_int16_t section_length; //12bits
  662 +
  663 + // 2B
  664 + /**
  665 + * This is a 16-bit field which serves as a label to identify this Transport Stream from any other
  666 + * multiplex within a network. Its value is defined by the user.
  667 + */
  668 + u_int16_t transport_stream_id; //16bits
  669 +
  670 + // 1B
  671 + // 2bits reerverd.
  672 + /**
  673 + * This 5-bit field is the version number of the whole Program Association Table. The version number
  674 + * shall be incremented by 1 modulo 32 whenever the definition of the Program Association Table changes. When the
  675 + * current_next_indicator is set to '1', then the version_number shall be that of the currently applicable Program Association
  676 + * Table. When the current_next_indicator is set to '0', then the version_number shall be that of the next applicable Program
  677 + * Association Table.
  678 + */
  679 + int8_t version_number; //5bits
  680 + /**
  681 + * A 1-bit indicator, which when set to '1' indicates that the Program Association Table sent is
  682 + * currently applicable. When the bit is set to '0', it indicates that the table sent is not yet applicable and shall be the next
  683 + * table to become valid.
  684 + */
  685 + int8_t current_next_indicator; //1bit
  686 +
  687 + // 1B
  688 + /**
  689 + * This 8-bit field gives the number of this section. The section_number of the first section in the
  690 + * Program Association Table shall be 0x00. It shall be incremented by 1 with each additional section in the Program
  691 + * Association Table.
  692 + */
  693 + u_int8_t section_number; //8bits
  694 +
  695 + // 1B
  696 + /**
  697 + * This 8-bit field specifies the number of the last section (that is, the section with the highest
  698 + * section_number) of the complete Program Association Table.
  699 + */
  700 + u_int8_t last_section_number; //8bits
  701 +
  702 + // multiple 4B program data.
  703 + int nb_programs;
  704 + SrsTsPayloadPATProgram* programs;
  705 +
  706 + // 4B
  707 + int32_t CRC_32; //32bits
  708 +public:
  709 + SrsTsPayloadPAT(SrsTsPacket* p);
  710 + virtual ~SrsTsPayloadPAT();
  711 +public:
  712 + virtual int decode(SrsStream* stream);
  713 +};
  714 +
  715 +/**
519 * write data from frame(header info) and buffer(data) to ts file. 716 * write data from frame(header info) and buffer(data) to ts file.
520 * it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter 717 * it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
521 */ 718 */