# HG changeset patch # User bellard # Date 1117807541 0 # Node ID e277d8928f4988a7efc97e7ac3e98db879306e93 # Parent 5f3e609d73611a522fa03a490d8295c3cbea8853 buggy PCR output (just to keep HW decoders happy) - audio and subtitle language support - 'title' stream field sets the service name - fixed TS packet stuffing code - support for subtitle PES packets diff -r 5f3e609d7361 -r e277d8928f49 mpegtsenc.c --- a/mpegtsenc.c Fri Jun 03 14:02:29 2005 +0000 +++ b/mpegtsenc.c Fri Jun 03 14:05:41 2005 +0000 @@ -190,8 +190,10 @@ /* 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ *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 @@ 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 @@ 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;