Originally committed as revision 4349 to svn://svn.ffmpeg.org/ffmpeg/trunk
Fabrice Bellard authored on 2005/06/03 23:05:41... | ... |
@@ -190,8 +190,10 @@ int mpegts_write_section1(MpegTSSection *s, int tid, int id, |
190 | 190 |
/* we retransmit the SI info at this rate */ |
191 | 191 |
#define SDT_RETRANS_TIME 500 |
192 | 192 |
#define PAT_RETRANS_TIME 100 |
193 |
+#define PCR_RETRANS_TIME 20 |
|
193 | 194 |
|
194 | 195 |
typedef struct MpegTSWriteStream { |
196 |
+ struct MpegTSService *service; |
|
195 | 197 |
int pid; /* stream associated pid */ |
196 | 198 |
int cc; |
197 | 199 |
int payload_index; |
... | ... |
@@ -201,10 +203,12 @@ typedef struct MpegTSWriteStream { |
201 | 201 |
|
202 | 202 |
typedef struct MpegTSService { |
203 | 203 |
MpegTSSection pmt; /* MPEG2 pmt table context */ |
204 |
- int pcr_pid; |
|
205 | 204 |
int sid; /* service ID */ |
206 | 205 |
char *name; |
207 | 206 |
char *provider_name; |
207 |
+ int pcr_pid; |
|
208 |
+ int pcr_packet_count; |
|
209 |
+ int pcr_packet_freq; |
|
208 | 210 |
} MpegTSService; |
209 | 211 |
|
210 | 212 |
typedef struct MpegTSWrite { |
... | ... |
@@ -289,6 +293,34 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) |
289 | 289 |
q += 2; /* patched after */ |
290 | 290 |
|
291 | 291 |
/* write optional descriptors here */ |
292 |
+ switch(st->codec.codec_type) { |
|
293 |
+ case CODEC_TYPE_AUDIO: |
|
294 |
+ if (strlen(st->language) == 3) { |
|
295 |
+ *q++ = 0x0a; /* ISO 639 language descriptor */ |
|
296 |
+ *q++ = 4; |
|
297 |
+ *q++ = st->language[0]; |
|
298 |
+ *q++ = st->language[1]; |
|
299 |
+ *q++ = st->language[2]; |
|
300 |
+ *q++ = 0; /* undefined type */ |
|
301 |
+ } |
|
302 |
+ break; |
|
303 |
+ case CODEC_TYPE_SUBTITLE: |
|
304 |
+ { |
|
305 |
+ const char *language; |
|
306 |
+ language = st->language; |
|
307 |
+ if (strlen(language) != 3) |
|
308 |
+ language = "eng"; |
|
309 |
+ *q++ = 0x59; |
|
310 |
+ *q++ = 8; |
|
311 |
+ *q++ = language[0]; |
|
312 |
+ *q++ = language[1]; |
|
313 |
+ *q++ = language[2]; |
|
314 |
+ *q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */ |
|
315 |
+ put16(&q, 1); /* page id */ |
|
316 |
+ put16(&q, 1); /* ancillary page id */ |
|
317 |
+ } |
|
318 |
+ break; |
|
319 |
+ } |
|
292 | 320 |
|
293 | 321 |
val = 0xf000 | (q - desc_length_ptr - 2); |
294 | 322 |
desc_length_ptr[0] = val >> 8; |
... | ... |
@@ -385,12 +417,16 @@ static int mpegts_write_header(AVFormatContext *s) |
385 | 385 |
MpegTSService *service; |
386 | 386 |
AVStream *st; |
387 | 387 |
int i, total_bit_rate; |
388 |
- |
|
388 |
+ const char *service_name; |
|
389 |
+ |
|
389 | 390 |
ts->tsid = DEFAULT_TSID; |
390 | 391 |
ts->onid = DEFAULT_ONID; |
391 | 392 |
/* allocate a single DVB service */ |
393 |
+ service_name = s->title; |
|
394 |
+ if (service_name[0] == '\0') |
|
395 |
+ service_name = DEFAULT_SERVICE_NAME; |
|
392 | 396 |
service = mpegts_add_service(ts, DEFAULT_SID, |
393 |
- DEFAULT_PROVIDER_NAME, DEFAULT_SERVICE_NAME); |
|
397 |
+ DEFAULT_PROVIDER_NAME, service_name); |
|
394 | 398 |
service->pmt.write_packet = section_write_packet; |
395 | 399 |
service->pmt.opaque = s; |
396 | 400 |
|
... | ... |
@@ -412,16 +448,26 @@ static int mpegts_write_header(AVFormatContext *s) |
412 | 412 |
if (!ts_st) |
413 | 413 |
goto fail; |
414 | 414 |
st->priv_data = ts_st; |
415 |
+ ts_st->service = service; |
|
415 | 416 |
ts_st->pid = DEFAULT_START_PID + i; |
416 | 417 |
ts_st->payload_pts = AV_NOPTS_VALUE; |
417 |
- /* update PCR pid if needed */ |
|
418 |
+ /* update PCR pid by using the first video stream */ |
|
418 | 419 |
if (st->codec.codec_type == CODEC_TYPE_VIDEO && |
419 | 420 |
service->pcr_pid == 0x1fff) |
420 | 421 |
service->pcr_pid = ts_st->pid; |
421 | 422 |
total_bit_rate += st->codec.bit_rate; |
422 | 423 |
} |
424 |
+ |
|
425 |
+ /* if no video stream, use the first stream as PCR */ |
|
426 |
+ if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { |
|
427 |
+ ts_st = s->streams[0]->priv_data; |
|
428 |
+ service->pcr_pid = ts_st->pid; |
|
429 |
+ } |
|
430 |
+ |
|
423 | 431 |
if (total_bit_rate <= 8 * 1024) |
424 | 432 |
total_bit_rate = 8 * 1024; |
433 |
+ service->pcr_packet_freq = (total_bit_rate * PCR_RETRANS_TIME) / |
|
434 |
+ (TS_PACKET_SIZE * 8 * 1000); |
|
425 | 435 |
ts->sdt_packet_freq = (total_bit_rate * SDT_RETRANS_TIME) / |
426 | 436 |
(TS_PACKET_SIZE * 8 * 1000); |
427 | 437 |
ts->pat_packet_freq = (total_bit_rate * PAT_RETRANS_TIME) / |
... | ... |
@@ -477,12 +523,27 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
477 | 477 |
MpegTSWriteStream *ts_st = st->priv_data; |
478 | 478 |
uint8_t buf[TS_PACKET_SIZE]; |
479 | 479 |
uint8_t *q; |
480 |
- int val, is_start, len, ts_len, header_len; |
|
480 |
+ int val, is_start, len, header_len, write_pcr, private_code; |
|
481 |
+ int afc_len, stuffing_len; |
|
482 |
+ int64_t pcr = -1; /* avoid warning */ |
|
481 | 483 |
|
482 | 484 |
is_start = 1; |
483 | 485 |
while (payload_size > 0) { |
484 | 486 |
retransmit_si_info(s); |
485 | 487 |
|
488 |
+ write_pcr = 0; |
|
489 |
+ if (ts_st->pid == ts_st->service->pcr_pid) { |
|
490 |
+ ts_st->service->pcr_packet_count++; |
|
491 |
+ if (ts_st->service->pcr_packet_count >= |
|
492 |
+ ts_st->service->pcr_packet_freq) { |
|
493 |
+ ts_st->service->pcr_packet_count = 0; |
|
494 |
+ write_pcr = 1; |
|
495 |
+ /* XXX: this is incorrect, but at least we have a PCR |
|
496 |
+ value */ |
|
497 |
+ pcr = pts; |
|
498 |
+ } |
|
499 |
+ } |
|
500 |
+ |
|
486 | 501 |
/* prepare packet header */ |
487 | 502 |
q = buf; |
488 | 503 |
*q++ = 0x47; |
... | ... |
@@ -491,25 +552,50 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
491 | 491 |
val |= 0x40; |
492 | 492 |
*q++ = val; |
493 | 493 |
*q++ = ts_st->pid; |
494 |
- *q++ = 0x10 | ts_st->cc; |
|
494 |
+ *q++ = 0x10 | ts_st->cc | (write_pcr ? 0x20 : 0); |
|
495 | 495 |
ts_st->cc = (ts_st->cc + 1) & 0xf; |
496 |
+ if (write_pcr) { |
|
497 |
+ *q++ = 7; /* AFC length */ |
|
498 |
+ *q++ = 0x10; /* flags: PCR present */ |
|
499 |
+ *q++ = pcr >> 25; |
|
500 |
+ *q++ = pcr >> 17; |
|
501 |
+ *q++ = pcr >> 9; |
|
502 |
+ *q++ = pcr >> 1; |
|
503 |
+ *q++ = (pcr & 1) << 7; |
|
504 |
+ *q++ = 0; |
|
505 |
+ } |
|
496 | 506 |
if (is_start) { |
497 | 507 |
/* write PES header */ |
498 | 508 |
*q++ = 0x00; |
499 | 509 |
*q++ = 0x00; |
500 | 510 |
*q++ = 0x01; |
501 |
- if (st->codec.codec_type == CODEC_TYPE_VIDEO) |
|
511 |
+ private_code = 0; |
|
512 |
+ if (st->codec.codec_type == CODEC_TYPE_VIDEO) { |
|
502 | 513 |
*q++ = 0xe0; |
503 |
- else |
|
504 |
- *q++ = 0xc0; |
|
514 |
+ } else if (st->codec.codec_type == CODEC_TYPE_AUDIO && |
|
515 |
+ (st->codec.codec_id == CODEC_ID_MP2 || |
|
516 |
+ st->codec.codec_id == CODEC_ID_MP3)) { |
|
517 |
+ *q++ = 0xc0; |
|
518 |
+ } else { |
|
519 |
+ *q++ = 0xbd; |
|
520 |
+ if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) { |
|
521 |
+ private_code = 0x20; |
|
522 |
+ } |
|
523 |
+ } |
|
505 | 524 |
if (pts != AV_NOPTS_VALUE) |
506 | 525 |
header_len = 8; |
507 | 526 |
else |
508 | 527 |
header_len = 3; |
528 |
+ if (private_code != 0) |
|
529 |
+ header_len++; |
|
509 | 530 |
len = payload_size + header_len; |
510 | 531 |
*q++ = len >> 8; |
511 | 532 |
*q++ = len; |
512 |
- *q++ = 0x80; |
|
533 |
+ val = 0x80; |
|
534 |
+ /* data alignment indicator is required for subtitle data */ |
|
535 |
+ if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) |
|
536 |
+ val |= 0x04; |
|
537 |
+ *q++ = val; |
|
513 | 538 |
if (pts != AV_NOPTS_VALUE) { |
514 | 539 |
*q++ = 0x80; /* PTS only */ |
515 | 540 |
*q++ = 0x05; /* header len */ |
... | ... |
@@ -526,25 +612,42 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, |
526 | 526 |
*q++ = 0x00; |
527 | 527 |
*q++ = 0x00; |
528 | 528 |
} |
529 |
+ if (private_code != 0) |
|
530 |
+ *q++ = private_code; |
|
529 | 531 |
is_start = 0; |
530 | 532 |
} |
531 |
- /* write header */ |
|
532 |
- ts_len = q - buf; |
|
533 |
- put_buffer(&s->pb, buf, ts_len); |
|
534 |
- /* write data */ |
|
535 |
- len = TS_PACKET_SIZE - ts_len; |
|
533 |
+ /* header size */ |
|
534 |
+ header_len = q - buf; |
|
535 |
+ /* data len */ |
|
536 |
+ len = TS_PACKET_SIZE - header_len; |
|
536 | 537 |
if (len > payload_size) |
537 | 538 |
len = payload_size; |
538 |
- put_buffer(&s->pb, payload, len); |
|
539 |
+ stuffing_len = TS_PACKET_SIZE - header_len - len; |
|
540 |
+ if (stuffing_len > 0) { |
|
541 |
+ /* add stuffing with AFC */ |
|
542 |
+ if (buf[3] & 0x20) { |
|
543 |
+ /* stuffing already present: increase its size */ |
|
544 |
+ afc_len = buf[4] + 1; |
|
545 |
+ memmove(buf + 4 + afc_len + stuffing_len, |
|
546 |
+ buf + 4 + afc_len, |
|
547 |
+ header_len - (4 + afc_len)); |
|
548 |
+ buf[4] += stuffing_len; |
|
549 |
+ memset(buf + 4 + afc_len, 0xff, stuffing_len); |
|
550 |
+ } else { |
|
551 |
+ /* add stuffing */ |
|
552 |
+ memmove(buf + 4 + stuffing_len, buf + 4, header_len - 4); |
|
553 |
+ buf[3] |= 0x20; |
|
554 |
+ buf[4] = stuffing_len - 1; |
|
555 |
+ if (stuffing_len >= 2) { |
|
556 |
+ buf[5] = 0x00; |
|
557 |
+ memset(buf + 6, 0xff, stuffing_len - 2); |
|
558 |
+ } |
|
559 |
+ } |
|
560 |
+ } |
|
561 |
+ memcpy(buf + TS_PACKET_SIZE - len, payload, len); |
|
539 | 562 |
payload += len; |
540 | 563 |
payload_size -= len; |
541 |
- ts_len += len; |
|
542 |
- /* stuffing */ |
|
543 |
- len = TS_PACKET_SIZE - ts_len; |
|
544 |
- if (len > 0) { |
|
545 |
- memset(buf, 0xff, len); |
|
546 |
- put_buffer(&s->pb, buf, len); |
|
547 |
- } |
|
564 |
+ put_buffer(&s->pb, buf, TS_PACKET_SIZE); |
|
548 | 565 |
} |
549 | 566 |
put_flush_packet(&s->pb); |
550 | 567 |
} |
... | ... |
@@ -555,10 +658,17 @@ static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) |
555 | 555 |
int size= pkt->size; |
556 | 556 |
uint8_t *buf= pkt->data; |
557 | 557 |
MpegTSWriteStream *ts_st = st->priv_data; |
558 |
- int len; |
|
558 |
+ int len, max_payload_size; |
|
559 | 559 |
|
560 |
+ if (st->codec.codec_type == CODEC_TYPE_SUBTITLE) { |
|
561 |
+ /* for subtitle, a single PES packet must be generated */ |
|
562 |
+ mpegts_write_pes(s, st, buf, size, pkt->pts); |
|
563 |
+ return 0; |
|
564 |
+ } |
|
565 |
+ |
|
566 |
+ max_payload_size = DEFAULT_PES_PAYLOAD_SIZE; |
|
560 | 567 |
while (size > 0) { |
561 |
- len = DEFAULT_PES_PAYLOAD_SIZE - ts_st->payload_index; |
|
568 |
+ len = max_payload_size - ts_st->payload_index; |
|
562 | 569 |
if (len > size) |
563 | 570 |
len = size; |
564 | 571 |
memcpy(ts_st->payload + ts_st->payload_index, buf, len); |
... | ... |
@@ -567,7 +677,7 @@ static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) |
567 | 567 |
ts_st->payload_index += len; |
568 | 568 |
if (ts_st->payload_pts == AV_NOPTS_VALUE) |
569 | 569 |
ts_st->payload_pts = pkt->pts; |
570 |
- if (ts_st->payload_index >= DEFAULT_PES_PAYLOAD_SIZE) { |
|
570 |
+ if (ts_st->payload_index >= max_payload_size) { |
|
571 | 571 |
mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_index, |
572 | 572 |
ts_st->payload_pts); |
573 | 573 |
ts_st->payload_pts = AV_NOPTS_VALUE; |