Newer
Older
Fabrice Bellard
committed
/*
* MPEG2 transport stream (aka DVB) muxer
* Copyright (c) 2003 Fabrice Bellard
Fabrice Bellard
committed
*
Diego Biurrun
committed
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
Fabrice Bellard
committed
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
Diego Biurrun
committed
* version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard
committed
*
Diego Biurrun
committed
* FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard
committed
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
Diego Biurrun
committed
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard
committed
*/
#include "libavutil/bswap.h"
#include "libavutil/crc.h"
#include "libavcodec/mpegvideo.h"
Fabrice Bellard
committed
#include "avformat.h"
#include "mpegts.h"
/* write DVB SI sections */
/*********************************************/
/* mpegts section writer */
typedef struct MpegTSSection {
int pid;
int cc;
void (*write_packet)(struct MpegTSSection *s, const uint8_t *packet);
void *opaque;
} MpegTSSection;
typedef struct MpegTSService {
MpegTSSection pmt; /* MPEG2 pmt table context */
int sid; /* service ID */
char *name;
char *provider_name;
int pcr_pid;
int pcr_packet_count;
int pcr_packet_freq;
} MpegTSService;
typedef struct MpegTSWrite {
MpegTSSection pat; /* MPEG2 pat table */
MpegTSSection sdt; /* MPEG2 sdt table context */
MpegTSService **services;
int sdt_packet_count;
int sdt_packet_freq;
int pat_packet_count;
int pat_packet_freq;
int nb_services;
int onid;
int tsid;
uint64_t cur_pcr;
int mux_rate;
} MpegTSWrite;
Fabrice Bellard
committed
/* NOTE: 4 bytes must be left at the end for the crc32 */
Stefan Huehner
committed
static void mpegts_write_section(MpegTSSection *s, uint8_t *buf, int len)
Fabrice Bellard
committed
{
MpegTSWrite *ts = ((AVFormatContext*)s->opaque)->priv_data;
Fabrice Bellard
committed
unsigned int crc;
unsigned char packet[TS_PACKET_SIZE];
const unsigned char *buf_ptr;
unsigned char *q;
int first, b, len1, left;
crc = bswap_32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, buf, len - 4));
Fabrice Bellard
committed
buf[len - 4] = (crc >> 24) & 0xff;
buf[len - 3] = (crc >> 16) & 0xff;
buf[len - 2] = (crc >> 8) & 0xff;
buf[len - 1] = (crc) & 0xff;
Fabrice Bellard
committed
/* send each packet */
buf_ptr = buf;
while (len > 0) {
first = (buf == buf_ptr);
q = packet;
*q++ = 0x47;
b = (s->pid >> 8);
if (first)
b |= 0x40;
*q++ = b;
*q++ = s->pid;
*q++ = 0x10 | s->cc;
Fabrice Bellard
committed
if (first)
*q++ = 0; /* 0 offset */
len1 = TS_PACKET_SIZE - (q - packet);
Fabrice Bellard
committed
len1 = len;
memcpy(q, buf_ptr, len1);
q += len1;
/* add known padding data */
left = TS_PACKET_SIZE - (q - packet);
if (left > 0)
memset(q, 0xff, left);
s->write_packet(s, packet);
buf_ptr += len1;
len -= len1;
ts->cur_pcr += TS_PACKET_SIZE*8*90000LL/ts->mux_rate;
Fabrice Bellard
committed
}
}
static inline void put16(uint8_t **q_ptr, int val)
{
uint8_t *q;
q = *q_ptr;
*q++ = val >> 8;
*q++ = val;
*q_ptr = q;
}
Stefan Huehner
committed
static int mpegts_write_section1(MpegTSSection *s, int tid, int id,
Fabrice Bellard
committed
int version, int sec_num, int last_sec_num,
uint8_t *buf, int len)
{
uint8_t section[1024], *q;
unsigned int tot_len;
Fabrice Bellard
committed
tot_len = 3 + 5 + len + 4;
/* check if not too big */
if (tot_len > 1024)
return -1;
q = section;
*q++ = tid;
put16(&q, 0xb000 | (len + 5 + 4)); /* 5 byte header + 4 byte CRC */
put16(&q, id);
*q++ = 0xc1 | (version << 1); /* current_next_indicator = 1 */
*q++ = sec_num;
*q++ = last_sec_num;
memcpy(q, buf, len);
Fabrice Bellard
committed
mpegts_write_section(s, section, tot_len);
return 0;
}
/*********************************************/
/* mpegts writer */
#define DEFAULT_PMT_START_PID 0x1000
#define DEFAULT_START_PID 0x0100
#define DEFAULT_PROVIDER_NAME "FFmpeg"
#define DEFAULT_SERVICE_NAME "Service01"
/* default network id, transport stream and service identifiers */
#define DEFAULT_ONID 0x0001
#define DEFAULT_TSID 0x0001
#define DEFAULT_SID 0x0001
/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */
#define DEFAULT_PES_HEADER_FREQ 16
#define DEFAULT_PES_PAYLOAD_SIZE ((DEFAULT_PES_HEADER_FREQ - 1) * 184 + 170)
Fabrice Bellard
committed
/* we retransmit the SI info at this rate */
#define SDT_RETRANS_TIME 500
#define PAT_RETRANS_TIME 100
#define PCR_RETRANS_TIME 20
Fabrice Bellard
committed
typedef struct MpegTSWriteStream {
struct MpegTSService *service;
Fabrice Bellard
committed
int pid; /* stream associated pid */
int cc;
int payload_index;
int64_t payload_pts;
int64_t payload_dts;
uint8_t payload[DEFAULT_PES_PAYLOAD_SIZE];
Fabrice Bellard
committed
} MpegTSWriteStream;
static void mpegts_write_pat(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
MpegTSService *service;
uint8_t data[1012], *q;
int i;
Fabrice Bellard
committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
q = data;
for(i = 0; i < ts->nb_services; i++) {
service = ts->services[i];
put16(&q, service->sid);
put16(&q, 0xe000 | service->pmt.pid);
}
mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, 0, 0, 0,
data, q - data);
}
static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
{
// MpegTSWrite *ts = s->priv_data;
uint8_t data[1012], *q, *desc_length_ptr, *program_info_length_ptr;
int val, stream_type, i;
q = data;
put16(&q, 0xe000 | service->pcr_pid);
program_info_length_ptr = q;
q += 2; /* patched after */
/* put program info here */
val = 0xf000 | (q - program_info_length_ptr - 2);
program_info_length_ptr[0] = val >> 8;
program_info_length_ptr[1] = val;
Fabrice Bellard
committed
for(i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
Michael Niedermayer
committed
switch(st->codec->codec_id) {
case CODEC_ID_MPEG1VIDEO:
case CODEC_ID_MPEG2VIDEO:
stream_type = STREAM_TYPE_VIDEO_MPEG2;
Fabrice Bellard
committed
break;
case CODEC_ID_MPEG4:
stream_type = STREAM_TYPE_VIDEO_MPEG4;
break;
case CODEC_ID_H264:
stream_type = STREAM_TYPE_VIDEO_H264;
break;
case CODEC_ID_DIRAC:
stream_type = STREAM_TYPE_VIDEO_DIRAC;
break;
case CODEC_ID_MP2:
case CODEC_ID_MP3:
Michael Niedermayer
committed
stream_type = STREAM_TYPE_AUDIO_MPEG1;
Fabrice Bellard
committed
break;
case CODEC_ID_AAC:
stream_type = STREAM_TYPE_AUDIO_AAC;
break;
case CODEC_ID_AC3:
stream_type = STREAM_TYPE_AUDIO_AC3;
break;
Fabrice Bellard
committed
default:
stream_type = STREAM_TYPE_PRIVATE_DATA;
break;
}
*q++ = stream_type;
put16(&q, 0xe000 | ts_st->pid);
desc_length_ptr = q;
q += 2; /* patched after */
/* write optional descriptors here */
Michael Niedermayer
committed
switch(st->codec->codec_type) {
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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;
case CODEC_TYPE_VIDEO:
if (stream_type == STREAM_TYPE_VIDEO_DIRAC) {
*q++ = 0x05; /*MPEG-2 registration descriptor*/
*q++ = 4;
*q++ = 'd';
*q++ = 'r';
*q++ = 'a';
*q++ = 'c';
}
break;
}
Fabrice Bellard
committed
val = 0xf000 | (q - desc_length_ptr - 2);
desc_length_ptr[0] = val >> 8;
desc_length_ptr[1] = val;
}
mpegts_write_section1(&service->pmt, PMT_TID, service->sid, 0, 0, 0,
data, q - data);
Fabrice Bellard
committed
/* NOTE: str == NULL is accepted for an empty string */
static void putstr8(uint8_t **q_ptr, const char *str)
{
uint8_t *q;
int len;
q = *q_ptr;
if (!str)
len = 0;
else
len = strlen(str);
*q++ = len;
memcpy(q, str, len);
q += len;
*q_ptr = q;
}
static void mpegts_write_sdt(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
MpegTSService *service;
uint8_t data[1012], *q, *desc_list_len_ptr, *desc_len_ptr;
int i, running_status, free_ca_mode, val;
Fabrice Bellard
committed
q = data;
put16(&q, ts->onid);
*q++ = 0xff;
for(i = 0; i < ts->nb_services; i++) {
service = ts->services[i];
put16(&q, service->sid);
*q++ = 0xfc | 0x00; /* currently no EIT info */
desc_list_len_ptr = q;
q += 2;
running_status = 4; /* running */
free_ca_mode = 0;
/* write only one descriptor for the service name and provider */
*q++ = 0x48;
desc_len_ptr = q;
q++;
*q++ = 0x01; /* digital television service */
putstr8(&q, service->provider_name);
putstr8(&q, service->name);
desc_len_ptr[0] = q - desc_len_ptr - 1;
/* fill descriptor length */
val = (running_status << 13) | (free_ca_mode << 12) |
Fabrice Bellard
committed
(q - desc_list_len_ptr - 2);
desc_list_len_ptr[0] = val >> 8;
desc_list_len_ptr[1] = val;
}
mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, 0, 0, 0,
data, q - data);
}
static MpegTSService *mpegts_add_service(MpegTSWrite *ts,
int sid,
const char *provider_name,
Fabrice Bellard
committed
const char *name)
{
MpegTSService *service;
service = av_mallocz(sizeof(MpegTSService));
if (!service)
return NULL;
service->pmt.pid = DEFAULT_PMT_START_PID + ts->nb_services - 1;
service->sid = sid;
service->provider_name = av_strdup(provider_name);
service->name = av_strdup(name);
service->pcr_pid = 0x1fff;
dynarray_add(&ts->services, &ts->nb_services, service);
return service;
}
static void section_write_packet(MpegTSSection *s, const uint8_t *packet)
{
AVFormatContext *ctx = s->opaque;
put_buffer(ctx->pb, packet, TS_PACKET_SIZE);
Fabrice Bellard
committed
}
static int mpegts_write_header(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st;
MpegTSService *service;
AVStream *st;
int i, total_bit_rate;
const char *service_name;
uint64_t sdt_size, pat_pmt_size, pos;
Fabrice Bellard
committed
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, service_name);
Fabrice Bellard
committed
service->pmt.write_packet = section_write_packet;
service->pmt.opaque = s;
ts->pat.pid = PAT_PID;
ts->pat.cc = 0;
ts->pat.write_packet = section_write_packet;
ts->pat.opaque = s;
ts->sdt.pid = SDT_PID;
ts->sdt.cc = 0;
ts->sdt.write_packet = section_write_packet;
ts->sdt.opaque = s;
/* assign pids to each stream */
total_bit_rate = 0;
for(i = 0;i < s->nb_streams; i++) {
st = s->streams[i];
ts_st = av_mallocz(sizeof(MpegTSWriteStream));
if (!ts_st)
goto fail;
st->priv_data = ts_st;
ts_st->service = service;
Fabrice Bellard
committed
ts_st->pid = DEFAULT_START_PID + i;
ts_st->payload_pts = AV_NOPTS_VALUE;
ts_st->payload_dts = AV_NOPTS_VALUE;
/* update PCR pid by using the first video stream */
if (st->codec->codec_type == CODEC_TYPE_VIDEO &&
Fabrice Bellard
committed
service->pcr_pid == 0x1fff)
service->pcr_pid = ts_st->pid;
Michael Niedermayer
committed
total_bit_rate += st->codec->bit_rate;
Fabrice Bellard
committed
}
/* 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;
}
Fabrice Bellard
committed
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) /
Fabrice Bellard
committed
(TS_PACKET_SIZE * 8 * 1000);
ts->pat_packet_freq = (total_bit_rate * PAT_RETRANS_TIME) /
Fabrice Bellard
committed
(TS_PACKET_SIZE * 8 * 1000);
ts->mux_rate = 1; // avoid div by 0
Fabrice Bellard
committed
/* write info at the start of the file, so that it will be fast to
find them */
pos = url_ftell(s->pb);
Fabrice Bellard
committed
mpegts_write_sdt(s);
sdt_size = url_ftell(s->pb) - pos;
pos = url_ftell(s->pb);
Fabrice Bellard
committed
mpegts_write_pat(s);
for(i = 0; i < ts->nb_services; i++) {
mpegts_write_pmt(s, ts->services[i]);
}
pat_pmt_size = url_ftell(s->pb) - pos;
total_bit_rate +=
total_bit_rate * 25 / (8 * DEFAULT_PES_PAYLOAD_SIZE) + /* PES header size */
total_bit_rate * 4 / (8 * TS_PACKET_SIZE) + /* TS header size */
SDT_RETRANS_TIME * sdt_size + /* SDT size */
PAT_RETRANS_TIME * pat_pmt_size + /* PAT+PMT size */
PCR_RETRANS_TIME * 8; /* PCR size */
av_log(s, AV_LOG_DEBUG, "muxrate %d freq sdt %d pat %d\n",
total_bit_rate, ts->sdt_packet_freq, ts->pat_packet_freq);
if (s->mux_rate)
ts->mux_rate = s->mux_rate;
else
ts->mux_rate = total_bit_rate;
// adjust pcr
ts->cur_pcr /= ts->mux_rate;
put_flush_packet(s->pb);
Fabrice Bellard
committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
return 0;
fail:
for(i = 0;i < s->nb_streams; i++) {
st = s->streams[i];
av_free(st->priv_data);
}
return -1;
}
/* send SDT, PAT and PMT tables regulary */
static void retransmit_si_info(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
int i;
if (++ts->sdt_packet_count == ts->sdt_packet_freq) {
ts->sdt_packet_count = 0;
mpegts_write_sdt(s);
}
if (++ts->pat_packet_count == ts->pat_packet_freq) {
ts->pat_packet_count = 0;
mpegts_write_pat(s);
for(i = 0; i < ts->nb_services; i++) {
mpegts_write_pmt(s, ts->services[i]);
}
}
}
static void write_pts(uint8_t *q, int fourbits, int64_t pts)
{
int val;
val = fourbits << 4 | (((pts >> 30) & 0x07) << 1) | 1;
*q++ = val;
val = (((pts >> 15) & 0x7fff) << 1) | 1;
*q++ = val >> 8;
*q++ = val;
val = (((pts) & 0x7fff) << 1) | 1;
*q++ = val >> 8;
*q++ = val;
}
/* NOTE: pes_data contains all the PES packet */
static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
const uint8_t *payload, int payload_size,
int64_t pts, int64_t dts)
Fabrice Bellard
committed
{
MpegTSWriteStream *ts_st = st->priv_data;
MpegTSWrite *ts = s->priv_data;
uint8_t buf[TS_PACKET_SIZE];
Fabrice Bellard
committed
uint8_t *q;
int val, is_start, len, header_len, write_pcr, private_code, flags;
int afc_len, stuffing_len;
int64_t pcr = -1; /* avoid warning */
Fabrice Bellard
committed
is_start = 1;
while (payload_size > 0) {
retransmit_si_info(s);
Fabrice Bellard
committed
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;
}
}
/* prepare packet header */
q = buf;
*q++ = 0x47;
val = (ts_st->pid >> 8);
if (is_start)
val |= 0x40;
*q++ = val;
*q++ = ts_st->pid;
*q++ = 0x10 | ts_st->cc | (write_pcr ? 0x20 : 0);
ts_st->cc = (ts_st->cc + 1) & 0xf;
if (write_pcr) {
// add 11, pcr references the last byte of program clock reference base
pcr = ts->cur_pcr + (4+7)*8*90000LL / ts->mux_rate;
if (dts != AV_NOPTS_VALUE && dts < pcr)
av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
*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;
private_code = 0;
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
if (st->codec->codec_id == CODEC_ID_DIRAC) {
*q++ = 0xfd;
} else
*q++ = 0xe0;
Michael Niedermayer
committed
} else if (st->codec->codec_type == CODEC_TYPE_AUDIO &&
(st->codec->codec_id == CODEC_ID_MP2 ||
st->codec->codec_id == CODEC_ID_MP3)) {
} else {
*q++ = 0xbd;
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_SUBTITLE) {
private_code = 0x20;
}
}
header_len = 0;
flags = 0;
if (pts != AV_NOPTS_VALUE) {
header_len += 5;
flags |= 0x80;
}
if (dts != AV_NOPTS_VALUE && dts != pts) {
header_len += 5;
flags |= 0x40;
}
if (st->codec->codec_type == CODEC_TYPE_VIDEO &&
st->codec->codec_id == CODEC_ID_DIRAC) {
/* set PES_extension_flag */
pes_extension = 1;
flags |= 0x01;
/*
* One byte for PES2 extension flag +
* one byte for extension length +
* one byte for extension id
*/
header_len += 3;
}
len = payload_size + header_len + 3;
if (private_code != 0)
*q++ = len >> 8;
*q++ = len;
val = 0x80;
/* data alignment indicator is required for subtitle data */
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_SUBTITLE)
val |= 0x04;
*q++ = val;
*q++ = flags;
*q++ = header_len;
if (pts != AV_NOPTS_VALUE) {
write_pts(q, flags >> 6, pts);
q += 5;
}
if (dts != AV_NOPTS_VALUE && dts != pts) {
write_pts(q, 1, dts);
q += 5;
Fabrice Bellard
committed
}
if (pes_extension && st->codec->codec_id == CODEC_ID_DIRAC) {
flags = 0x01; /* set PES_extension_flag_2 */
*q++ = flags;
*q++ = 0x80 | 0x01; /* marker bit + extension length */
/*
* Set the stream id extension flag bit to 0 and
* write the extended stream id
*/
*q++ = 0x00 | 0x60;
}
if (private_code != 0)
*q++ = private_code;
is_start = 0;
Fabrice Bellard
committed
}
/* header size */
header_len = q - buf;
/* data len */
len = TS_PACKET_SIZE - header_len;
if (len > payload_size)
len = payload_size;
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,
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;
put_buffer(s->pb, buf, TS_PACKET_SIZE);
ts->cur_pcr += TS_PACKET_SIZE*8*90000LL/ts->mux_rate;
Fabrice Bellard
committed
}
put_flush_packet(s->pb);
}
static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st = s->streams[pkt->stream_index];
int size= pkt->size;
uint8_t *buf= pkt->data;
uint8_t *data= NULL;
MpegTSWriteStream *ts_st = st->priv_data;
int len, max_payload_size;
const uint8_t *access_unit_index = NULL;
const uint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE;
if (pkt->pts != AV_NOPTS_VALUE)
pts = pkt->pts + delay;
if (pkt->dts != AV_NOPTS_VALUE)
dts = pkt->dts + delay;
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_SUBTITLE) {
/* for subtitle, a single PES packet must be generated */
mpegts_write_pes(s, st, buf, size, pts, AV_NOPTS_VALUE);
return 0;
}
if (st->codec->codec_id == CODEC_ID_DIRAC) {
/* for Dirac, a single PES packet must be generated */
mpegts_write_pes(s, st, buf, size, pts, dts);
return 0;
}
max_payload_size = DEFAULT_PES_PAYLOAD_SIZE;
if (st->codec->codec_id == CODEC_ID_MPEG2VIDEO ||
st->codec->codec_id == CODEC_ID_MPEG1VIDEO) {
const uint8_t *p = pkt->data;
const uint8_t *end = pkt->data+pkt->size;
uint32_t state = -1;
while (p < end) {
p = ff_find_start_code(p, end, &state);
if (state == PICTURE_START_CODE) {
access_unit_index = p - 4;
break;
}
}
} if (st->codec->codec_id == CODEC_ID_H264) {
if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
av_log(s, AV_LOG_ERROR, "h264 bitstream malformated\n");
return -1;
}
if (pkt->data[4] != 0x09) { // AUD NAL
data = av_malloc(pkt->size+6);
if (!data)
return -1;
memcpy(data+6, pkt->data, pkt->size);
AV_WB32(data, 0x00000001);
data[4] = 0x09;
data[5] = 0xe0; // any slice type
buf = data;
size = pkt->size+6;
}
access_unit_index = buf;
Baptiste Coudurier
committed
} else {
access_unit_index = pkt->data;
}
if (!access_unit_index) {
av_log(s, AV_LOG_ERROR, "error, could not find access unit start\n");
return -1;
}
while (size > 0) {
len = max_payload_size - ts_st->payload_index;
if (len > size)
len = size;
memcpy(ts_st->payload + ts_st->payload_index, buf, len);
buf += len;
size -= len;
ts_st->payload_index += len;
if (access_unit_index && access_unit_index < buf &&
ts_st->payload_pts == AV_NOPTS_VALUE &&
ts_st->payload_dts == AV_NOPTS_VALUE) {
ts_st->payload_dts = dts;
ts_st->payload_pts = pts;
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_dts);
ts_st->payload_pts = AV_NOPTS_VALUE;
ts_st->payload_dts = AV_NOPTS_VALUE;
ts_st->payload_index = 0;
access_unit_index = NULL; // unset access unit to avoid setting pts/dts again
}
}
av_free(data);
Fabrice Bellard
committed
return 0;
}
static int mpegts_write_end(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st;
MpegTSService *service;
AVStream *st;
int i;
/* flush current packets */
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
ts_st = st->priv_data;
if (ts_st->payload_index > 0) {
mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_index,
ts_st->payload_pts, ts_st->payload_dts);
Fabrice Bellard
committed
}
}
put_flush_packet(s->pb);
Fabrice Bellard
committed
for(i = 0; i < ts->nb_services; i++) {
service = ts->services[i];
av_freep(&service->provider_name);
av_freep(&service->name);
av_free(service);
}
av_free(ts->services);
return 0;
}
AVOutputFormat mpegts_muxer = {
Fabrice Bellard
committed
"mpegts",
NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
Fabrice Bellard
committed
"video/x-mpegts",
Fabrice Bellard
committed
sizeof(MpegTSWrite),
CODEC_ID_MP2,
CODEC_ID_MPEG2VIDEO,
Fabrice Bellard
committed
mpegts_write_header,
mpegts_write_packet,
mpegts_write_end,
};