ts_indexer: Add TS indexer. [1/1]
PD#SWPL-119993
Problem:
Need TS indexer
Solution:
Add ts indexer and test pass
Verify:
verify MPEG2/H264/HEVC on ubuntu
Change-Id: I69e49afc34f47090cb87fd2541e95fdc979a2e22
Signed-off-by: Yahui Han <yahui.han@amlogic.com>
diff --git a/include/ts_indexer.h b/include/ts_indexer.h
new file mode 100644
index 0000000..147ef18
--- /dev/null
+++ b/include/ts_indexer.h
@@ -0,0 +1,141 @@
+/**
+ * \file
+ * TS indexer.
+ */
+
+#ifndef _TS_INDEXER_H_
+#define _TS_INDEXER_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**Video format*/
+typedef enum {
+ TS_INDEXER_VIDEO_FORMAT_MPEG2, /**< MPEG2*/
+ TS_INDEXER_VIDEO_FORMAT_H264, /**< H264*/
+ TS_INDEXER_VIDEO_FORMAT_HEVC /**< HEVC*/
+} TS_Indexer_StreamFormat_t;
+
+/**Event type.*/
+typedef enum {
+ TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME, /**< Video I frame PTS.*/
+ TS_INDEXER_EVENT_TYPE_VIDEO_PTS, /**< Video PTS.*/
+ TS_INDEXER_EVENT_TYPE_AUDIO_PTS /**< Audio PTS.*/
+} TS_Indexer_EventType_t;
+
+/**Stream Parser state.*/
+typedef enum {
+ TS_INDEXER_STATE_INIT, /**< Init state.*/
+ TS_INDEXER_STATE_TS_START, /**< TS header state with start_indicator==1.*/
+ TS_INDEXER_STATE_PES_HEADER, /**< PES header state.*/
+ TS_INDEXER_STATE_PES_PTS, /**< PES pts state.*/
+ TS_INDEXER_STATE_PES_I_FRAME /**< PES I-frame state.*/
+} TS_Indexer_State_t;
+
+/**Event.*/
+typedef struct {
+ TS_Indexer_EventType_t type; /**< Event type.*/
+ int pid; /**< The PID of the stream.*/
+ uint64_t offset; /**< The offset of the first TS packet of this frame.*/
+ uint64_t pts; /**< The PTS of this frame.*/
+} TS_Indexer_Event_t;
+
+/**TS indexer.*/
+typedef struct TS_Indexer_s TS_Indexer_t;
+
+/**Event callback function.*/
+typedef void (*TS_Indexer_EventCallback_t) (TS_Indexer_t *ts_indexer, TS_Indexer_Event_t *event);
+
+/**PES parser.*/
+typedef struct {
+ uint64_t pts; /**< The a/v PTS.*/
+ uint64_t offset; /**< The current offset.*/
+ uint8_t data[184+16]; /**< The PES data.*/
+ int len; /**< The length of PES data`.*/
+ TS_Indexer_State_t state; /**< The stream state.*/
+} PESParser;
+
+/**TS parser.*/
+typedef struct {
+ int pid; /**< The a/v PID.*/
+ TS_Indexer_StreamFormat_t format; /**< The a/v format.*/
+ PESParser PES; /**< The PES parser.*/
+ uint64_t offset; /**< The offset of packet with start indicator.*/
+} TSParser;
+
+/**TS indexer.*/
+struct TS_Indexer_s {
+ TSParser video_parser; /**< The video parser.*/
+ TSParser audio_parser; /**< The audio parser.*/
+ uint64_t offset; /**< The current offset.*/
+ TS_Indexer_EventCallback_t callback; /**< The event callback function.*/
+};
+
+/**
+ * Initialize the TS indexer.
+ * \param ts_indexer The TS indexer to be initialized.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int ts_indexer_init (TS_Indexer_t *ts_indexer);
+
+/**
+ * Release the TS indexer.
+ * \param ts_indexer The TS indexer to be released.
+ */
+void ts_indexer_destroy (TS_Indexer_t *ts_indexer);
+
+/**
+ * Set the video format.
+ * \param ts_indexer The TS indexer.
+ * \param format The video format.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int ts_indexer_set_video_format (TS_Indexer_t *ts_indexer, TS_Indexer_StreamFormat_t format);
+
+/**
+ * Set the video PID.
+ * \param ts_indexer The TS indexer.
+ * \param pid The video PID.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int ts_indexer_set_video_pid (TS_Indexer_t *ts_indexer, int pid);
+
+/**
+ * Set the audio PID.
+ * \param ts_indexer The TS indexer.
+ * \param pid The audio PID.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int ts_indexer_set_audio_pid (TS_Indexer_t *ts_indexer, int pid);
+
+/**
+ * Set the event callback function.
+ * \param ts_indexer The TS indexer.
+ * \param callback The event callback function.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int ts_indexer_set_event_callback (TS_Indexer_t *ts_indexer, TS_Indexer_EventCallback_t callback);
+
+/**
+ * Parse the TS stream and generate the index data.
+ * \param ts_indexer The TS indexer.
+ * \param data The TS data.
+ * \param len The length of the TS data in bytes.
+ * \return The left TS data length of bytes.
+ */
+int ts_indexer_parse (TS_Indexer_t *ts_indexer, uint8_t *data, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_TS_INDEXER_H_*/
+
diff --git a/src/ts_indexer.c b/src/ts_indexer.c
new file mode 100644
index 0000000..c0cef04
--- /dev/null
+++ b/src/ts_indexer.c
@@ -0,0 +1,544 @@
+#include <stdio.h>
+#include <string.h>
+#include "ts_indexer.h"
+
+#define TS_PKT_SIZE (188)
+
+//#define TS_INDEXER_DEBUG
+#ifdef TS_INDEXER_DEBUG
+#define INF(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
+#else
+#define INF(fmt, ...)
+#endif
+
+#define ERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
+
+#define NAL_TYPE_IDR 5 // IDR NALU 类型
+#define NAL_TYPE_NON_IDR 1 // 非 IDR NALU 类型
+
+/**
+ * Initialize the TS indexer.
+ * \param ts_indexer The TS indexer to be initialized.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int
+ts_indexer_init (TS_Indexer_t *ts_indexer)
+{
+ TSParser init_parser;
+
+ if (ts_indexer == NULL) {
+ return -1;
+ }
+
+ memset(&init_parser, 0, sizeof(TSParser));
+ init_parser.pid = 0x1fff;
+ init_parser.format = -1;
+ init_parser.PES.pts = -1;
+ init_parser.PES.offset = 0;
+ init_parser.PES.len = 0;
+ init_parser.PES.state = TS_INDEXER_STATE_INIT;
+
+ memcpy(&ts_indexer->video_parser, &init_parser, sizeof(TSParser));
+ memcpy(&ts_indexer->audio_parser, &init_parser, sizeof(TSParser));
+ ts_indexer->callback = NULL;
+ ts_indexer->offset = 0;
+
+ return 0;
+}
+
+/**
+ * Release the TS indexer.
+ * \param ts_indexer The TS indexer to be released.
+ */
+void
+ts_indexer_destroy (TS_Indexer_t *ts_indexer)
+{
+ return;
+}
+
+/**
+ * Set the video format.
+ * \param ts_indexer The TS indexer.
+ * \param format The stream format.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int
+ts_indexer_set_video_format (TS_Indexer_t *ts_indexer, TS_Indexer_StreamFormat_t format)
+{
+ if (ts_indexer == NULL)
+ return -1;
+
+ ts_indexer->video_parser.format = format;
+
+ return 0;
+}
+
+/**
+ * Set the video PID.
+ * \param ts_indexer The TS indexer.
+ * \param pid The video PID.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int
+ts_indexer_set_video_pid (TS_Indexer_t *ts_indexer, int pid)
+{
+ if (ts_indexer == NULL)
+ return -1;
+
+
+ TSParser *parser = &ts_indexer->video_parser;
+ parser->pid = pid;
+ parser->offset = 0;
+ parser->PES.pts = -1;
+ parser->PES.offset = 0;
+ parser->PES.len = 0;
+ parser->PES.state = TS_INDEXER_STATE_INIT;
+
+ return 0;
+}
+
+/**
+ * Set the audio PID.
+ * \param ts_indexer The TS indexer.
+ * \param pid The audio PID.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int
+ts_indexer_set_audio_pid (TS_Indexer_t *ts_indexer, int pid)
+{
+ if (ts_indexer == NULL)
+ return -1;
+
+ TSParser *parser = &ts_indexer->audio_parser;
+ parser->pid = pid;
+ parser->offset = 0;
+ parser->PES.pts = -1;
+ parser->PES.offset = 0;
+ parser->PES.len = 0;
+ parser->PES.state = TS_INDEXER_STATE_INIT;
+
+ return 0;
+}
+
+/**
+ * Set the event callback function.
+ * \param ts_indexer The TS indexer.
+ * \param callback The event callback function.
+ * \retval 0 On success.
+ * \retval -1 On error.
+ */
+int
+ts_indexer_set_event_callback (TS_Indexer_t *ts_indexer, TS_Indexer_EventCallback_t callback)
+{
+ ts_indexer->callback = callback;
+
+ return 0;
+}
+
+static void find_mpeg(uint8_t *data, int len, TS_Indexer_t *indexer, TSParser *stream)
+{
+ int i;
+ uint32_t needle = 0;
+ uint8_t *haystack = data;
+ int haystack_len = len;
+ int left = len;
+ // start code of picture header
+ uint8_t arr[4] = {0x00, 0x00, 0x01, 0x00};
+ TS_Indexer_Event_t event;
+
+ /* mpeg header needs at least 4 bytes */
+ if (left < 4) {
+ memcpy(&stream->PES.data[0], &haystack, left);
+ stream->PES.len = left;
+ return;
+ }
+
+ memset(&event, 0, sizeof(event));
+ event.pid = stream->pid;
+ event.offset = stream->offset;
+
+ for (i = 0; i < 4; ++i) {
+ needle += (arr[i] << (8 * i));
+ }
+
+ for (i = 0; i < haystack_len - sizeof(needle) + 1;) {
+ if (*(uint32_t *)(haystack + i) == needle) {
+ // picture header found
+ if (left < 5) {
+ INF("MPEG2 picture header across TS Packet\n");
+
+ /* MEPG2 picture header across TS packet, should cache the left data */
+ memcpy(&stream->PES.data[0], haystack + i, left);
+ stream->PES.len = left;
+ return;
+ }
+
+ int frame_type = (haystack[i + 5] >> 3) & 0x7;
+ INF("frame_type: %d\n", frame_type);
+ if (frame_type == 1) {
+ INF("I-frame found, offset: %ld\n", event.offset);
+ if (stream->format == TS_INDEXER_VIDEO_FORMAT_MPEG2) {
+ event.pts = stream->PES.pts;
+ event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
+ if (indexer->callback) {
+ indexer->callback(indexer, &event);
+ }
+ } else {
+ ERR("%s not MPEG2 video I-frame??\n", __func__);
+ }
+ }
+
+ i += 5;
+ left -= 5;
+ } else {
+ i ++;
+ left --;
+ }
+ }
+
+ if (left > 0) {
+ memcpy(&stream->PES.data[0], &haystack[i], left);
+ stream->PES.len = left;
+ } else {
+ stream->PES.len = 0;
+ }
+}
+
+static uint8_t *get_nalu(uint8_t *data, size_t len, size_t *nalu_len)
+{
+ size_t i;
+ uint8_t *p = data;
+
+ if (len == 0)
+ return NULL;
+
+ //INF("%s enter, len:%#lx\n", __func__, len);
+ for (i = 0; i < len - 4; ++i) {
+ if (p[i] == 0x00 && p[i+1] == 0x00 && p[i+2] == 0x01) {
+ uint8_t *frame_data = data + i;
+ size_t frame_data_len = 0;
+
+ //INF("%s start code prefix\n", __func__);
+ for (size_t j = i + 4; j < len - 4; ++j) {
+ if (p[j] == 0x00 && p[j+1] == 0x00 && p[j+2] == 0x01) {
+ frame_data_len = j - i;
+ break;
+ }
+ }
+
+ if (frame_data_len > 0) {
+ *nalu_len = frame_data_len;
+ return frame_data;
+ } else {
+ frame_data_len = len - i;
+ *nalu_len = frame_data_len;
+ return frame_data;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void find_h264(uint8_t *data, size_t len, TS_Indexer_t *indexer, TSParser *stream)
+{
+ TS_Indexer_Event_t event;
+ uint8_t *nalu = data;
+ size_t pes_data_len = len;
+ size_t nalu_len;
+
+ memset(&event, 0, sizeof(event));
+ event.pid = stream->pid;
+ event.offset = stream->offset;
+
+ while (1) {
+ int left = pes_data_len - (nalu - data);
+ if (left <= 4) {
+ memcpy(&stream->PES.data[0], nalu, left);
+ stream->PES.len = left;
+ break;
+ }
+
+ nalu = get_nalu(nalu, left, &nalu_len);
+ if (nalu == NULL)
+ break;
+
+ if (nalu[0] == 0x00 && nalu[1] == 0x00 && nalu[2] == 0x01) {
+ int nal_type = nalu[3] & 0x1f;
+ if (nal_type == NAL_TYPE_IDR || nal_type == NAL_TYPE_NON_IDR) {
+ int nal_ref_idr = nalu[3] & 0x60;
+ if (nal_ref_idr == 0x60) { //I-frame
+ INF("H264 I-frame found\n");
+ event.pts = stream->PES.pts;
+ event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
+ if (indexer->callback) {
+ indexer->callback(indexer, &event);
+ }
+ }
+ }
+ }
+
+ nalu += nalu_len;
+ }
+
+ stream->PES.len = 0;
+}
+
+static void find_h265(uint8_t *data, int len, TS_Indexer_t *indexer, TSParser *stream)
+{
+ TS_Indexer_Event_t event;
+ uint8_t *nalu = data;
+ size_t pes_data_len = len;
+ size_t nalu_len;
+
+ memset(&event, 0, sizeof(event));
+ event.pid = stream->pid;
+ event.offset = stream->offset;
+
+ while (nalu != NULL) {
+ int left = pes_data_len - (nalu - data);
+ if (left <= 4) {
+ memcpy(&stream->PES.data[0], nalu, left);
+ stream->PES.len = left;
+ break;
+ }
+
+ nalu = get_nalu(nalu, left, &nalu_len);
+ if (nalu == NULL)
+ break;
+
+ if (nalu[0] == 0x00 && nalu[1] == 0x00 && nalu[2] == 0x01) {
+ int nalu_type = (nalu[3] & 0x7E) >> 1;
+ //INF("nalu[3]: %#x, nalu_type: %#x\n", nalu[3], nalu_type);
+ if (nalu_type == 19) {
+ event.pts = stream->PES.pts;
+ event.type = TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME;
+ INF("HEVC I-frame found\n");
+ if (indexer->callback) {
+ indexer->callback(indexer, &event);
+ }
+ } else {
+ //INF("%s, not HEVC video I-frame. nalu_type: %#x\n", __func__, nalu_type);
+ }
+ }
+
+ nalu += nalu_len;
+ }
+ stream->PES.len = 0;
+}
+
+/*Parse the PES packet*/
+static void
+pes_packet(TS_Indexer_t *ts_indexer, uint8_t *data, int len, TSParser *stream)
+{
+ uint8_t *p = data;
+ TS_Indexer_t *pi = ts_indexer;
+ int left = len;
+ TS_Indexer_Event_t event;
+
+ memset(&event, 0, sizeof(event));
+ event.pid = stream->pid;
+ event.offset = stream->offset;
+
+ INF("stream: %p, state: %d\n", stream, stream->PES.state);
+ if (stream->PES.state <= TS_INDEXER_STATE_INIT) {
+ INF("%s, invalid state\n", __func__);
+ stream->PES.len = 0;
+ return;
+ }
+
+ /* needs splice two pieces of data together if have cache data */
+ if (stream->PES.len > 0) {
+ INF("%s have cache data %d bytes\n", __func__, stream->PES.len);
+ memcpy(&stream->PES.data[stream->PES.len], data, len);
+ p = &stream->PES.data[0];
+ left = stream->PES.len + len;
+ stream->PES.len = left;
+ }
+
+ if (stream->PES.state == TS_INDEXER_STATE_TS_START) {
+ /* needs cache data if no enough data to parse PES header */
+ if (left < 6) {
+ if (stream->PES.len <= 0) {
+ memcpy(&stream->PES.data[0], p, left);
+ stream->PES.len = left;
+ }
+ INF("not enough ts payload len: %#x\n", left);
+ return;
+ }
+
+ // chect the PES packet start code prefix
+ if ((p[0] != 0) || (p[1] != 0) || (p[2] != 1)) {
+ stream->PES.len = 0;
+ stream->PES.state = TS_INDEXER_STATE_INIT;
+ INF("%s, not the expected start code!\n", __func__);
+ return;
+ }
+
+ p += 6;
+ left -= 6;
+ stream->PES.state = TS_INDEXER_STATE_PES_HEADER;
+ }
+
+ if (stream->PES.state == TS_INDEXER_STATE_PES_HEADER) {
+ if (left < 8) {
+ if (stream->PES.len <= 0) {
+ memcpy(&stream->PES.data[0], p, left);
+ stream->PES.len = left;
+ }
+ INF("not enough optional pes header len: %#x\n", left);
+ return;
+ }
+
+ int header_length = p[2];
+ if (p[1] & 0x80) {
+ // parser pts
+ p += 3;
+ left -= 3;
+ event.pts = stream->PES.pts = (((uint64_t)(p[0] & 0x0E) << 29) |
+ ((uint64_t)p[1] << 22) |
+ ((uint64_t)(p[2] & 0xFE) << 14) |
+ ((uint64_t)p[3] << 7) |
+ (((uint64_t)p[4] & 0xFE) >> 1));
+ INF("pts: %lx, pos:%lx\n", event.pts, event.offset);
+
+ if (stream == &pi->video_parser) {
+ event.type = TS_INDEXER_EVENT_TYPE_VIDEO_PTS;
+ } else {
+ event.type = TS_INDEXER_EVENT_TYPE_AUDIO_PTS;
+ }
+ if (pi->callback) {
+ pi->callback(pi, &event);
+ }
+ }
+ stream->PES.state = TS_INDEXER_STATE_PES_PTS;
+
+ p += header_length;
+ left -= header_length;
+ }
+
+ stream->PES.len = left;
+ if (left <= 0
+ || stream->PES.state < TS_INDEXER_STATE_PES_PTS) {
+ return;
+ }
+
+ INF("stream->format: %d, left: %d\n", stream->format, left);
+ switch (stream->format) {
+ case TS_INDEXER_VIDEO_FORMAT_MPEG2:
+ find_mpeg(p, left, pi, &pi->video_parser);
+ break;
+
+ case TS_INDEXER_VIDEO_FORMAT_H264:
+ find_h264(p, left, pi, &pi->video_parser);
+ break;
+
+ case TS_INDEXER_VIDEO_FORMAT_HEVC:
+ find_h265(p, left, pi, &pi->video_parser);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*Parse the TS packet.*/
+static void
+ts_packet(TS_Indexer_t *ts_indexer, uint8_t *data)
+{
+ uint16_t pid;
+ uint8_t afc;
+ uint8_t *p = data;
+ TS_Indexer_t *pi = ts_indexer;
+ int len;
+ int is_start;
+
+ is_start = p[1] & 0x40;
+ pid = ((p[1] & 0x1f) << 8) | p[2];
+ if (pid == 0x1fff)
+ return;
+
+ if ((pid != pi->video_parser.pid) &&
+ (pid != pi->audio_parser.pid)) {
+ return;
+ }
+
+ if (is_start) {
+ if (pid == pi->video_parser.pid) {
+ pi->video_parser.offset = pi->offset;
+ pi->video_parser.PES.state = TS_INDEXER_STATE_TS_START;
+ }
+ else if (pid == pi->audio_parser.pid) {
+ pi->audio_parser.offset = pi->offset;
+ pi->audio_parser.PES.state = TS_INDEXER_STATE_TS_START;
+ }
+ }
+
+ afc = (p[3] >> 4) & 0x03;
+
+ p += 4;
+ len = 184;
+
+ if (afc & 2) {
+ int adp_field_len = p[0];
+ p++;
+ len--;
+
+ p += adp_field_len;
+ len -= adp_field_len;
+
+ if (len < 0) {
+ ERR("illegal adaption field length!");
+ return;
+ }
+ }
+
+ // has payload
+ if ((afc & 1) && (len > 0)) {
+ // parser pes packet
+ pes_packet(pi, p, len, (pid == pi->video_parser.pid) ? &pi->video_parser : &pi->audio_parser);
+ }
+}
+
+/**
+ * Parse the TS stream and generate the index data.
+ * \param ts_indexer The TS indexer.
+ * \param data The TS data.
+ * \param len The length of the TS data in bytes.
+ * \return The left TS data length of bytes.
+ */
+int
+ts_indexer_parse (TS_Indexer_t *ts_indexer, uint8_t *data, int len)
+{
+ uint8_t *p = data;
+ int left = len;
+
+ if (ts_indexer == NULL || data == NULL || len <= 0)
+ return -1;
+
+ while (left > 0) {
+ // find the sync byte
+ if (*p == 0x47) {
+ if (left < TS_PKT_SIZE) {
+ INF("%s data length may not be 188-byte aligned\n", __func__);
+ return left;
+ }
+
+ // parse one ts packet
+ ts_packet(ts_indexer, p);
+ p += TS_PKT_SIZE;
+ left -= TS_PKT_SIZE;
+ ts_indexer->offset += TS_PKT_SIZE;
+ } else {
+ p++;
+ left--;
+ ts_indexer->offset++;
+ }
+ }
+
+ return left;
+}
diff --git a/test/ts_indexer_test/Makefile b/test/ts_indexer_test/Makefile
new file mode 100644
index 0000000..b63f4a7
--- /dev/null
+++ b/test/ts_indexer_test/Makefile
@@ -0,0 +1,17 @@
+OUTPUT := ts_indexer_test
+SRCS := ts_indexer.c ts_indexer_test.c
+OBJS=$(SRCS:.c=.o)
+
+CFLAGS += -Wall -O2 -g -fPIC -I./../../include
+
+all: $(OUTPUT)
+
+$(OUTPUT): $(OBJS)
+ gcc $(LDFLAGS) -o $@ $^
+
+.c.o:
+ gcc $(CFLAGS) -c $< -o $@
+
+clean:
+ rm -rf $(OBJS) $(OUTPUT)
+
diff --git a/test/ts_indexer_test/ts_indexer_test.c b/test/ts_indexer_test/ts_indexer_test.c
new file mode 100644
index 0000000..68602e6
--- /dev/null
+++ b/test/ts_indexer_test/ts_indexer_test.c
@@ -0,0 +1,146 @@
+/**
+ * \page ts_indexer_test
+ * \section Introduction
+ * test code with ts_indexer_xxxxx APIs.
+ * It supports:
+ * \li parse pts/i-frame from MPEG2/H264/HEVC transport stream
+ *
+ * \section Usage
+ *
+ * \li ts: bitstream file path
+ * \li vpid: the pid of video bitstream with pts/i-frame
+ * \li vfmt: the video format
+ * \li apid: the pid of audio bitstream with pts
+ *
+ *
+ * parse pts/i-frame:
+ * \code
+ * ts_indexer_test [ts=] [vpid=] [vfmt=] [apid=]
+ * \endcode
+ *
+ * \endsection
+ */
+
+#ifdef _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ts_indexer.h"
+
+#define INF(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
+#define ERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
+
+static void ts_indexer_event_cb(TS_Indexer_t *ts_indexer, TS_Indexer_Event_t *event)
+{
+ if (ts_indexer == NULL || event == NULL)
+ return;
+
+ switch (event->type) {
+ case TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME:
+ INF("pid: %#x ", event->pid);
+ INF("TS_INDEXER_EVENT_TYPE_VIDEO_I_FRAME, offset: %lx, pts: %lx\n",
+ event->offset, event->pts);
+ break;
+
+ case TS_INDEXER_EVENT_TYPE_VIDEO_PTS:
+ #if 0
+ INF("PID: %#x ", event->pid);
+ INF("TS_INDEXER_EVENT_TYPE_VIDEO_PTS, Offset: %lx, Pts: %lx\n",
+ event->offset, event->pts);
+ #endif
+ break;
+
+ case TS_INDEXER_EVENT_TYPE_AUDIO_PTS:
+ #if 0
+ INF("PID: %#x ", event->pid);
+ INF("TS_INDEXER_EVENT_TYPE_AUDIO_PTS, Offset: %lx, Pts: %lx\n",
+ event->offset, event->pts);
+ #endif
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+static char *help_vfmt =
+ "\n\t0:TS_INDEXER_VIDEO_FORMAT_MPEG2" /**< MPEG2 video.*/
+ "\n\t1:TS_INDEXER_VIDEO_FORMAT_H264" /**< H264.*/
+ "\n\t2:TS_INDEXER_VIDEO_FORMAT_HEVC"; /**<HEVC.*/
+
+static void usage(int argc, char *argv[])
+{
+ INF("Usage: %s [ts=] [vpid=] [vfmt=] [apid=]\n", argv[0]);
+ INF("Usage: %s\n", help_vfmt);
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ char file[512];
+ int vpid = 0x1fff;
+ int apid = 0x1fff;
+ int vfmt = -1;
+ TS_Indexer_t ts_indexer;
+ int block_size = 188*1024;
+
+ memset(&file[0], 0, sizeof(file));
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i], "ts=", 3))
+ sscanf(argv[i], "ts=%s", &file[0]);
+ else if (!strncmp(argv[i], "vpid=", 5))
+ sscanf(argv[i], "vpid=%i", &vpid);
+ else if (!strncmp(argv[i], "vfmt=", 5))
+ sscanf(argv[i], "vfmt=%i", &vfmt);
+ else if (!strncmp(argv[i], "apid=", 5))
+ sscanf(argv[i], "apid=%i", &apid);
+ else if (!strncmp(argv[i], "help", 4)) {
+ usage(argc, argv);
+ exit(0);
+ }
+ }
+
+ if (argc == 1 ||
+ (vpid == 0x1fff && apid == 0x1fff) ||
+ (vpid != 0x1fff && vfmt == -1))
+ {
+ usage(argc, argv);
+ exit(0);
+ }
+
+ memset(&ts_indexer, 0, sizeof(TS_Indexer_t));
+ ts_indexer_init(&ts_indexer);
+ ts_indexer_set_video_pid(&ts_indexer, vpid);
+ ts_indexer_set_audio_pid(&ts_indexer, apid);
+ ts_indexer_set_video_format(&ts_indexer, vfmt);
+ ts_indexer_set_event_callback(&ts_indexer, ts_indexer_event_cb);
+
+ FILE *f = fopen(file, "rb");
+ if (f == NULL) {
+ ERR("open %s failed!\n", file);
+ return -1;
+ }
+
+ uint8_t *data = malloc(block_size);
+ if (data == NULL) {
+ ERR("no heap memory!\n");
+ return -1;
+ }
+
+ size_t len = 0;
+ INF("vpid: %#x, vfmt: %d, apid:%#x\n", vpid, vfmt, apid);
+ while (1) {
+ len = fread(data, 1, block_size, f);
+ if (len <= 0)
+ break;
+
+ ts_indexer_parse(&ts_indexer, data, len);
+ }
+
+ return 0;
+}