blob: c0cef04326395e3ca35f69c9bdd480e7d45abe52 [file] [log] [blame]
Gong Kefa504d72023-04-18 10:23:35 +08001#include <stdio.h>
2#include <string.h>
3#include "ts_indexer.h"
4
5#define TS_PKT_SIZE (188)
6
7//#define TS_INDEXER_DEBUG
8#ifdef TS_INDEXER_DEBUG
9#define INF(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
10#else
11#define INF(fmt, ...)
12#endif
13
14#define ERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
15
16#define NAL_TYPE_IDR 5 // IDR NALU 类型
17#define NAL_TYPE_NON_IDR 1 // 非 IDR NALU 类型
18
19/**
20 * Initialize the TS indexer.
21 * \param ts_indexer The TS indexer to be initialized.
22 * \retval 0 On success.
23 * \retval -1 On error.
24 */
25int
26ts_indexer_init (TS_Indexer_t *ts_indexer)
27{
28 TSParser init_parser;
29
30 if (ts_indexer == NULL) {
31 return -1;
32 }
33
34 memset(&init_parser, 0, sizeof(TSParser));
35 init_parser.pid = 0x1fff;
36 init_parser.format = -1;
37 init_parser.PES.pts = -1;
38 init_parser.PES.offset = 0;
39 init_parser.PES.len = 0;
40 init_parser.PES.state = TS_INDEXER_STATE_INIT;
41
42 memcpy(&ts_indexer->video_parser, &init_parser, sizeof(TSParser));
43 memcpy(&ts_indexer->audio_parser, &init_parser, sizeof(TSParser));
44 ts_indexer->callback = NULL;
45 ts_indexer->offset = 0;
46
47 return 0;
48}
49
50/**
51 * Release the TS indexer.
52 * \param ts_indexer The TS indexer to be released.
53 */
54void
55ts_indexer_destroy (TS_Indexer_t *ts_indexer)
56{
57 return;
58}
59
60/**
61 * Set the video format.
62 * \param ts_indexer The TS indexer.
63 * \param format The stream format.
64 * \retval 0 On success.
65 * \retval -1 On error.
66 */
67int
68ts_indexer_set_video_format (TS_Indexer_t *ts_indexer, TS_Indexer_StreamFormat_t format)
69{
70 if (ts_indexer == NULL)
71 return -1;
72
73 ts_indexer->video_parser.format = format;
74
75 return 0;
76}
77
78/**
79 * Set the video PID.
80 * \param ts_indexer The TS indexer.
81 * \param pid The video PID.
82 * \retval 0 On success.
83 * \retval -1 On error.
84 */
85int
86ts_indexer_set_video_pid (TS_Indexer_t *ts_indexer, int pid)
87{
88 if (ts_indexer == NULL)
89 return -1;
90
91
92 TSParser *parser = &ts_indexer->video_parser;
93 parser->pid = pid;
94 parser->offset = 0;
95 parser->PES.pts = -1;
96 parser->PES.offset = 0;
97 parser->PES.len = 0;
98 parser->PES.state = TS_INDEXER_STATE_INIT;
99
100 return 0;
101}
102
103/**
104 * Set the audio PID.
105 * \param ts_indexer The TS indexer.
106 * \param pid The audio PID.
107 * \retval 0 On success.
108 * \retval -1 On error.
109 */
110int
111ts_indexer_set_audio_pid (TS_Indexer_t *ts_indexer, int pid)
112{
113 if (ts_indexer == NULL)
114 return -1;
115
116 TSParser *parser = &ts_indexer->audio_parser;
117 parser->pid = pid;
118 parser->offset = 0;
119 parser->PES.pts = -1;
120 parser->PES.offset = 0;
121 parser->PES.len = 0;
122 parser->PES.state = TS_INDEXER_STATE_INIT;
123
124 return 0;
125}
126
127/**
128 * Set the event callback function.
129 * \param ts_indexer The TS indexer.
130 * \param callback The event callback function.
131 * \retval 0 On success.
132 * \retval -1 On error.
133 */
134int
135ts_indexer_set_event_callback (TS_Indexer_t *ts_indexer, TS_Indexer_EventCallback_t callback)
136{
137 ts_indexer->callback = callback;
138
139 return 0;
140}
141
142static void find_mpeg(uint8_t *data, int len, TS_Indexer_t *indexer, TSParser *stream)
143{
144 int i;
145 uint32_t needle = 0;
146 uint8_t *haystack = data;
147 int haystack_len = len;
148 int left = len;
149 // start code of picture header
150 uint8_t arr[4] = {0x00, 0x00, 0x01, 0x00};
151 TS_Indexer_Event_t event;
152
153 /* mpeg header needs at least 4 bytes */
154 if (left < 4) {
155 memcpy(&stream->PES.data[0], &haystack, left);
156 stream->PES.len = left;
157 return;
158 }
159
160 memset(&event, 0, sizeof(event));
161 event.pid = stream->pid;
162 event.offset = stream->offset;
163
164 for (i = 0; i < 4; ++i) {
165 needle += (arr[i] << (8 * i));
166 }
167
168 for (i = 0; i < haystack_len - sizeof(needle) + 1;) {
169 if (*(uint32_t *)(haystack + i) == needle) {
170 // picture header found
171 if (left < 5) {
172 INF("MPEG2 picture header across TS Packet\n");
173
174 /* MEPG2 picture header across TS packet, should cache the left data */
175 memcpy(&stream->PES.data[0], haystack + i, left);
176 stream->PES.len = left;
177 return;
178 }
179
180 int frame_type = (haystack[i + 5] >> 3) & 0x7;
181 INF("frame_type: %d\n", frame_type);
182 if (frame_type == 1) {
183 INF("I-frame found, offset: %ld\n", event.offset);
184 if (stream->format == TS_INDEXER_VIDEO_FORMAT_MPEG2) {
185 event.pts = stream->PES.pts;
186 event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
187 if (indexer->callback) {
188 indexer->callback(indexer, &event);
189 }
190 } else {
191 ERR("%s not MPEG2 video I-frame??\n", __func__);
192 }
193 }
194
195 i += 5;
196 left -= 5;
197 } else {
198 i ++;
199 left --;
200 }
201 }
202
203 if (left > 0) {
204 memcpy(&stream->PES.data[0], &haystack[i], left);
205 stream->PES.len = left;
206 } else {
207 stream->PES.len = 0;
208 }
209}
210
211static uint8_t *get_nalu(uint8_t *data, size_t len, size_t *nalu_len)
212{
213 size_t i;
214 uint8_t *p = data;
215
216 if (len == 0)
217 return NULL;
218
219 //INF("%s enter, len:%#lx\n", __func__, len);
220 for (i = 0; i < len - 4; ++i) {
221 if (p[i] == 0x00 && p[i+1] == 0x00 && p[i+2] == 0x01) {
222 uint8_t *frame_data = data + i;
223 size_t frame_data_len = 0;
224
225 //INF("%s start code prefix\n", __func__);
226 for (size_t j = i + 4; j < len - 4; ++j) {
227 if (p[j] == 0x00 && p[j+1] == 0x00 && p[j+2] == 0x01) {
228 frame_data_len = j - i;
229 break;
230 }
231 }
232
233 if (frame_data_len > 0) {
234 *nalu_len = frame_data_len;
235 return frame_data;
236 } else {
237 frame_data_len = len - i;
238 *nalu_len = frame_data_len;
239 return frame_data;
240 }
241 }
242 }
243
244 return NULL;
245}
246
247static void find_h264(uint8_t *data, size_t len, TS_Indexer_t *indexer, TSParser *stream)
248{
249 TS_Indexer_Event_t event;
250 uint8_t *nalu = data;
251 size_t pes_data_len = len;
252 size_t nalu_len;
253
254 memset(&event, 0, sizeof(event));
255 event.pid = stream->pid;
256 event.offset = stream->offset;
257
258 while (1) {
259 int left = pes_data_len - (nalu - data);
260 if (left <= 4) {
261 memcpy(&stream->PES.data[0], nalu, left);
262 stream->PES.len = left;
263 break;
264 }
265
266 nalu = get_nalu(nalu, left, &nalu_len);
267 if (nalu == NULL)
268 break;
269
270 if (nalu[0] == 0x00 && nalu[1] == 0x00 && nalu[2] == 0x01) {
271 int nal_type = nalu[3] & 0x1f;
272 if (nal_type == NAL_TYPE_IDR || nal_type == NAL_TYPE_NON_IDR) {
273 int nal_ref_idr = nalu[3] & 0x60;
274 if (nal_ref_idr == 0x60) { //I-frame
275 INF("H264 I-frame found\n");
276 event.pts = stream->PES.pts;
277 event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
278 if (indexer->callback) {
279 indexer->callback(indexer, &event);
280 }
281 }
282 }
283 }
284
285 nalu += nalu_len;
286 }
287
288 stream->PES.len = 0;
289}
290
291static void find_h265(uint8_t *data, int len, TS_Indexer_t *indexer, TSParser *stream)
292{
293 TS_Indexer_Event_t event;
294 uint8_t *nalu = data;
295 size_t pes_data_len = len;
296 size_t nalu_len;
297
298 memset(&event, 0, sizeof(event));
299 event.pid = stream->pid;
300 event.offset = stream->offset;
301
302 while (nalu != NULL) {
303 int left = pes_data_len - (nalu - data);
304 if (left <= 4) {
305 memcpy(&stream->PES.data[0], nalu, left);
306 stream->PES.len = left;
307 break;
308 }
309
310 nalu = get_nalu(nalu, left, &nalu_len);
311 if (nalu == NULL)
312 break;
313
314 if (nalu[0] == 0x00 && nalu[1] == 0x00 && nalu[2] == 0x01) {
315 int nalu_type = (nalu[3] & 0x7E) >> 1;
316 //INF("nalu[3]: %#x, nalu_type: %#x\n", nalu[3], nalu_type);
317 if (nalu_type == 19) {
318 event.pts = stream->PES.pts;
319 event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
320 INF("HEVC I-frame found\n");
321 if (indexer->callback) {
322 indexer->callback(indexer, &event);
323 }
324 } else {
325 //INF("%s, not HEVC video I-frame. nalu_type: %#x\n", __func__, nalu_type);
326 }
327 }
328
329 nalu += nalu_len;
330 }
331 stream->PES.len = 0;
332}
333
334/*Parse the PES packet*/
335static void
336pes_packet(TS_Indexer_t *ts_indexer, uint8_t *data, int len, TSParser *stream)
337{
338 uint8_t *p = data;
339 TS_Indexer_t *pi = ts_indexer;
340 int left = len;
341 TS_Indexer_Event_t event;
342
343 memset(&event, 0, sizeof(event));
344 event.pid = stream->pid;
345 event.offset = stream->offset;
346
347 INF("stream: %p, state: %d\n", stream, stream->PES.state);
348 if (stream->PES.state <= TS_INDEXER_STATE_INIT) {
349 INF("%s, invalid state\n", __func__);
350 stream->PES.len = 0;
351 return;
352 }
353
354 /* needs splice two pieces of data together if have cache data */
355 if (stream->PES.len > 0) {
356 INF("%s have cache data %d bytes\n", __func__, stream->PES.len);
357 memcpy(&stream->PES.data[stream->PES.len], data, len);
358 p = &stream->PES.data[0];
359 left = stream->PES.len + len;
360 stream->PES.len = left;
361 }
362
363 if (stream->PES.state == TS_INDEXER_STATE_TS_START) {
364 /* needs cache data if no enough data to parse PES header */
365 if (left < 6) {
366 if (stream->PES.len <= 0) {
367 memcpy(&stream->PES.data[0], p, left);
368 stream->PES.len = left;
369 }
370 INF("not enough ts payload len: %#x\n", left);
371 return;
372 }
373
374 // chect the PES packet start code prefix
375 if ((p[0] != 0) || (p[1] != 0) || (p[2] != 1)) {
376 stream->PES.len = 0;
377 stream->PES.state = TS_INDEXER_STATE_INIT;
378 INF("%s, not the expected start code!\n", __func__);
379 return;
380 }
381
382 p += 6;
383 left -= 6;
384 stream->PES.state = TS_INDEXER_STATE_PES_HEADER;
385 }
386
387 if (stream->PES.state == TS_INDEXER_STATE_PES_HEADER) {
388 if (left < 8) {
389 if (stream->PES.len <= 0) {
390 memcpy(&stream->PES.data[0], p, left);
391 stream->PES.len = left;
392 }
393 INF("not enough optional pes header len: %#x\n", left);
394 return;
395 }
396
397 int header_length = p[2];
398 if (p[1] & 0x80) {
399 // parser pts
400 p += 3;
401 left -= 3;
402 event.pts = stream->PES.pts = (((uint64_t)(p[0] & 0x0E) << 29) |
403 ((uint64_t)p[1] << 22) |
404 ((uint64_t)(p[2] & 0xFE) << 14) |
405 ((uint64_t)p[3] << 7) |
406 (((uint64_t)p[4] & 0xFE) >> 1));
407 INF("pts: %lx, pos:%lx\n", event.pts, event.offset);
408
409 if (stream == &pi->video_parser) {
410 event.type = TS_INDEXER_EVENT_TYPE_VIDEO_PTS;
411 } else {
412 event.type = TS_INDEXER_EVENT_TYPE_AUDIO_PTS;
413 }
414 if (pi->callback) {
415 pi->callback(pi, &event);
416 }
417 }
418 stream->PES.state = TS_INDEXER_STATE_PES_PTS;
419
420 p += header_length;
421 left -= header_length;
422 }
423
424 stream->PES.len = left;
425 if (left <= 0
426 || stream->PES.state < TS_INDEXER_STATE_PES_PTS) {
427 return;
428 }
429
430 INF("stream->format: %d, left: %d\n", stream->format, left);
431 switch (stream->format) {
432 case TS_INDEXER_VIDEO_FORMAT_MPEG2:
433 find_mpeg(p, left, pi, &pi->video_parser);
434 break;
435
436 case TS_INDEXER_VIDEO_FORMAT_H264:
437 find_h264(p, left, pi, &pi->video_parser);
438 break;
439
440 case TS_INDEXER_VIDEO_FORMAT_HEVC:
441 find_h265(p, left, pi, &pi->video_parser);
442 break;
443
444 default:
445 break;
446 }
447}
448
449/*Parse the TS packet.*/
450static void
451ts_packet(TS_Indexer_t *ts_indexer, uint8_t *data)
452{
453 uint16_t pid;
454 uint8_t afc;
455 uint8_t *p = data;
456 TS_Indexer_t *pi = ts_indexer;
457 int len;
458 int is_start;
459
460 is_start = p[1] & 0x40;
461 pid = ((p[1] & 0x1f) << 8) | p[2];
462 if (pid == 0x1fff)
463 return;
464
465 if ((pid != pi->video_parser.pid) &&
466 (pid != pi->audio_parser.pid)) {
467 return;
468 }
469
470 if (is_start) {
471 if (pid == pi->video_parser.pid) {
472 pi->video_parser.offset = pi->offset;
473 pi->video_parser.PES.state = TS_INDEXER_STATE_TS_START;
474 }
475 else if (pid == pi->audio_parser.pid) {
476 pi->audio_parser.offset = pi->offset;
477 pi->audio_parser.PES.state = TS_INDEXER_STATE_TS_START;
478 }
479 }
480
481 afc = (p[3] >> 4) & 0x03;
482
483 p += 4;
484 len = 184;
485
486 if (afc & 2) {
487 int adp_field_len = p[0];
488 p++;
489 len--;
490
491 p += adp_field_len;
492 len -= adp_field_len;
493
494 if (len < 0) {
495 ERR("illegal adaption field length!");
496 return;
497 }
498 }
499
500 // has payload
501 if ((afc & 1) && (len > 0)) {
502 // parser pes packet
503 pes_packet(pi, p, len, (pid == pi->video_parser.pid) ? &pi->video_parser : &pi->audio_parser);
504 }
505}
506
507/**
508 * Parse the TS stream and generate the index data.
509 * \param ts_indexer The TS indexer.
510 * \param data The TS data.
511 * \param len The length of the TS data in bytes.
512 * \return The left TS data length of bytes.
513 */
514int
515ts_indexer_parse (TS_Indexer_t *ts_indexer, uint8_t *data, int len)
516{
517 uint8_t *p = data;
518 int left = len;
519
520 if (ts_indexer == NULL || data == NULL || len <= 0)
521 return -1;
522
523 while (left > 0) {
524 // find the sync byte
525 if (*p == 0x47) {
526 if (left < TS_PKT_SIZE) {
527 INF("%s data length may not be 188-byte aligned\n", __func__);
528 return left;
529 }
530
531 // parse one ts packet
532 ts_packet(ts_indexer, p);
533 p += TS_PKT_SIZE;
534 left -= TS_PKT_SIZE;
535 ts_indexer->offset += TS_PKT_SIZE;
536 } else {
537 p++;
538 left--;
539 ts_indexer->offset++;
540 }
541 }
542
543 return left;
544}