├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bitreader.c ├── bitreader.h ├── tsparser.c ├── tsparser.h └── tsunpacker.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ts 3 | 4 | tsunpacker 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-c 3 | LDFLAGS= 4 | SOURCES=bitreader.c tsparser.c tsunpacker.c 5 | OBJECTS=$(SOURCES:.c=.o) 6 | EXECUTABLE=tsunpacker 7 | 8 | all: $(SOURCES) $(EXECUTABLE) 9 | 10 | $(EXECUTABLE): $(OBJECTS) 11 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 12 | 13 | .c.o: 14 | $(CC) $(CFLAGS) $< -o $@ 15 | 16 | clean: 17 | rm -rf *o tsunpacker 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mpegts-basic-parser 2 | Basic MPEG-TS parser written in C language 3 | -------------------------------------------------------------------------------- /bitreader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MpegtTS Basic Parser 3 | * Copyright (c) jeoliva, All rights reserved. 4 | 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | 15 | * You should have received a copy of the GNU Lesser General Public 16 | *License along with this library. 17 | */ 18 | 19 | #include "bitreader.h" 20 | 21 | void fillReservoir(ABitReader *bitReader) 22 | { 23 | bitReader->mReservoir = 0; 24 | size_t i; 25 | for (i = 0; bitReader->mSize > 0 && i < 4; ++i) 26 | { 27 | bitReader->mReservoir = (bitReader->mReservoir << 8) | *(bitReader->mData); 28 | 29 | ++bitReader->mData; 30 | --bitReader->mSize; 31 | } 32 | 33 | bitReader->mNumBitsLeft = 8 * i; 34 | bitReader->mReservoir <<= 32 - bitReader->mNumBitsLeft; 35 | } 36 | 37 | void putBits(ABitReader *bitReader, uint32_t x, size_t n) 38 | { 39 | bitReader->mReservoir = (bitReader->mReservoir >> n) | (x << (32 - n)); 40 | bitReader->mNumBitsLeft += n; 41 | 42 | } 43 | 44 | void initABitReader(ABitReader *bitReader, uint8_t *data, size_t size) 45 | { 46 | bitReader->mData = data; 47 | bitReader->mSize = size; 48 | bitReader->mReservoir = 0; 49 | bitReader->mNumBitsLeft = 0; 50 | } 51 | 52 | uint32_t getBits(ABitReader *bitReader, size_t n) 53 | { 54 | uint32_t result = 0; 55 | while (n > 0) 56 | { 57 | if (bitReader->mNumBitsLeft == 0) 58 | { 59 | fillReservoir(bitReader); 60 | } 61 | 62 | size_t m = n; 63 | if (m > bitReader->mNumBitsLeft) 64 | { 65 | m = bitReader->mNumBitsLeft; 66 | } 67 | 68 | result = (result << m) | (bitReader->mReservoir >> (32 - m)); 69 | bitReader->mReservoir <<= m; 70 | bitReader->mNumBitsLeft -= m; 71 | n -= m; 72 | } 73 | 74 | return result; 75 | } 76 | 77 | void skipBits(ABitReader *bitReader, size_t n) 78 | { 79 | while (n > 32) 80 | { 81 | getBits(bitReader, 32); 82 | n -= 32; 83 | } 84 | 85 | if (n > 0) 86 | { 87 | getBits(bitReader, n); 88 | } 89 | } 90 | 91 | size_t numBitsLeft(ABitReader *bitReader) 92 | { 93 | return bitReader->mSize * 8 + bitReader->mNumBitsLeft; 94 | } 95 | 96 | uint8_t *getBitReaderData(ABitReader *bitReader) 97 | { 98 | return bitReader->mData - bitReader->mNumBitsLeft / 8; 99 | } 100 | -------------------------------------------------------------------------------- /bitreader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MpegtTS Basic Parser 3 | * Copyright (c) jeoliva, All rights reserved. 4 | 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | 15 | * You should have received a copy of the GNU Lesser General Public 16 | *License along with this library. 17 | */ 18 | 19 | #ifndef __BIT_READER_H__ 20 | #define __BIT_READER_H__ 21 | 22 | #include 23 | #include 24 | 25 | typedef struct ABitReader 26 | { 27 | uint8_t *mData; 28 | size_t mSize; 29 | 30 | uint32_t mReservoir; // left-aligned bits 31 | size_t mNumBitsLeft; 32 | }ABitReader; 33 | 34 | 35 | void initABitReader(ABitReader *bitReader, uint8_t *data, size_t size); 36 | uint32_t getBits(ABitReader *bitReader, size_t n); 37 | void skipBits(ABitReader *bitReader, size_t n); 38 | size_t numBitsLeft(ABitReader *bitReader); 39 | uint8_t *getBitReaderData(ABitReader *bitReader); 40 | 41 | 42 | #endif // A_BIT_READER_H_ 43 | -------------------------------------------------------------------------------- /tsparser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MpegtTS Basic Parser 3 | * Copyright (c) jeoliva, All rights reserved. 4 | 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | 15 | * You should have received a copy of the GNU Lesser General Public 16 | *License along with this library. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "bitreader.h" 26 | #include "tsparser.h" 27 | 28 | // Parse a TS packet 29 | void parseTSPacket(TSParser *parser, ABitReader *bitReader) 30 | { 31 | uint32_t sync_byte; 32 | uint32_t transport_error_indicator; 33 | uint32_t payload_unit_start_indicator; 34 | uint32_t transport_priority; 35 | uint32_t pid; 36 | uint32_t transport_scrambling_control; 37 | uint32_t adaptation_field_control; 38 | uint32_t continuity_counter; 39 | 40 | sync_byte = getBits(bitReader, 8); 41 | 42 | transport_error_indicator = getBits(bitReader, 1); 43 | if (transport_error_indicator != 0) 44 | { 45 | printf("Packet with Error. Transport Error indicator: %u\n", transport_error_indicator); 46 | } 47 | 48 | payload_unit_start_indicator = getBits(bitReader, 1); 49 | printf("Payload unit start indicator: %u\n", payload_unit_start_indicator); 50 | 51 | transport_priority = getBits(bitReader, 1); 52 | printf("Transport Priority: %u\n", transport_priority); 53 | 54 | pid = getBits(bitReader, 13); 55 | printf("PID: 0x%04x\n", pid); 56 | 57 | transport_scrambling_control = getBits(bitReader, 2); 58 | printf("Transport Scrambling Control: %u\n", transport_scrambling_control); 59 | 60 | adaptation_field_control = getBits(bitReader, 2); 61 | printf("Adaptation field control: %u\n", adaptation_field_control); 62 | 63 | continuity_counter = getBits(bitReader, 4); 64 | printf("Continuity Counter: %u\n", continuity_counter); 65 | 66 | if (adaptation_field_control == 2 || adaptation_field_control == 3) 67 | { 68 | parseAdaptationField(parser, bitReader); 69 | } 70 | 71 | if (adaptation_field_control == 1 || adaptation_field_control == 3) 72 | { 73 | parseProgramId(parser, bitReader, pid, payload_unit_start_indicator); 74 | } 75 | } 76 | 77 | // Parse adaptation field 78 | void parseAdaptationField(TSParser *parser, ABitReader *bitReader) 79 | { 80 | uint32_t adaptation_field_length = getBits(bitReader, 8); 81 | printf("Adaptation field length: %u\n", adaptation_field_length); 82 | if (adaptation_field_length > 0) 83 | { 84 | skipBits(bitReader, adaptation_field_length * 8); 85 | } 86 | } 87 | 88 | // Parse program Id 89 | void parseProgramId(TSParser *parser, ABitReader *bitReader, uint32_t pid, uint32_t payload_unit_start_indicator) 90 | { 91 | int i, handled = 0; 92 | TSPointersListItem *listItem; 93 | TSProgram *pProgram; 94 | 95 | if (pid == 0) 96 | { 97 | if (payload_unit_start_indicator) 98 | { 99 | uint32_t skip = getBits(bitReader, 8); 100 | skipBits(bitReader, skip * 8); 101 | } 102 | 103 | parseProgramAssociationTable(parser, bitReader); 104 | return; 105 | } 106 | 107 | for (listItem = parser->mPrograms.mHead; listItem != NULL; listItem = listItem->mNext) 108 | { 109 | pProgram = (TSProgram *)listItem->mData; 110 | if (pid == pProgram->mProgramMapPID) 111 | { 112 | if (payload_unit_start_indicator) 113 | { 114 | uint32_t skip = getBits(bitReader, 8); 115 | skipBits(bitReader, skip * 8); 116 | } 117 | 118 | parseProgramMap(parser, pProgram, bitReader); 119 | handled = 1; 120 | break; 121 | } 122 | else 123 | { 124 | TSStream *pStream = getStreamByPID(pProgram, pid); 125 | if (pStream != NULL) 126 | { 127 | parseStream(pStream, payload_unit_start_indicator, bitReader); 128 | 129 | handled = 1; 130 | break; 131 | } 132 | } 133 | } 134 | 135 | if (!handled) 136 | { 137 | printf("PID 0x%04x not handled.\n", pid); 138 | } 139 | } 140 | 141 | // Parse Program Association table 142 | void parseProgramAssociationTable(TSParser *parser, ABitReader *bitReader) 143 | { 144 | size_t i; 145 | uint32_t table_id = getBits(bitReader, 8); 146 | printf(" table_id = %u\n", table_id); 147 | 148 | uint32_t section_syntax_indicator = getBits(bitReader, 1); 149 | printf(" section_syntax_indicator = %u\n", section_syntax_indicator); 150 | 151 | getBits(bitReader, 1); 152 | printf(" reserved = %u\n", getBits(bitReader, 2)); 153 | 154 | uint32_t section_length = getBits(bitReader, 12); 155 | printf(" section_length = %u\n", section_length); 156 | 157 | printf(" transport_stream_id = %u\n", getBits(bitReader, 16)); 158 | printf(" reserved = %u\n", getBits(bitReader, 2)); 159 | printf(" version_number = %u\n", getBits(bitReader, 5)); 160 | printf(" current_next_indicator = %u\n", getBits(bitReader, 1)); 161 | printf(" section_number = %u\n", getBits(bitReader, 8)); 162 | printf(" last_section_number = %u\n", getBits(bitReader, 8)); 163 | 164 | size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */); 165 | printf(" numProgramBytes = %ld\n", numProgramBytes); 166 | 167 | for (i = 0; i < numProgramBytes / 4; ++i) 168 | { 169 | uint32_t program_number = getBits(bitReader, 16); 170 | printf(" program_number = %u\n", program_number); 171 | 172 | printf(" reserved = %u\n", getBits(bitReader, 3)); 173 | 174 | if (program_number == 0) 175 | { 176 | printf(" network_PID = 0x%04x\n", getBits(bitReader, 13)); 177 | } 178 | else 179 | { 180 | unsigned programMapPID = getBits(bitReader, 13); 181 | 182 | printf(" program_map_PID = 0x%04x\n", programMapPID); 183 | addProgram(parser, programMapPID); 184 | } 185 | } 186 | printf(" CRC = 0x%08x\n", getBits(bitReader, 32)); 187 | } 188 | 189 | // Parse Stream 190 | void parseStream(TSStream *stream, uint32_t payload_unit_start_indicator, ABitReader *bitReader) 191 | { 192 | size_t payloadSizeBits; 193 | 194 | if (payload_unit_start_indicator) 195 | { 196 | if (stream->mPayloadStarted) 197 | { 198 | flushStreamData(stream); 199 | } 200 | stream->mPayloadStarted = 1; 201 | } 202 | 203 | if (!stream->mPayloadStarted) 204 | { 205 | return; 206 | } 207 | 208 | payloadSizeBits = numBitsLeft(bitReader); 209 | 210 | memcpy(stream->mBuffer + stream->mBufferSize, getBitReaderData(bitReader), payloadSizeBits / 8); 211 | stream->mBufferSize += (payloadSizeBits / 8); 212 | } 213 | 214 | // Flush stream data 215 | void flushStreamData(TSStream *stream) 216 | { 217 | ABitReader bitReader; 218 | initABitReader(&bitReader, (uint8_t *)stream->mBuffer, stream->mBufferSize); 219 | 220 | parsePES(stream, &bitReader); 221 | 222 | stream->mBufferSize = 0; 223 | } 224 | 225 | // Parse a PES packet 226 | void parsePES(TSStream *stream, ABitReader *bitReader) 227 | { 228 | uint32_t packet_startcode_prefix = getBits(bitReader, 24); 229 | uint32_t stream_id = getBits(bitReader, 8); 230 | uint32_t PES_packet_length = getBits(bitReader, 16); 231 | 232 | if (stream_id != 0xbc // program_stream_map 233 | && stream_id != 0xbe // padding_stream 234 | && stream_id != 0xbf // private_stream_2 235 | && stream_id != 0xf0 // ECM 236 | && stream_id != 0xf1 // EMM 237 | && stream_id != 0xff // program_stream_directory 238 | && stream_id != 0xf2 // DSMCC 239 | && stream_id != 0xf8) // H.222.1 type E 240 | { 241 | uint32_t PTS_DTS_flags; 242 | uint32_t ESCR_flag; 243 | uint32_t ES_rate_flag; 244 | uint32_t DSM_trick_mode_flag; 245 | uint32_t additional_copy_info_flag; 246 | uint32_t PES_header_data_length; 247 | uint32_t optional_bytes_remaining; 248 | uint64_t PTS = 0, DTS = 0; 249 | 250 | skipBits(bitReader, 8); 251 | 252 | PTS_DTS_flags = getBits(bitReader, 2); 253 | ESCR_flag = getBits(bitReader, 1); 254 | ES_rate_flag = getBits(bitReader, 1); 255 | DSM_trick_mode_flag = getBits(bitReader, 1); 256 | additional_copy_info_flag = getBits(bitReader, 1); 257 | 258 | skipBits(bitReader, 2); 259 | 260 | PES_header_data_length = getBits(bitReader, 8); 261 | optional_bytes_remaining = PES_header_data_length; 262 | 263 | if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) 264 | { 265 | skipBits(bitReader, 4); 266 | PTS = parseTSTimestamp(bitReader); 267 | skipBits(bitReader, 1); 268 | 269 | optional_bytes_remaining -= 5; 270 | 271 | if (PTS_DTS_flags == 3) 272 | { 273 | skipBits(bitReader, 4); 274 | 275 | DTS = parseTSTimestamp(bitReader); 276 | skipBits(bitReader, 1); 277 | 278 | optional_bytes_remaining -= 5; 279 | } 280 | } 281 | 282 | if (ESCR_flag) 283 | { 284 | skipBits(bitReader, 2); 285 | 286 | uint64_t ESCR = parseTSTimestamp(bitReader); 287 | 288 | skipBits(bitReader, 11); 289 | 290 | optional_bytes_remaining -= 6; 291 | } 292 | 293 | if (ES_rate_flag) 294 | { 295 | skipBits(bitReader, 24); 296 | optional_bytes_remaining -= 3; 297 | } 298 | 299 | skipBits(bitReader, optional_bytes_remaining * 8); 300 | 301 | // ES data follows. 302 | if (PES_packet_length != 0) 303 | { 304 | uint32_t dataLength = PES_packet_length - 3 - PES_header_data_length; 305 | 306 | // Signaling we have payload data 307 | onPayloadData(stream, PTS_DTS_flags, PTS, DTS, getBitReaderData(bitReader), dataLength); 308 | 309 | skipBits(bitReader, dataLength * 8); 310 | } 311 | else 312 | { 313 | size_t payloadSizeBits; 314 | // Signaling we have payload data 315 | onPayloadData(stream, PTS_DTS_flags, PTS, DTS, getBitReaderData(bitReader), numBitsLeft(bitReader) / 8); 316 | 317 | payloadSizeBits = numBitsLeft(bitReader); 318 | } 319 | } 320 | else if (stream_id == 0xbe) 321 | { // padding_stream 322 | skipBits(bitReader, PES_packet_length * 8); 323 | } 324 | else 325 | { 326 | skipBits(bitReader, PES_packet_length * 8); 327 | } 328 | } 329 | 330 | int64_t parseTSTimestamp(ABitReader *bitReader) 331 | { 332 | int64_t result = ((uint64_t)getBits(bitReader, 3)) << 30; 333 | skipBits(bitReader, 1); 334 | result |= ((uint64_t)getBits(bitReader, 15)) << 15; 335 | skipBits(bitReader, 1); 336 | result |= getBits(bitReader, 15); 337 | 338 | return result; 339 | } 340 | 341 | // convert PTS to timestamp 342 | int64_t convertPTSToTimestamp(TSStream *stream, uint64_t PTS) 343 | { 344 | if (!stream->mProgram->mFirstPTSValid) 345 | { 346 | stream->mProgram->mFirstPTSValid = 1; 347 | stream->mProgram->mFirstPTS = PTS; 348 | PTS = 0; 349 | } 350 | else if (PTS < stream->mProgram->mFirstPTS) 351 | { 352 | PTS = 0; 353 | } 354 | else 355 | { 356 | PTS -= stream->mProgram->mFirstPTS; 357 | } 358 | 359 | return (PTS * 100) / 9; 360 | } 361 | 362 | // Function called when we have payload content 363 | void onPayloadData(TSStream *stream, uint32_t PTS_DTS_flag, uint64_t PTS, uint64_t DTS, uint8_t *data, size_t size) 364 | { 365 | int64_t timeUs = convertPTSToTimestamp(stream, PTS); 366 | if (stream->mStreamType == TS_STREAM_VIDEO) 367 | { 368 | printf("Payload Data!!!! Video (%02x), PTS: %lld, DTS:%lld, Size: %ld\n", stream->mStreamType, PTS, DTS, size); 369 | } 370 | else if (stream->mStreamType == TS_STREAM_AUDIO) 371 | { 372 | printf("Payload Data!!!! Audio (%02x), PTS: %lld, DTS:%lld, Size: %ld\n", stream->mStreamType, PTS, DTS, size); 373 | } 374 | } 375 | 376 | // Add a new program to the list of programs 377 | void addProgram(TSParser *parser, uint32_t programMapPID) 378 | { 379 | TSProgram *program = (TSProgram *)malloc(sizeof(TSProgram)); 380 | memset(program, 0, sizeof(TSProgram)); 381 | 382 | program->mProgramMapPID = programMapPID; 383 | 384 | addItemToList(&parser->mPrograms, program); 385 | } 386 | 387 | // Add a new stream to the specified program 388 | void addStream(TSProgram *program, uint32_t elementaryPID, uint32_t streamType) 389 | { 390 | TSStream *stream = (TSStream *)malloc(sizeof(TSStream)); 391 | memset(stream, 0, sizeof(TSStream)); 392 | 393 | stream->mProgram = program; 394 | stream->mElementaryPID = elementaryPID; 395 | stream->mStreamType = streamType; 396 | 397 | printf("Add Stream. PID: %d, Stream Type: %d (%X)\n", elementaryPID, streamType, streamType); 398 | 399 | addItemToList(&program->mStreams, stream); 400 | } 401 | 402 | // Add an item to a linked list 403 | void addItemToList(TSPointersList *list, void *data) 404 | { 405 | TSPointersListItem *item = (TSPointersListItem *)malloc(sizeof(TSPointersListItem)); 406 | item->mData = data; 407 | item->mNext = NULL; 408 | 409 | if (list->mTail != NULL) 410 | { 411 | list->mTail->mNext = item; 412 | list->mTail = item; 413 | } 414 | else 415 | { 416 | list->mHead = list->mTail = item; 417 | } 418 | } 419 | 420 | // Get Stream object given its id 421 | TSStream *getStreamByPID(TSProgram *program, uint32_t pid) 422 | { 423 | TSPointersListItem *listItem; 424 | TSStream *stream; 425 | for (listItem = program->mStreams.mHead; listItem != NULL; listItem = listItem->mNext) 426 | { 427 | stream = (TSStream *)listItem->mData; 428 | if (stream != NULL && stream->mElementaryPID == pid) 429 | { 430 | return stream; 431 | } 432 | } 433 | return NULL; 434 | } 435 | 436 | // Get a Program object given its id 437 | TSProgram *getProgramByPID(TSParser *parser, uint32_t pid) 438 | { 439 | TSPointersListItem *listItem; 440 | TSProgram *program; 441 | for (listItem = parser->mPrograms.mHead; listItem != NULL; listItem = listItem->mNext) 442 | { 443 | program = (TSProgram *)listItem->mData; 444 | if (program != NULL && program->mProgramMapPID == pid) 445 | { 446 | return program; 447 | } 448 | } 449 | return NULL; 450 | } 451 | 452 | // Parse program map 453 | void parseProgramMap(TSParser *parser, TSProgram *program, ABitReader *bitReader) 454 | { 455 | uint32_t section_syntax_indicator; 456 | uint32_t table_id; 457 | uint32_t section_length; 458 | uint32_t program_info_length; 459 | size_t infoBytesRemaining; 460 | uint32_t streamType; 461 | uint32_t elementaryPID; 462 | uint32_t ES_info_length; 463 | uint32_t info_bytes_remaining; 464 | 465 | table_id = getBits(bitReader, 8); 466 | 467 | printf("****** PROGRAM MAP *****\n"); 468 | printf(" table_id = %u\n", table_id); 469 | 470 | section_syntax_indicator = getBits(bitReader, 1); 471 | printf(" section_syntax_indicator = %u\n", section_syntax_indicator); 472 | 473 | // Reserved 474 | skipBits(bitReader, 3); 475 | 476 | section_length = getBits(bitReader, 12); 477 | printf(" section_length = %u\n", section_length); 478 | printf(" program_number = %u\n", getBits(bitReader, 16)); 479 | printf(" reserved = %u\n", getBits(bitReader, 2)); 480 | printf(" version_number = %u\n", getBits(bitReader, 5)); 481 | printf(" current_next_indicator = %u\n", getBits(bitReader, 1)); 482 | printf(" section_number = %u\n", getBits(bitReader, 8)); 483 | printf(" last_section_number = %u\n", getBits(bitReader, 8)); 484 | printf(" reserved = %u\n", getBits(bitReader, 3)); 485 | printf(" PCR_PID = 0x%04x\n", getBits(bitReader, 13)); 486 | printf(" reserved = %u\n", getBits(bitReader, 4)); 487 | 488 | program_info_length = getBits(bitReader, 12); 489 | printf(" program_info_length = %u\n", program_info_length); 490 | 491 | skipBits(bitReader, program_info_length * 8); // skip descriptors 492 | 493 | // infoBytesRemaining is the number of bytes that make up the 494 | // variable length section of ES_infos. It does not include the 495 | // final CRC. 496 | infoBytesRemaining = section_length - 9 - program_info_length - 4; 497 | 498 | while (infoBytesRemaining > 0) 499 | { 500 | streamType = getBits(bitReader, 8); 501 | printf(" stream_type = 0x%02x\n", streamType); 502 | 503 | printf(" reserved = %u\n", getBits(bitReader, 3)); 504 | 505 | elementaryPID = getBits(bitReader, 13); 506 | printf(" elementary_PID = 0x%04x\n", elementaryPID); 507 | 508 | printf(" reserved = %u\n", getBits(bitReader, 4)); 509 | 510 | ES_info_length = getBits(bitReader, 12); 511 | printf(" ES_info_length = %u\n", ES_info_length); 512 | 513 | info_bytes_remaining = ES_info_length; 514 | while (info_bytes_remaining >= 2) 515 | { 516 | uint32_t descLength; 517 | printf(" tag = 0x%02x\n", getBits(bitReader, 8)); 518 | 519 | descLength = getBits(bitReader, 8); 520 | printf(" len = %u\n", descLength); 521 | 522 | skipBits(bitReader, descLength * 8); 523 | 524 | info_bytes_remaining -= descLength + 2; 525 | } 526 | 527 | if (getStreamByPID(program, elementaryPID) == NULL) 528 | addStream(program, elementaryPID, streamType); 529 | 530 | infoBytesRemaining -= 5 + ES_info_length; 531 | } 532 | 533 | printf(" CRC = 0x%08x\n", getBits(bitReader, 32)); 534 | printf("****** PROGRAM MAP *****\n"); 535 | } 536 | 537 | // Free program resources 538 | void freeProgramResources(TSProgram *program) 539 | { 540 | // Free Streams 541 | TSPointersListItem *item; 542 | TSStream *pStream; 543 | while (program->mStreams.mHead != NULL) 544 | { 545 | item = program->mStreams.mHead; 546 | program->mStreams.mHead = item->mNext; 547 | 548 | if (item->mData != NULL) 549 | { 550 | pStream = (TSStream *)item->mData; 551 | free(pStream); 552 | } 553 | 554 | free(item); 555 | } 556 | } 557 | 558 | // Free parser resources 559 | void freeParserResources(TSParser *parser) 560 | { 561 | // Free Programs 562 | TSPointersListItem *item; 563 | TSProgram *pProgram; 564 | while (parser->mPrograms.mHead != NULL) 565 | { 566 | item = parser->mPrograms.mHead; 567 | parser->mPrograms.mHead = item->mNext; 568 | 569 | if (item->mData != NULL) 570 | { 571 | pProgram = (TSProgram *)item->mData; 572 | freeProgramResources(pProgram); 573 | free(pProgram); 574 | } 575 | 576 | free(item); 577 | } 578 | } 579 | 580 | // Signal a discontinuity 581 | void signalDiscontinuity(TSParser *parser, int isSeek) 582 | { 583 | TSPointersListItem *item; 584 | TSProgram *pProgram; 585 | while (parser->mPrograms.mHead != NULL) 586 | { 587 | item = parser->mPrograms.mHead; 588 | parser->mPrograms.mHead = item->mNext; 589 | 590 | if (item->mData != NULL) 591 | { 592 | pProgram = (TSProgram *)item->mData; 593 | signalDiscontinuityToProgram(pProgram, isSeek); 594 | } 595 | } 596 | } 597 | 598 | // Signal a discontinuity to a program 599 | void signalDiscontinuityToProgram(TSProgram *program, int isSeek) 600 | { 601 | TSPointersListItem *item; 602 | TSStream *pStream; 603 | while (program->mStreams.mHead != NULL) 604 | { 605 | item = program->mStreams.mHead; 606 | program->mStreams.mHead = item->mNext; 607 | 608 | if (item->mData != NULL) 609 | { 610 | pStream = (TSStream *)item->mData; 611 | signalDiscontinuityToStream(pStream, isSeek); 612 | } 613 | } 614 | } 615 | 616 | // Signal a discontinuity to a stream 617 | void signalDiscontinuityToStream(TSStream *stream, int isSeek) 618 | { 619 | stream->mPayloadStarted = 0; 620 | stream->mBufferSize = 0; 621 | } 622 | -------------------------------------------------------------------------------- /tsparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MpegtTS Basic Parser 3 | * Copyright (c) jeoliva, All rights reserved. 4 | 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | 15 | * You should have received a copy of the GNU Lesser General Public 16 | *License along with this library. 17 | */ 18 | 19 | #ifndef __TS_PARSER_H__ 20 | #define __TS_PARSER_H__ 21 | 22 | #define TS_PACKET_SIZE 188 23 | #define TS_SYNC 0x47 24 | #define TS_DISCONTINUITY 0x0 25 | 26 | #define TS_STREAM_VIDEO 0x1b 27 | #define TS_STREAM_AUDIO 0x0f 28 | 29 | // Linked lists structure 30 | typedef struct TSPointersListItem 31 | { 32 | void *mData; 33 | struct TSPointersListItem *mNext; 34 | } TSPointersListItem; 35 | 36 | typedef struct TSSPointersList 37 | { 38 | TSPointersListItem *mHead; 39 | TSPointersListItem *mTail; 40 | } TSPointersList; 41 | 42 | // Programs (one TS file could have 1 or more programs) 43 | typedef struct TSProgram 44 | { 45 | uint32_t mProgramMapPID; 46 | uint64_t mFirstPTS; 47 | int mFirstPTSValid; 48 | 49 | TSPointersList mStreams; 50 | } TSProgram; 51 | 52 | // Streams (one program could have one or more streams) 53 | typedef struct TSStream 54 | { 55 | TSProgram *mProgram; 56 | 57 | uint32_t mElementaryPID; 58 | uint32_t mStreamType; 59 | uint32_t mPayloadStarted; 60 | 61 | char mBuffer[128 * 1024]; 62 | int mBufferSize; 63 | } TSStream; 64 | 65 | // Parser. Keeps a reference to the list of programs 66 | typedef struct TSParser 67 | { 68 | TSPointersList mPrograms; 69 | } TSParser; 70 | 71 | void signalDiscontinuity(TSParser *parser, int isSeek); 72 | void signalDiscontinuityToProgram(TSProgram *program, int isSeek); 73 | void signalDiscontinuityToStream(TSStream *stream, int isSeek); 74 | 75 | void parseTSPacket(TSParser *parser, ABitReader *bitReader); 76 | void parseAdaptationField(TSParser *parser, ABitReader *bitReader); 77 | void parseProgramId(TSParser *parser, ABitReader *bitReader, uint32_t pid, uint32_t payload_unit_start_indicator); 78 | void parseProgramAssociationTable(TSParser *parser, ABitReader *bitReader); 79 | void parseProgramMap(TSParser *parser, TSProgram *program, ABitReader *bitReader); 80 | void parseStream(TSStream *stream, uint32_t payload_unit_start_indicator, ABitReader *bitReader); 81 | void parsePES(TSStream *stream, ABitReader *bitReader); 82 | int64_t parseTSTimestamp(ABitReader *bitReader); 83 | 84 | void addProgram(TSParser *parser, uint32_t programMapPID); 85 | void addStream(TSProgram *program, uint32_t elementaryPID, uint32_t streamType); 86 | 87 | TSStream *getStreamByPID(TSProgram *program, uint32_t pid); 88 | TSProgram *getProgramByPID(TSParser *parser, uint32_t pid); 89 | 90 | void flushStreamData(TSStream *stream); 91 | void onPayloadData(TSStream *stream, uint32_t PTS_DTS_flag, uint64_t PTS, uint64_t DTS, uint8_t *data, size_t size); 92 | 93 | void freeParserResources(TSParser *parser); 94 | 95 | void addItemToList(TSPointersList *list, void *data); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /tsunpacker.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MpegtTS Basic Parser 3 | * Copyright (c) jeoliva, All rights reserved. 4 | 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | 15 | * You should have received a copy of the GNU Lesser General Public 16 | *License along with this library. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "bitreader.h" 27 | #include "tsparser.h" 28 | 29 | // Main method 30 | int main(int argc, char *argv[]) 31 | { 32 | int fd, bytes_read; 33 | uint8_t packet_buffer[TS_PACKET_SIZE]; 34 | int n_packets = 0; 35 | 36 | TSParser tsParser; 37 | memset(&tsParser, 0, sizeof(TSParser)); 38 | 39 | fd = open(argv[1], O_RDONLY); 40 | if (fd < 0) 41 | { 42 | printf("Error opening the stream\nSyntax: tsunpacket FileToParse.ts\n"); 43 | return -1; 44 | } 45 | 46 | // Parse file while we can read full TS packets 47 | while (1) 48 | { 49 | bytes_read = read(fd, packet_buffer, TS_PACKET_SIZE); 50 | 51 | if (packet_buffer[0] == TS_DISCONTINUITY) 52 | { 53 | printf("Discontinuity detected!\n"); 54 | signalDiscontinuity(&tsParser, 0); 55 | } 56 | else if (bytes_read < TS_PACKET_SIZE) 57 | { 58 | printf("End of file!\n"); 59 | break; 60 | } 61 | else if (packet_buffer[0] == TS_SYNC) 62 | { 63 | ABitReader bitReader; 64 | initABitReader(&bitReader, packet_buffer, bytes_read); 65 | 66 | parseTSPacket(&tsParser, &bitReader); 67 | 68 | n_packets++; 69 | } 70 | } 71 | 72 | printf("Number of packets found: %d\n", n_packets); 73 | 74 | // Freeing resources 75 | close(fd); 76 | freeParserResources(&tsParser); 77 | 78 | return 0; 79 | } 80 | --------------------------------------------------------------------------------