├── .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 |
--------------------------------------------------------------------------------