|
|
|
@@ -190,8 +190,10 @@ int mpegts_write_section1(MpegTSSection *s, int tid, int id, |
|
|
|
/* we retransmit the SI info at this rate */ |
|
|
|
#define SDT_RETRANS_TIME 500 |
|
|
|
#define PAT_RETRANS_TIME 100 |
|
|
|
#define PCR_RETRANS_TIME 20 |
|
|
|
|
|
|
|
typedef struct MpegTSWriteStream { |
|
|
|
struct MpegTSService *service; |
|
|
|
int pid; /* stream associated pid */ |
|
|
|
int cc; |
|
|
|
int payload_index; |
|
|
|
@@ -201,10 +203,12 @@ typedef struct MpegTSWriteStream { |
|
|
|
|
|
|
|
typedef struct MpegTSService { |
|
|
|
MpegTSSection pmt; /* MPEG2 pmt table context */ |
|
|
|
int pcr_pid; |
|
|
|
int sid; /* service ID */ |
|
|
|
char *name; |
|
|
|
char *provider_name; |
|
|
|
int pcr_pid; |
|
|
|
int pcr_packet_count; |
|
|
|
int pcr_packet_freq; |
|
|
|
} MpegTSService; |
|
|
|
|
|
|
|
typedef struct MpegTSWrite { |
|
|
|
@@ -289,6 +293,34 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) |
|
|
|
q += 2; /* patched after */ |
|
|
|
|
|
|
|
/* write optional descriptors here */ |
|
|
|
switch(st->codec.codec_type) { |
|
|
|
case CODEC_TYPE_AUDIO: |
|
|
|
if (strlen(st->language) == 3) { |
|
|
|
*q++ = 0x0a; /* ISO 639 language descriptor */ |
|
|
|
*q++ = 4; |
|
|
|
*q++ = st->language[0]; |
|
|
|
*q++ = st->language[1]; |
|
|
|
*q++ = st->language[2]; |
|
|
|
*q++ = 0; /* undefined type */ |
|
|
|
} |
|
|
|
break; |
|
|
|
case CODEC_TYPE_SUBTITLE: |
|
|
|
{ |
|
|
|
const char *language; |
|
|
|
language = st->language; |
|
|
|
if (strlen(language) != 3) |
|
|
|
language = "eng"; |
|
|
|
*q++ = 0x59; |
|
|
|
*q++ = 8; |
|
|
|
*q++ = language[0]; |
|
|
|
*q++ = language[1]; |
|
|
|
*q++ = language[2]; |
|
|
|
*q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */ |
|
|
|
put16(&q, 1); /* page id */ |
|
|
|
put16(&q, 1); /* ancillary page id */ |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
val = 0xf000 | (q - desc_length_ptr - 2); |
|
|
|
desc_length_ptr[0] = val >> 8; |
|
|
|
@@ -385,12 +417,16 @@ static int mpegts_write_header(AVFormatContext *s) |
|
|
|
MpegTSService *service; |
|
|
|
AVStream *st; |
|
|
|
int i, total_bit_rate; |
|
|
|
|
|
|
|
const char *service_name; |
|
|
|
|
|
|
|
ts->tsid = DEFAULT_TSID; |
|
|
|
ts->onid = DEFAULT_ONID; |
|
|
|
/* allocate a single DVB service */ |
|
|
|
service_name = s->title; |
|
|
|
if (service_name[0] == '\0') |
|
|
|
service_name = DEFAULT_SERVICE_NAME; |
|
|
|
service = mpegts_add_service(ts, DEFAULT_SID, |
|
|
|
DEFAULT_PROVIDER_NAME, DEFAULT_SERVICE_NAME); |
|
|
|
DEFAULT_PROVIDER_NAME, service_name); |
|
|
|
service->pmt.write_packet = section_write_packet; |
|
|
|
service->pmt.opaque = s; |
|
|
|
|
|
|
|
@@ -412,16 +448,26 @@ static int mpegts_write_header(AVFormatContext *s) |
|
|
|
if (!ts_st) |
|
|
|
goto fail; |
|
|
|
st->priv_data = ts_st; |
|
|
|
ts_st->service = service; |
|
|
|
ts_st->pid = DEFAULT_START_PID + i; |
|
|
|
ts_st->payload_pts = AV_NOPTS_VALUE; |
|
|
|
/* update PCR pid if needed */ |
|
|
|
/* update PCR pid by using the first video stream */ |
|
|
|
if (st->codec.codec_type == CODEC_TYPE_VIDEO && |
|
|
|
service->pcr_pid == 0x1fff) |
|
|
|
service->pcr_pid = ts_st->pid; |
|
|
|
total_bit_rate += st->codec.bit_rate; |
|
|
|
} |
|
|
|
|
|
|
|
/* if no video stream, use the first stream as PCR */ |
|
|
|
if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { |
|
|
|
ts_st = s->streams[0]->priv_data; |
|
|
|
service->pcr_pid = ts_st->pid; |
|
|
|
} |
|
|
|
|
|
|
|
if (total_bit_rate <= 8 * 1024) |
|
|
|
total_bit_rate = 8 * 1024; |
|
|
|
service->pcr_packet_freq = (total_bit_rate * PCR_RETRANS_TIME) / |
|
|
|
(TS_PACKET_SIZE * 8 * 1000); |
|
|
|
ts->sdt_packet_freq = (total_bit_rate * SDT_RETRANS_TIME) / |
|
|
|
(TS_PACKET_SIZE * 8 * 1000); |
|
|
|
ts->pat_packet_freq = (total_bit_rate * PAT_RETRANS_TIME) / |
|
|
|
@@ -477,12 +523,27 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
|
|
|
MpegTSWriteStream *ts_st = st->priv_data; |
|
|
|
uint8_t buf[TS_PACKET_SIZE]; |
|
|
|
uint8_t *q; |
|
|
|
int val, is_start, len, ts_len, header_len; |
|
|
|
int val, is_start, len, header_len, write_pcr, private_code; |
|
|
|
int afc_len, stuffing_len; |
|
|
|
int64_t pcr = -1; /* avoid warning */ |
|
|
|
|
|
|
|
is_start = 1; |
|
|
|
while (payload_size > 0) { |
|
|
|
retransmit_si_info(s); |
|
|
|
|
|
|
|
write_pcr = 0; |
|
|
|
if (ts_st->pid == ts_st->service->pcr_pid) { |
|
|
|
ts_st->service->pcr_packet_count++; |
|
|
|
if (ts_st->service->pcr_packet_count >= |
|
|
|
ts_st->service->pcr_packet_freq) { |
|
|
|
ts_st->service->pcr_packet_count = 0; |
|
|
|
write_pcr = 1; |
|
|
|
/* XXX: this is incorrect, but at least we have a PCR |
|
|
|
value */ |
|
|
|
pcr = pts; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* prepare packet header */ |
|
|
|
q = buf; |
|
|
|
*q++ = 0x47; |
|
|
|
@@ -491,25 +552,50 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
|
|
|
val |= 0x40; |
|
|
|
*q++ = val; |
|
|
|
*q++ = ts_st->pid; |
|
|
|
*q++ = 0x10 | ts_st->cc; |
|
|
|
*q++ = 0x10 | ts_st->cc | (write_pcr ? 0x20 : 0); |
|
|
|
ts_st->cc = (ts_st->cc + 1) & 0xf; |
|
|
|
if (write_pcr) { |
|
|
|
*q++ = 7; /* AFC length */ |
|
|
|
*q++ = 0x10; /* flags: PCR present */ |
|
|
|
*q++ = pcr >> 25; |
|
|
|
*q++ = pcr >> 17; |
|
|
|
*q++ = pcr >> 9; |
|
|
|
*q++ = pcr >> 1; |
|
|
|
*q++ = (pcr & 1) << 7; |
|
|
|
*q++ = 0; |
|
|
|
} |
|
|
|
if (is_start) { |
|
|
|
/* write PES header */ |
|
|
|
*q++ = 0x00; |
|
|
|
*q++ = 0x00; |
|
|
|
*q++ = 0x01; |
|
|
|
if (st->codec.codec_type == CODEC_TYPE_VIDEO) |
|
|
|
private_code = 0; |
|
|
|
if (st->codec.codec_type == CODEC_TYPE_VIDEO) { |
|
|
|
*q++ = 0xe0; |
|
|
|
else |
|
|
|
*q++ = 0xc0; |
|
|
|
} else if (st->codec.codec_type == CODEC_TYPE_AUDIO && |
|
|
|
(st->codec.codec_id == CODEC_ID_MP2 || |
|
|
|
st->codec.codec_id == CODEC_ID_MP3)) { |
|
|
|
*q++ = 0xc0; |
|
|
|
} else { |
|
|
|
*q++ = 0xbd; |
|
|
|
if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) { |
|
|
|
private_code = 0x20; |
|
|
|
} |
|
|
|
} |
|
|
|
if (pts != AV_NOPTS_VALUE) |
|
|
|
header_len = 8; |
|
|
|
else |
|
|
|
header_len = 3; |
|
|
|
if (private_code != 0) |
|
|
|
header_len++; |
|
|
|
len = payload_size + header_len; |
|
|
|
*q++ = len >> 8; |
|
|
|
*q++ = len; |
|
|
|
*q++ = 0x80; |
|
|
|
val = 0x80; |
|
|
|
/* data alignment indicator is required for subtitle data */ |
|
|
|
if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) |
|
|
|
val |= 0x04; |
|
|
|
*q++ = val; |
|
|
|
if (pts != AV_NOPTS_VALUE) { |
|
|
|
*q++ = 0x80; /* PTS only */ |
|
|
|
*q++ = 0x05; /* header len */ |
|
|
|
@@ -526,25 +612,42 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
|
|
|
*q++ = 0x00; |
|
|
|
*q++ = 0x00; |
|
|
|
} |
|
|
|
if (private_code != 0) |
|
|
|
*q++ = private_code; |
|
|
|
is_start = 0; |
|
|
|
} |
|
|
|
/* write header */ |
|
|
|
ts_len = q - buf; |
|
|
|
put_buffer(&s->pb, buf, ts_len); |
|
|
|
/* write data */ |
|
|
|
len = TS_PACKET_SIZE - ts_len; |
|
|
|
/* header size */ |
|
|
|
header_len = q - buf; |
|
|
|
/* data len */ |
|
|
|
len = TS_PACKET_SIZE - header_len; |
|
|
|
if (len > payload_size) |
|
|
|
len = payload_size; |
|
|
|
put_buffer(&s->pb, payload, len); |
|
|
|
stuffing_len = TS_PACKET_SIZE - header_len - len; |
|
|
|
if (stuffing_len > 0) { |
|
|
|
/* add stuffing with AFC */ |
|
|
|
if (buf[3] & 0x20) { |
|
|
|
/* stuffing already present: increase its size */ |
|
|
|
afc_len = buf[4] + 1; |
|
|
|
memmove(buf + 4 + afc_len + stuffing_len, |
|
|
|
buf + 4 + afc_len, |
|
|
|
header_len - (4 + afc_len)); |
|
|
|
buf[4] += stuffing_len; |
|
|
|
memset(buf + 4 + afc_len, 0xff, stuffing_len); |
|
|
|
} else { |
|
|
|
/* add stuffing */ |
|
|
|
memmove(buf + 4 + stuffing_len, buf + 4, header_len - 4); |
|
|
|
buf[3] |= 0x20; |
|
|
|
buf[4] = stuffing_len - 1; |
|
|
|
if (stuffing_len >= 2) { |
|
|
|
buf[5] = 0x00; |
|
|
|
memset(buf + 6, 0xff, stuffing_len - 2); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
memcpy(buf + TS_PACKET_SIZE - len, payload, len); |
|
|
|
payload += len; |
|
|
|
payload_size -= len; |
|
|
|
ts_len += len; |
|
|
|
/* stuffing */ |
|
|
|
len = TS_PACKET_SIZE - ts_len; |
|
|
|
if (len > 0) { |
|
|
|
memset(buf, 0xff, len); |
|
|
|
put_buffer(&s->pb, buf, len); |
|
|
|
} |
|
|
|
put_buffer(&s->pb, buf, TS_PACKET_SIZE); |
|
|
|
} |
|
|
|
put_flush_packet(&s->pb); |
|
|
|
} |
|
|
|
@@ -555,10 +658,17 @@ static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
int size= pkt->size; |
|
|
|
uint8_t *buf= pkt->data; |
|
|
|
MpegTSWriteStream *ts_st = st->priv_data; |
|
|
|
int len; |
|
|
|
int len, max_payload_size; |
|
|
|
|
|
|
|
if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) { |
|
|
|
/* for subtitle, a single PES packet must be generated */ |
|
|
|
mpegts_write_pes(s, st, buf, size, pkt->pts); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
max_payload_size = DEFAULT_PES_PAYLOAD_SIZE; |
|
|
|
while (size > 0) { |
|
|
|
len = DEFAULT_PES_PAYLOAD_SIZE - ts_st->payload_index; |
|
|
|
len = max_payload_size - ts_st->payload_index; |
|
|
|
if (len > size) |
|
|
|
len = size; |
|
|
|
memcpy(ts_st->payload + ts_st->payload_index, buf, len); |
|
|
|
@@ -567,7 +677,7 @@ static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
ts_st->payload_index += len; |
|
|
|
if (ts_st->payload_pts == AV_NOPTS_VALUE) |
|
|
|
ts_st->payload_pts = pkt->pts; |
|
|
|
if (ts_st->payload_index >= DEFAULT_PES_PAYLOAD_SIZE) { |
|
|
|
if (ts_st->payload_index >= max_payload_size) { |
|
|
|
mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_index, |
|
|
|
ts_st->payload_pts); |
|
|
|
ts_st->payload_pts = AV_NOPTS_VALUE; |
|
|
|
|