static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 };
#ifdef CONFIG_ENCODERS
-extern AVOutputFormat mpeg1system_mux;
-extern AVOutputFormat mpeg1vcd_mux;
-extern AVOutputFormat mpeg2vob_mux;
-extern AVOutputFormat mpeg2svcd_mux;
+static AVOutputFormat mpeg1system_mux;
+static AVOutputFormat mpeg1vcd_mux;
+static AVOutputFormat mpeg2vob_mux;
+static AVOutputFormat mpeg2svcd_mux;
static int put_pack_header(AVFormatContext *ctx,
uint8_t *buf, int64_t timestamp)
} else
put_bits(&pb, 6, s->audio_bound);
- if (s->is_vcd)
- put_bits(&pb, 1, 0); /* see VCD standard, p. IV-7*/
- else
- put_bits(&pb, 1, 1); /* variable bitrate*/
- put_bits(&pb, 1, 1); /* non constrainted bit stream */
+ if (s->is_vcd) {
+ /* see VCD standard, p. IV-7*/
+ put_bits(&pb, 1, 0);
+ put_bits(&pb, 1, 1);
+ } else {
+ put_bits(&pb, 1, 0); /* variable bitrate*/
+ put_bits(&pb, 1, 0); /* non constrainted bit stream */
+ }
if (s->is_vcd) {
/* see VCD standard p IV-7 */
} else {
stream->id = mpa_id++;
}
+
+ /* This value HAS to be used for VCD (see VCD standard, p. IV-7).
+ Right now it is also used for everything else.*/
stream->max_buffer_size = 4 * 1024;
s->audio_bound++;
break;
if (s->scr_stream_index == -1)
s->scr_stream_index = i;
stream->id = mpv_id++;
- stream->max_buffer_size = 46 * 1024;
+ if (s->is_vcd)
+ /* see VCD standard, p. IV-7*/
+ stream->max_buffer_size = 46 * 1024;
+ else
+ /* This value HAS to be used for SVCD (see SVCD standard, p. 26 V.2.3.2).
+ Right now it is also used for everything else.*/
+ stream->max_buffer_size = 230 * 1024;
s->video_bound++;
break;
default:
}
}
- if (s->is_vcd && stream->packet_number==0)
+ if ((s->is_vcd && stream->packet_number==0)
+ || (s->is_svcd && s->packet_number==0))
/* the first pack of each stream contains only the pack header,
the system header and some padding (see VCD standard p. IV-6)
Add the padding size, so that the actual payload becomes 0.*/
else {
/* packet header size */
buf_index += 6;
- if (s->is_mpeg2)
+ if (s->is_mpeg2) {
buf_index += 3;
+ if (stream->packet_number==0)
+ buf_index += 3; /* PES extension */
+ buf_index += 1; /* obligatory stuffing byte */
+ }
if (pts != AV_NOPTS_VALUE) {
if (dts != pts)
buf_index += 5 + 5;
uint8_t buffer[128];
int zero_trail_bytes = 0;
int pad_packet_bytes = 0;
+ int pes_flags;
+ int general_pack = 0; /*"general" pack without data specific to one stream?*/
id = stream->id;
each audio pack (see standard p. IV-8).*/
zero_trail_bytes += 20;
- if (s->is_vcd && stream->packet_number==0) {
- /* the first pack of each stream contains only the pack header,
+ if ((s->is_vcd && stream->packet_number==0)
+ || (s->is_svcd && s->packet_number==0)) {
+ /* for VCD the first pack of each stream contains only the pack header,
the system header and lots of padding (see VCD standard p. IV-6).
In the case of an audio pack, 20 zero bytes are also added at
the end.*/
+ /* For SVCD we fill the very first pack to increase compatibility with
+ some DVD players. Not mandated by the standard.*/
+ if (s->is_svcd)
+ general_pack = 1; /* the system header refers to both streams and no stream data*/
pad_packet_bytes = packet_size - zero_trail_bytes;
}
/* packet header */
if (s->is_mpeg2) {
header_len = 3;
+ if (stream->packet_number==0)
+ header_len += 3; /* PES extension */
+ header_len += 1; /* obligatory stuffing byte */
} else {
header_len = 0;
}
stuffing_size = payload_size - stream->buffer_ptr;
if (stuffing_size < 0)
stuffing_size = 0;
+ if (stuffing_size > 16) { /*<=16 for MPEG-1, <=32 for MPEG-2*/
+ pad_packet_bytes += stuffing_size;
+ packet_size -= stuffing_size;
+ payload_size -= stuffing_size;
+ stuffing_size = 0;
+ }
+
put_be32(&ctx->pb, startcode);
put_be16(&ctx->pb, packet_size);
if (s->is_mpeg2) {
put_byte(&ctx->pb, 0x80); /* mpeg2 id */
+ pes_flags=0;
+
if (pts != AV_NOPTS_VALUE) {
- if (dts != pts) {
- put_byte(&ctx->pb, 0xc0); /* flags */
- put_byte(&ctx->pb, header_len - 3 + stuffing_size);
- put_timestamp(&ctx->pb, 0x03, pts);
- put_timestamp(&ctx->pb, 0x01, dts);
- } else {
- put_byte(&ctx->pb, 0x80); /* flags */
- put_byte(&ctx->pb, header_len - 3 + stuffing_size);
- put_timestamp(&ctx->pb, 0x02, pts);
- }
- } else {
- put_byte(&ctx->pb, 0x00); /* flags */
- put_byte(&ctx->pb, header_len - 3 + stuffing_size);
+ pes_flags |= 0x80;
+ if (dts != pts)
+ pes_flags |= 0x40;
}
+
+ /* Both the MPEG-2 and the SVCD standards demand that the
+ P-STD_buffer_size field be included in the first packet of
+ every stream. (see SVCD standard p. 26 V.2.3.1 and V.2.3.2
+ and MPEG-2 standard 2.7.7) */
+ if (stream->packet_number == 0)
+ pes_flags |= 0x01;
+
+ put_byte(&ctx->pb, pes_flags); /* flags */
+ put_byte(&ctx->pb, header_len - 3 + stuffing_size);
+
+ if (pes_flags & 0x80) /*write pts*/
+ put_timestamp(&ctx->pb, (pes_flags & 0x40) ? 0x03 : 0x02, pts);
+ if (pes_flags & 0x40) /*write dts*/
+ put_timestamp(&ctx->pb, 0x01, dts);
+
+ if (pes_flags & 0x01) { /*write pes extension*/
+ put_byte(&ctx->pb, 0x10); /* flags */
+
+ /* P-STD buffer info */
+ if (id == AUDIO_ID)
+ put_be16(&ctx->pb, 0x4000 | stream->max_buffer_size/128);
+ else
+ put_be16(&ctx->pb, 0x6000 | stream->max_buffer_size/1024);
+ }
+
} else {
if (pts != AV_NOPTS_VALUE) {
if (dts != pts) {
}
}
+ if (s->is_mpeg2) {
+ /* special stuffing byte that is always written
+ to prevent accidental generation of start codes. */
+ put_byte(&ctx->pb, 0xff);
+
+ for(i=0;i<stuffing_size;i++)
+ put_byte(&ctx->pb, 0xff);
+ }
+
if (startcode == PRIVATE_STREAM_1) {
put_byte(&ctx->pb, id);
if (id >= 0xa0) {
}
}
- if (s->is_mpeg2)
- for(i=0;i<stuffing_size;i++)
- put_byte(&ctx->pb, 0xff);
-
/* output data */
put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
}
put_flush_packet(&ctx->pb);
s->packet_number++;
- stream->packet_number++;
+
+ /* only increase the stream packet number if this pack actually contains
+ something that is specific to this stream! I.e. a dedicated header
+ or some data.*/
+ if (!general_pack)
+ stream->packet_number++;
stream->nb_frames = 0;
stream->frame_start_offset = 0;
}
s->packet_number++;
}
-/* XXX: move that to upper layer */
-/* XXX: we assume that there are always 'max_b_frames' between
- reference frames. A better solution would be to use the AVFrame pts
- field */
-static void compute_pts_dts(AVStream *st, int64_t *ppts, int64_t *pdts,
- int64_t timestamp)
-{
- int frame_delay;
- int64_t pts, dts;
-
- if (st->codec.codec_type == CODEC_TYPE_VIDEO &&
- st->codec.max_b_frames != 0) {
- frame_delay = (st->codec.frame_rate_base * 90000LL) /
- st->codec.frame_rate;
- if (timestamp == 0) {
- /* specific case for first frame : DTS just before */
- pts = timestamp;
- dts = timestamp - frame_delay;
- } else {
- timestamp -= frame_delay;
- if (st->codec.coded_frame->pict_type == FF_B_TYPE) {
- /* B frames has identical pts/dts */
- pts = timestamp;
- dts = timestamp;
- } else {
- /* a reference frame has a pts equal to the dts of the
- _next_ one */
- dts = timestamp;
- pts = timestamp + (st->codec.max_b_frames + 1) * frame_delay;
- }
- }
-#if 1
- printf("pts=%0.3f dts=%0.3f pict_type=%c\n",
- pts / 90000.0, dts / 90000.0,
- av_get_pict_type_char(st->codec.coded_frame->pict_type));
-#endif
- } else {
- pts = timestamp;
- dts = timestamp;
- }
- *ppts = pts & ((1LL << 33) - 1);
- *pdts = dts & ((1LL << 33) - 1);
-}
-
static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts)
{
MpegMuxContext *s = ctx->priv_data;
int64_t scr;
+ StreamInfo *stream;
+ int i;
- if (s->is_vcd)
+ if (s->is_vcd) {
/* Since the data delivery rate is constant, SCR is computed
using the formula C + i * 1200 where C is the start constant
and i is the pack index.
will still be correct according to the standard. It just won't have
the "recommended" value).*/
scr = 36000 + s->packet_number * 1200;
+
+
+#if 0
+ for(i=0;i<ctx->nb_streams;i++) {
+ stream = ctx->streams[i]->priv_data;
+
+ if(scr > stream->start_pts && stream->start_pts!=AV_NOPTS_VALUE) {
+ av_log(ctx, AV_LOG_DEBUG, "mpeg vcd: SCR above PTS (scr=%0.3f, stream index=%d, stream_pts=%0.3f).\n", scr/90000.0, i, stream->start_pts / 90000.0);
+ }
+ }
+#endif
+ }
else {
+
+
/* XXX I believe this calculation of SCR is wrong. SCR
specifies at which time the data should enter the decoder.
Two packs cannot enter the decoder at the same time. */
scr = pts;
else
scr = s->last_scr;
+
+ /* "Sanity hack": make sure that the SCR does not overtake the pts of
+ buffered data that is still waiting to be written.*/
+ for(i=0;i<ctx->nb_streams;i++) {
+ stream = ctx->streams[i]->priv_data;
+
+ if(scr > stream->start_pts && stream->start_pts!=AV_NOPTS_VALUE) {
+ /* av_log(ctx, AV_LOG_DEBUG, "mpeg: restricting scr to stream pts (scr=%0.3f, stream index=%d, stream_pts=%0.3f).\n", scr/90000.0, i, stream->start_pts / 90000.0); */
+ scr = stream->start_pts;
+ }
+ }
}
s->last_scr=scr;
}
-static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
- const uint8_t *buf, int size,
- int64_t timestamp)
+static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt)
{
MpegMuxContext *s = ctx->priv_data;
+ int stream_index= pkt->stream_index;
+ int size= pkt->size;
+ uint8_t *buf= pkt->data;
AVStream *st = ctx->streams[stream_index];
StreamInfo *stream = st->priv_data;
int64_t pts, dts, new_start_pts, new_start_dts;
int len, avail_size;
- compute_pts_dts(st, &pts, &dts, timestamp);
-
+ pts= pkt->pts;
+ dts= pkt->dts;
+
+ if(s->is_svcd) {
+ /* offset pts and dts slightly into the future to be able
+ to do the compatibility fix below.*/
+ pts = (pts + 2) & ((1LL << 33) - 1);
+ dts = (dts + 2) & ((1LL << 33) - 1);
+
+ if (stream->packet_number == 0 && dts == pts)
+ /* For the very first packet we want to force the DTS to be included.
+ This increases compatibility with lots of DVD players.
+ Since the MPEG-2 standard mandates that DTS is only written when
+ it is different from PTS we have to move it slightly into the past.*/
+ dts = (dts - 2) & ((1LL << 33) - 1);
+ }
+ if(s->is_vcd) {
+ /* We have to offset the PTS, so that it is consistent with the SCR.
+ SCR starts at 36000, but the first two packs contain only padding
+ and the first pack from the other stream, respectively, may also have
+ been written before.
+ So the real data starts at SCR 36000+3*1200. */
+ pts = (pts + 36000 + 3600) & ((1LL << 33) - 1);
+ dts = (dts + 36000 + 3600) & ((1LL << 33) - 1);
+ }
#if 0
update_scr(ctx,stream_index,pts);
stream->start_dts = new_start_dts;
stream->nb_frames++;
if (stream->frame_start_offset == 0)
- stream->frame_start_offset = stream->buffer_ptr;
+ stream->frame_start_offset = stream->buffer_ptr + 1;
while (size > 0) {
avail_size = get_packet_payload_size(ctx, stream_index,
stream->start_pts,
return start_code;
}
-/* read the next (or previous) PES header. Return its position in ppos
+/* read the next PES header. Return its position in ppos
(if not NULL), and its start code, pts and dts.
*/
static int mpegps_read_pes_header(AVFormatContext *s,
int64_t *ppos, int *pstart_code,
- int64_t *ppts, int64_t *pdts, int find_next)
+ int64_t *ppts, int64_t *pdts)
{
MpegDemuxContext *m = s->priv_data;
int len, size, startcode, c, flags, header_len;
last_pos = -1;
redo:
- if (find_next) {
/* next start code (should be immediately after) */
m->header_state = 0xff;
size = MAX_SYNC_SIZE;
startcode = find_next_start_code(&s->pb, &size, &m->header_state);
- } else {
- if (last_pos >= 0)
- url_fseek(&s->pb, last_pos, SEEK_SET);
- size = MAX_SYNC_SIZE;
- startcode = find_prev_start_code(&s->pb, &size);
- last_pos = url_ftell(&s->pb) - 4;
- }
//printf("startcode=%x pos=0x%Lx\n", startcode, url_ftell(&s->pb));
if (startcode < 0)
- return -EIO;
+ return AVERROR_IO;
if (startcode == PACK_START_CODE)
goto redo;
if (startcode == SYSTEM_HEADER_START_CODE)
header_len--;
}
}
+ else if( c!= 0xf )
+ goto redo;
+
if (startcode == 0x1bd) {
if (len < 1)
goto redo;
AVPacket *pkt)
{
AVStream *st;
- int len, startcode, i, type, codec_id;
+ int len, startcode, i, type, codec_id = 0;
int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work
redo:
- len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts, 1);
+ len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts);
if (len < 0)
return len;
pkt->dts = dts;
pkt->stream_index = st->index;
#if 0
- printf("%d: pts=%0.3f dts=%0.3f\n",
+ av_log(s, AV_LOG_DEBUG, "%d: pts=%0.3f dts=%0.3f\n",
pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0);
#endif
+
return 0;
}
}
static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index,
- int64_t *ppos, int find_next)
+ int64_t *ppos, int64_t pos_limit)
{
int len, startcode;
int64_t pos, pts, dts;
#endif
url_fseek(&s->pb, pos, SEEK_SET);
for(;;) {
- len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts, find_next);
+ len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts);
if (len < 0) {
#ifdef DEBUG_SEEK
printf("none (ret=%d)\n", len);
dts != AV_NOPTS_VALUE) {
break;
}
- if (find_next) {
- url_fskip(&s->pb, len);
- } else {
- url_fseek(&s->pb, pos, SEEK_SET);
- }
+ url_fskip(&s->pb, len);
}
#ifdef DEBUG_SEEK
printf("pos=0x%llx dts=0x%llx %0.3f\n", pos, dts, dts / 90000.0);
return dts;
}
-static int mpegps_read_seek(AVFormatContext *s,
- int stream_index, int64_t timestamp)
-{
- int64_t pos_min, pos_max, pos, pos_limit;
- int64_t dts_min, dts_max, dts;
- int index, no_change;
- AVStream *st;
-
- timestamp = (timestamp * 90000) / AV_TIME_BASE;
-
-#ifdef DEBUG_SEEK
- printf("read_seek: %d %0.3f\n", stream_index, timestamp / 90000.0);
-#endif
-
- /* XXX: find stream_index by looking at the first PES packet found */
- if (stream_index < 0) {
- stream_index = av_find_default_stream_index(s);
- if (stream_index < 0)
- return -1;
- }
-
- dts_max=
- dts_min= AV_NOPTS_VALUE;
- pos_limit= -1; //gcc falsely says it may be uninitalized
-
- st= s->streams[stream_index];
- if(st->index_entries){
- AVIndexEntry *e;
-
- index= av_index_search_timestamp(st, timestamp);
- e= &st->index_entries[index];
- if(e->timestamp <= timestamp){
- pos_min= e->pos;
- dts_min= e->timestamp;
-#ifdef DEBUG_SEEK
- printf("unsing cached pos_min=0x%llx dts_min=%0.3f\n",
- pos_min,dts_min / 90000.0);
-#endif
- }else{
- assert(index==0);
- }
- index++;
- if(index < st->nb_index_entries){
- e= &st->index_entries[index];
- assert(e->timestamp >= timestamp);
- pos_max= e->pos;
- dts_max= e->timestamp;
- pos_limit= pos_max - e->min_distance;
-#ifdef DEBUG_SEEK
- printf("unsing cached pos_max=0x%llx dts_max=%0.3f\n",
- pos_max,dts_max / 90000.0);
-#endif
- }
- }
-
- if(dts_min == AV_NOPTS_VALUE){
- pos_min = 0;
- dts_min = mpegps_read_dts(s, stream_index, &pos_min, 1);
- if (dts_min == AV_NOPTS_VALUE) {
- /* we can reach this case only if no PTS are present in
- the whole stream */
- return -1;
- }
- }
- if(dts_max == AV_NOPTS_VALUE){
- pos_max = url_filesize(url_fileno(&s->pb)) - 1;
- dts_max = mpegps_read_dts(s, stream_index, &pos_max, 0);
- pos_limit= pos_max;
- }
-
- no_change=0;
- while (pos_min < pos_limit) {
-#ifdef DEBUG_SEEK
- printf("pos_min=0x%llx pos_max=0x%llx dts_min=%0.3f dts_max=%0.3f\n",
- pos_min, pos_max,
- dts_min / 90000.0, dts_max / 90000.0);
-#endif
- int64_t start_pos;
- assert(pos_limit <= pos_max);
-
- if(no_change==0){
- int64_t approximate_keyframe_distance= pos_max - pos_limit;
- // interpolate position (better than dichotomy)
- pos = (int64_t)((double)(pos_max - pos_min) *
- (double)(timestamp - dts_min) /
- (double)(dts_max - dts_min)) + pos_min - approximate_keyframe_distance;
- }else if(no_change==1){
- // bisection, if interpolation failed to change min or max pos last time
- pos = (pos_min + pos_limit)>>1;
- }else{
- // linear search if bisection failed, can only happen if there are very few or no keframes between min/max
- pos=pos_min;
- }
- if(pos <= pos_min)
- pos= pos_min + 1;
- else if(pos > pos_limit)
- pos= pos_limit;
- start_pos= pos;
-
- // read the next timestamp
- dts = mpegps_read_dts(s, stream_index, &pos, 1);
- if(pos == pos_max)
- no_change++;
- else
- no_change=0;
-#ifdef DEBUG_SEEK
-printf("%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, dts_min, dts, dts_max, timestamp, pos_limit, start_pos, no_change);
-#endif
- assert(dts != AV_NOPTS_VALUE);
- if (timestamp < dts) {
- pos_limit = start_pos - 1;
- pos_max = pos;
- dts_max = dts;
- } else {
- pos_min = pos;
- dts_min = dts;
- /* check if we are lucky */
- if (timestamp == dts)
- break;
- }
- }
-
- pos = pos_min;
-#ifdef DEBUG_SEEK
- pos_min = pos;
- dts_min = mpegps_read_dts(s, stream_index, &pos_min, 1);
- pos_min++;
- dts_max = mpegps_read_dts(s, stream_index, &pos_min, 1);
- printf("pos=0x%llx %0.3f<=%0.3f<=%0.3f\n",
- pos, dts_min / 90000.0, timestamp / 90000.0, dts_max / 90000.0);
-#endif
- /* do the seek */
- url_fseek(&s->pb, pos, SEEK_SET);
- return 0;
-}
-
#ifdef CONFIG_ENCODERS
static AVOutputFormat mpeg1system_mux = {
"mpeg",
mpegps_read_header,
mpegps_read_packet,
mpegps_read_close,
- mpegps_read_seek,
+ NULL, //mpegps_read_seek,
+ mpegps_read_dts,
};
int mpegps_init(void)