285 lines
9.8 KiB
C
285 lines
9.8 KiB
C
// ITU-T H.222.0(06/2012)
|
||
// Information technology – Generic coding of moving pictures and associated audio information: Systems
|
||
// 2.4.3.1 Transport stream(p34)
|
||
|
||
#include "mpeg-ts-proto.h"
|
||
#include "mpeg-ps-proto.h"
|
||
#include "mpeg-pes-proto.h"
|
||
#include "mpeg-util.h"
|
||
#include "mpeg-ts.h"
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
|
||
struct ts_demuxer_t {
|
||
struct pat_t pat;
|
||
|
||
ts_demuxer_onpacket onpacket;
|
||
void *param;
|
||
};
|
||
|
||
static uint32_t adaptation_filed_read(struct ts_adaptation_field_t *adp, const uint8_t *data, size_t bytes)
|
||
{
|
||
// 2.4.3.4 Adaptation field
|
||
// Table 2-6
|
||
uint32_t i = 0;
|
||
uint32_t j = 0;
|
||
|
||
assert(bytes <= TS_PACKET_SIZE);
|
||
adp->adaptation_field_length = data[i++];
|
||
if (adp->adaptation_field_length > 0) {
|
||
adp->discontinuity_indicator = (data[i] >> 7) & 0x01;
|
||
adp->random_access_indicator = (data[i] >> 6) & 0x01;
|
||
adp->elementary_stream_priority_indicator = (data[i] >> 5) & 0x01;
|
||
adp->PCR_flag = (data[i] >> 4) & 0x01;
|
||
adp->OPCR_flag = (data[i] >> 3) & 0x01;
|
||
adp->splicing_point_flag = (data[i] >> 2) & 0x01;
|
||
adp->transport_private_data_flag = (data[i] >> 1) & 0x01;
|
||
adp->adaptation_field_extension_flag = (data[i] >> 0) & 0x01;
|
||
i++;
|
||
|
||
if (adp->PCR_flag) {
|
||
adp->program_clock_reference_base = ((uint64_t)data[i] << 25) | ((uint64_t)data[i + 1] << 17) | ((uint64_t)data[i + 2] << 9) | ((uint64_t)data[i + 3] << 1) | ((data[i + 4] >> 7) & 0x01);
|
||
adp->program_clock_reference_extension = ((data[i + 4] & 0x01) << 8) | data[i + 5];
|
||
|
||
i += 6;
|
||
}
|
||
|
||
if (adp->OPCR_flag) {
|
||
adp->original_program_clock_reference_base = (((uint64_t)data[i]) << 25) | ((uint64_t)data[i + 1] << 17) | ((uint64_t)data[i + 2] << 9) | ((uint64_t)data[i + 3] << 1) | ((data[i + 4] >> 7) & 0x01);
|
||
adp->original_program_clock_reference_extension = ((data[i + 4] & 0x01) << 1) | data[i + 5];
|
||
|
||
i += 6;
|
||
}
|
||
|
||
if (adp->splicing_point_flag) {
|
||
adp->splice_countdown = data[i++];
|
||
}
|
||
|
||
if (adp->transport_private_data_flag) {
|
||
adp->transport_private_data_length = data[i++];
|
||
for (j = 0; j < adp->transport_private_data_length; j++) {
|
||
// uint8_t transport_private_data = data[i+j];
|
||
}
|
||
|
||
i += adp->transport_private_data_length;
|
||
}
|
||
|
||
if (adp->adaptation_field_extension_flag) {
|
||
uint8_t reserved;
|
||
adp->adaptation_field_extension_length = data[i++];
|
||
adp->ltw_flag = (data[i] >> 7) & 0x01;
|
||
adp->piecewise_rate_flag = (data[i] >> 6) & 0x01;
|
||
adp->seamless_splice_flag = (data[i] >> 5) & 0x01;
|
||
reserved = data[i] & 0x1F;
|
||
|
||
i++;
|
||
if (adp->ltw_flag) {
|
||
// uint8_t ltw_valid_flag = (data[i] >> 7) & 0x01;
|
||
// uint16_t ltw_offset = ((data[i] & 0x7F) << 8) | data[i+1];
|
||
i += 2;
|
||
}
|
||
|
||
if (adp->piecewise_rate_flag) {
|
||
// uint32_t piecewise_rate = ((data[i] & 0x3F) << 16) | (data[i+1] << 8) | data[i+2];
|
||
i += 3;
|
||
}
|
||
|
||
if (adp->seamless_splice_flag) {
|
||
// uint8_t Splice_type = (data[i] >> 4) & 0x0F;
|
||
// uint32_t DTS_next_AU = (((data[i] >> 1) & 0x07) << 30) | (data[i+1] << 22) | (((data[i+2] >> 1) & 0x7F) << 15) | (data[i+3] << 7) | ((data[i+4] >> 1) & 0x7F);
|
||
|
||
i += 5;
|
||
}
|
||
|
||
// reserved byte
|
||
}
|
||
|
||
// stuffing byte
|
||
}
|
||
|
||
return adp->adaptation_field_length + 1;
|
||
}
|
||
|
||
#define TS_IS_SYNC_BYTE(data) (data[0] == TS_SYNC_BYTE)
|
||
#define TS_TRANSPORT_ERROR_INDICATOR(data) (data[1] & 0x80)
|
||
#define TS_PAYLOAD_UNIT_START_INDICATOR(data) (data[1] & 0x40)
|
||
#define TS_TRANSPORT_PRIORITY(data) (data[1] & 0x20)
|
||
|
||
size_t ts_demuxer_flush(struct ts_demuxer_t *ts)
|
||
{
|
||
uint32_t i, j;
|
||
for (i = 0; i < ts->pat.pmt_count; i++) {
|
||
for (j = 0; j < ts->pat.pmts[i].stream_count; j++) {
|
||
struct pes_t *pes = &ts->pat.pmts[i].streams[j];
|
||
if (pes->pkt.size < 5) {
|
||
continue;
|
||
}
|
||
|
||
if (PSI_STREAM_H264 == pes->codecid) {
|
||
const uint8_t aud[] = {0, 0, 0, 1, 0x09, 0xf0};
|
||
pes_packet(&pes->pkt, pes, aud, sizeof(aud), ts->onpacket, ts->param);
|
||
} else if (PSI_STREAM_H265 == pes->codecid) {
|
||
const uint8_t aud[] = {0, 0, 0, 1, 0x46, 0x01, 0x50};
|
||
pes_packet(&pes->pkt, pes, aud, sizeof(aud), ts->onpacket, ts->param);
|
||
} else {
|
||
//assert(0);
|
||
pes_packet(&pes->pkt, pes, NULL, 0, ts->onpacket, ts->param);
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
size_t ts_demuxer_input(struct ts_demuxer_t *ts, const uint8_t *data, size_t bytes)
|
||
{
|
||
int r = 0;
|
||
uint32_t i, j, k;
|
||
uint32_t PID;
|
||
struct ts_packet_header_t pkhd;
|
||
|
||
// 2.4.3 Specification of the transport stream syntax and semantics
|
||
// Transport stream packets shall be 188 bytes long.
|
||
assert(188 == bytes);
|
||
|
||
// 2.4.3.2 Transport stream packet layer
|
||
// Table 2-2
|
||
memset(&pkhd, 0, sizeof(pkhd));
|
||
/* assert(0x47 == data[0]); // sync_byte */
|
||
if (0x47 != data[0]) {
|
||
return (size_t) - 1;
|
||
}
|
||
PID = ((data[1] << 8) | data[2]) & 0x1FFF;
|
||
pkhd.transport_error_indicator = (data[1] >> 7) & 0x01;
|
||
pkhd.payload_unit_start_indicator = (data[1] >> 6) & 0x01;
|
||
pkhd.transport_priority = (data[1] >> 5) & 0x01;
|
||
pkhd.transport_scrambling_control = (data[3] >> 6) & 0x03;
|
||
pkhd.adaptation_field_control = (data[3] >> 4) & 0x03;
|
||
pkhd.continuity_counter = data[3] & 0x0F;
|
||
|
||
// printf("-----------------------------------------------\n");
|
||
// printf("PID[%u]: Error: %u, Start:%u, Priority:%u, Scrambler:%u, AF: %u, CC: %u\n", PID, pkhd.transport_error_indicator, pkhd.payload_unit_start_indicator, pkhd.transport_priority, pkhd.transport_scrambling_control, pkhd.adaptation_field_control, pkhd.continuity_counter);
|
||
|
||
i = 4;
|
||
if (0x02 & pkhd.adaptation_field_control) {
|
||
i += adaptation_filed_read(&pkhd.adaptation, data + 4, bytes - 4);
|
||
|
||
if (pkhd.adaptation.adaptation_field_length > 0 && pkhd.adaptation.PCR_flag) {
|
||
int64_t t;
|
||
t = pkhd.adaptation.program_clock_reference_base / 90L; // ms;
|
||
/* printf("pcr: %02d:%02d:%02d.%03d - %" PRId64 "/%u\n", (int)(t / 3600000), (int)(t % 3600000)/60000, (int)((t/1000) % 60), (int)(t % 1000), pkhd.adaptation.program_clock_reference_base, pkhd.adaptation.program_clock_reference_extension); */
|
||
}
|
||
}
|
||
|
||
if (0x01 & pkhd.adaptation_field_control) {
|
||
if (TS_PID_PAT == PID) {
|
||
if (TS_PAYLOAD_UNIT_START_INDICATOR(data)) {
|
||
i += 1; // pointer 0x00
|
||
}
|
||
|
||
pat_read(&ts->pat, data + i, bytes - i);
|
||
} else if (TS_PID_SDT == PID) {
|
||
if (TS_PAYLOAD_UNIT_START_INDICATOR(data)) {
|
||
i += 1; // pointer 0x00
|
||
}
|
||
sdt_read(&ts->pat, data + i, bytes - i);
|
||
} else {
|
||
for (j = 0; j < ts->pat.pmt_count; j++) {
|
||
if (PID == ts->pat.pmts[j].pid) {
|
||
if (TS_PAYLOAD_UNIT_START_INDICATOR(data)) {
|
||
i += 1; // pointer 0x00
|
||
}
|
||
|
||
pmt_read(&ts->pat.pmts[j], data + i, bytes - i);
|
||
break;
|
||
} else {
|
||
for (k = 0; k < ts->pat.pmts[j].stream_count; k++) {
|
||
struct pes_t *pes = &ts->pat.pmts[j].streams[k];
|
||
if (PID != pes->pid) {
|
||
continue;
|
||
}
|
||
|
||
if (TS_PAYLOAD_UNIT_START_INDICATOR(data)) {
|
||
size_t n;
|
||
n = pes_read_header(pes, data + i, bytes - i);
|
||
assert(n > 0);
|
||
i += n;
|
||
}
|
||
|
||
r = pes_packet(&pes->pkt, pes, data + i, bytes - i, ts->onpacket, ts->param);
|
||
break; // find stream
|
||
}
|
||
} // PMT handler
|
||
}
|
||
} // PAT handler
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
static inline int mpeg_ts_is_idr_first_packet(const void *packet, int bytes)
|
||
{
|
||
const unsigned char *data;
|
||
struct ts_packet_header_t pkhd;
|
||
int payload_unit_start_indicator;
|
||
|
||
memset(&pkhd, 0, sizeof(pkhd));
|
||
|
||
data = (const unsigned char *)packet;
|
||
payload_unit_start_indicator = data[1] & 0x40;
|
||
pkhd.adaptation_field_control = (data[3] >> 4) & 0x03;
|
||
pkhd.continuity_counter = data[3] & 0x0F;
|
||
|
||
if (0x02 == pkhd.adaptation_field_control || 0x03 == pkhd.adaptation_field_control) {
|
||
adaptation_filed_read(&pkhd.adaptation, data + 4, bytes - 4);
|
||
}
|
||
|
||
return (payload_unit_start_indicator && pkhd.adaptation.random_access_indicator) ? 1 : 0;
|
||
}
|
||
|
||
struct ts_demuxer_t *ts_demuxer_create(ts_demuxer_onpacket onpacket, void *param)
|
||
{
|
||
struct ts_demuxer_t *ts;
|
||
ts = calloc(1, sizeof(struct ts_demuxer_t));
|
||
if (!ts) {
|
||
return NULL;
|
||
}
|
||
|
||
ts->onpacket = onpacket;
|
||
ts->param = param;
|
||
return ts;
|
||
}
|
||
|
||
int ts_demuxer_destroy(struct ts_demuxer_t *ts)
|
||
{
|
||
size_t i, j;
|
||
struct pes_t *pes;
|
||
for (i = 0; i < ts->pat.pmt_count; i++) {
|
||
for (j = 0; j < ts->pat.pmts[i].stream_count; j++) {
|
||
pes = &ts->pat.pmts[i].streams[j];
|
||
if (pes->pkt.data) {
|
||
free(pes->pkt.data);
|
||
}
|
||
pes->pkt.data = NULL;
|
||
}
|
||
}
|
||
|
||
free(ts);
|
||
return 0;
|
||
}
|
||
|
||
int ts_demuxer_getservice(struct ts_demuxer_t *ts, int program, char *provider, int nprovider, char *name, int nname)
|
||
{
|
||
struct pmt_t *pmt;
|
||
pmt = pat_find(&ts->pat, program);
|
||
if (NULL == pmt) {
|
||
return -1;
|
||
}
|
||
|
||
extern int snprintf(char *buf, unsigned long size, const char *fmt, ...);
|
||
snprintf(provider, nprovider, "%s", pmt->provider);
|
||
snprintf(name, nname, "%s", pmt->name);
|
||
return 0;
|
||
}
|
||
|