typedef struct {
uint8_t buffer[MAX_PAYLOAD_SIZE];
int buffer_ptr;
+ int nb_frames; /* number of starting frame encountered (AC3) */
+ int frame_start_offset; /* starting offset of the frame + 1 (0 if none) */
uint8_t id;
int max_buffer_size; /* in bytes */
int packet_number;
typedef struct {
int packet_size; /* required packet size */
- int packet_data_max_size; /* maximum data size inside a packet */
int packet_number;
int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */
int system_header_freq;
+ int system_header_size;
int mux_rate; /* bitrate in units of 50 bytes/s */
/* stream info */
int audio_bound;
return size;
}
+static int get_system_header_size(AVFormatContext *ctx)
+{
+ int buf_index, i, private_stream_coded;
+ StreamInfo *stream;
+
+ buf_index = 12;
+ private_stream_coded = 0;
+ for(i=0;i<ctx->nb_streams;i++) {
+ stream = ctx->streams[i]->priv_data;
+ if (stream->id < 0xc0) {
+ if (private_stream_coded)
+ continue;
+ private_stream_coded = 1;
+ }
+ buf_index += 3;
+ }
+ return buf_index;
+}
+
static int mpeg_mux_init(AVFormatContext *ctx)
{
MpegMuxContext *s = ctx->priv_data;
else
s->packet_size = 2048;
- /* startcode(4) + length(2) + flags(1) */
- s->packet_data_max_size = s->packet_size - 7;
- if (s->is_mpeg2)
- s->packet_data_max_size -= 2;
s->audio_bound = 0;
s->video_bound = 0;
mpa_id = AUDIO_ID;
stream->start_pts = AV_NOPTS_VALUE;
stream->start_dts = AV_NOPTS_VALUE;
}
+ s->system_header_size = get_system_header_size(ctx);
s->last_scr = 0;
return 0;
fail:
put_be16(pb, (uint16_t)((((timestamp) & 0x7fff) << 1) | 1));
}
+
+/* return the exact available payload size for the next packet for
+ stream 'stream_index'. 'pts' and 'dts' are only used to know if
+ timestamps are needed in the packet header. */
+static int get_packet_payload_size(AVFormatContext *ctx, int stream_index,
+ int64_t pts, int64_t dts)
+{
+ MpegMuxContext *s = ctx->priv_data;
+ int buf_index;
+ StreamInfo *stream;
+
+ buf_index = 0;
+ if (((s->packet_number % s->pack_header_freq) == 0)) {
+ /* pack header size */
+ if (s->is_mpeg2)
+ buf_index += 14;
+ else
+ buf_index += 12;
+ if ((s->packet_number % s->system_header_freq) == 0)
+ buf_index += s->system_header_size;
+ }
+
+ /* packet header size */
+ buf_index += 6;
+ if (s->is_mpeg2)
+ buf_index += 3;
+ if (pts != AV_NOPTS_VALUE) {
+ if (dts != AV_NOPTS_VALUE)
+ buf_index += 5 + 5;
+ else
+ buf_index += 5;
+ } else {
+ if (!s->is_mpeg2)
+ buf_index++;
+ }
+
+ stream = ctx->streams[stream_index]->priv_data;
+ if (stream->id < 0xc0) {
+ /* AC3 private data header */
+ buf_index += 4;
+ }
+ return s->packet_size - buf_index;
+}
+
/* flush the packet on stream stream_index */
static void flush_packet(AVFormatContext *ctx, int stream_index,
int64_t pts, int64_t dts, int64_t scr)
MpegMuxContext *s = ctx->priv_data;
StreamInfo *stream = ctx->streams[stream_index]->priv_data;
uint8_t *buf_ptr;
- int size, payload_size, startcode, id, len, stuffing_size, i, header_len;
+ int size, payload_size, startcode, id, stuffing_size, i, header_len;
+ int packet_size;
uint8_t buffer[128];
id = stream->id;
header_len++;
}
- payload_size = s->packet_size - (size + 6 + header_len);
+ packet_size = s->packet_size - (size + 6);
+ payload_size = packet_size - header_len;
if (id < 0xc0) {
startcode = PRIVATE_STREAM_1;
payload_size -= 4;
} else {
startcode = 0x100 + id;
}
+
stuffing_size = payload_size - stream->buffer_ptr;
if (stuffing_size < 0)
stuffing_size = 0;
-
put_be32(&ctx->pb, startcode);
- put_be16(&ctx->pb, payload_size + header_len);
+ put_be16(&ctx->pb, packet_size);
/* stuffing */
for(i=0;i<stuffing_size;i++)
put_byte(&ctx->pb, 0xff);
if (startcode == PRIVATE_STREAM_1) {
put_byte(&ctx->pb, id);
if (id >= 0x80 && id <= 0xbf) {
- /* XXX: need to check AC3 spec */
- put_byte(&ctx->pb, 1);
- put_byte(&ctx->pb, 0);
- put_byte(&ctx->pb, 2);
+ put_byte(&ctx->pb, stream->nb_frames);
+ put_be16(&ctx->pb, stream->frame_start_offset);
}
}
put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
put_flush_packet(&ctx->pb);
- /* preserve remaining data */
- len = stream->buffer_ptr - payload_size;
- if (len < 0)
- len = 0;
- memmove(stream->buffer, stream->buffer + stream->buffer_ptr - len, len);
- stream->buffer_ptr = len;
-
s->packet_number++;
stream->packet_number++;
+ stream->nb_frames = 0;
+ stream->frame_start_offset = 0;
}
static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
MpegMuxContext *s = ctx->priv_data;
AVStream *st = ctx->streams[stream_index];
StreamInfo *stream = st->priv_data;
- int64_t dts;
- int len;
+ int64_t dts, new_start_pts, new_start_dts;
+ int len, avail_size;
/* XXX: system clock should be computed precisely, especially for
CBR case. The current mode gives at least something coherent */
dts = AV_NOPTS_VALUE;
/* we assume here that pts != AV_NOPTS_VALUE */
+ new_start_pts = stream->start_pts;
+ new_start_dts = stream->start_dts;
+
if (stream->start_pts == AV_NOPTS_VALUE) {
- stream->start_pts = pts;
- stream->start_dts = dts;
+ new_start_pts = pts;
+ new_start_dts = dts;
+ }
+ avail_size = get_packet_payload_size(ctx, stream_index,
+ new_start_pts,
+ new_start_dts);
+ if (stream->buffer_ptr >= avail_size) {
+ /* unlikely case: outputing the pts or dts increase the packet
+ size so that we cannot write the start of the next
+ packet. In this case, we must flush the current packet with
+ padding */
+ flush_packet(ctx, stream_index,
+ stream->start_pts, stream->start_dts, s->last_scr);
+ stream->buffer_ptr = 0;
}
+ stream->start_pts = new_start_pts;
+ stream->start_dts = new_start_dts;
+ stream->nb_frames++;
+ if (stream->frame_start_offset == 0)
+ stream->frame_start_offset = stream->buffer_ptr;
while (size > 0) {
- len = s->packet_data_max_size - stream->buffer_ptr;
+ avail_size = get_packet_payload_size(ctx, stream_index,
+ stream->start_pts,
+ stream->start_dts);
+ len = avail_size - stream->buffer_ptr;
if (len > size)
len = size;
memcpy(stream->buffer + stream->buffer_ptr, buf, len);
stream->buffer_ptr += len;
buf += len;
size -= len;
- while (stream->buffer_ptr >= s->packet_data_max_size) {
- /* output the packet */
+ if (stream->buffer_ptr >= avail_size) {
+ /* if packet full, we send it now */
flush_packet(ctx, stream_index,
stream->start_pts, stream->start_dts, s->last_scr);
+ stream->buffer_ptr = 0;
/* Make sure only the FIRST pes packet for this frame has
a timestamp */
stream->start_pts = AV_NOPTS_VALUE;
stream->start_dts = AV_NOPTS_VALUE;
}
}
+
return 0;
}
/* flush each packet */
for(i=0;i<ctx->nb_streams;i++) {
stream = ctx->streams[i]->priv_data;
- while (stream->buffer_ptr > 0) {
- flush_packet(ctx, i, AV_NOPTS_VALUE, AV_NOPTS_VALUE, s->last_scr);
+ if (stream->buffer_ptr > 0) {
+ /* NOTE: we can always write the remaining data as it was
+ tested before in mpeg_mux_write_packet() */
+ flush_packet(ctx, i, stream->start_pts, stream->start_dts,
+ s->last_scr);
}
}
}
if (startcode >= 0x1e0 && startcode <= 0x1ef) {
type = CODEC_TYPE_VIDEO;
- codec_id = CODEC_ID_MPEG1VIDEO;
+ codec_id = CODEC_ID_MPEG2VIDEO;
} else if (startcode >= 0x1c0 && startcode <= 0x1df) {
type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_MP2;
"vob",
sizeof(MpegMuxContext),
CODEC_ID_MP2,
- CODEC_ID_MPEG1VIDEO,
+ CODEC_ID_MPEG2VIDEO,
mpeg_mux_init,
mpeg_mux_write_packet,
mpeg_mux_end,