summaryrefslogtreecommitdiffstats
path: root/ext/mplex/multplex.cc
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@gmail.com>2002-10-24 22:37:51 +0000
committerWim Taymans <wim.taymans@gmail.com>2002-10-24 22:37:51 +0000
commit1496394c0f4be1a718fb189846c6350cbd9e43da (patch)
tree7f839063fd206ad3db102a7a20c336d0c8423295 /ext/mplex/multplex.cc
parent440801dd9e53284925575c2170b53bf947522414 (diff)
downloadgst-plugins-bad-1496394c0f4be1a718fb189846c6350cbd9e43da.tar.gz
gst-plugins-bad-1496394c0f4be1a718fb189846c6350cbd9e43da.tar.bz2
gst-plugins-bad-1496394c0f4be1a718fb189846c6350cbd9e43da.zip
First stab at porting mplex
Original commit message from CVS: First stab at porting mplex
Diffstat (limited to 'ext/mplex/multplex.cc')
-rw-r--r--ext/mplex/multplex.cc1120
1 files changed, 1120 insertions, 0 deletions
diff --git a/ext/mplex/multplex.cc b/ext/mplex/multplex.cc
new file mode 100644
index 00000000..5a1d4b1b
--- /dev/null
+++ b/ext/mplex/multplex.cc
@@ -0,0 +1,1120 @@
+
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <mjpeg_types.h>
+#include <mjpeg_logging.h>
+#include <format_codes.h>
+
+#include "videostrm.hh"
+#include "outputstream.hh"
+
+
+/*******************************************************************
+ Find the timecode corresponding to given position in the system stream
+ (assuming the SCR starts at 0 at the beginning of the stream
+@param bytepos byte position in the stream
+@param ts returns the number of clockticks the bytepos is from the file start
+****************************************************************** */
+
+void
+OutputStream::ByteposTimecode (bitcount_t bytepos, clockticks & ts)
+{
+ ts = (bytepos * CLOCKS) / static_cast < bitcount_t > (dmux_rate);
+}
+
+
+/**********
+ *
+ * UpdateSectorHeaders - Update the sector headers after a change in stream
+ * position / SCR
+ **********
+
+ **********
+ *
+ * NextPosAndSCR - Update nominal (may be >= actual) byte count
+ * and SCR to next output sector.
+ *
+ ********/
+
+void
+OutputStream::NextPosAndSCR ()
+{
+ bytes_output += sector_transport_size;
+ ByteposTimecode (bytes_output, current_SCR);
+ if (start_of_new_pack) {
+ psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+ pack_header_ptr = &pack_header;
+ if (include_sys_header)
+ sys_header_ptr = &sys_header;
+ else
+ sys_header_ptr = NULL;
+
+ } else
+ pack_header_ptr = NULL;
+}
+
+
+/**********
+ *
+ * NextPosAndSCR - Update nominal (may be >= actual) byte count
+ * and SCR to next output sector.
+ * @param bytepos byte position in the stream
+ ********/
+
+void
+OutputStream::SetPosAndSCR (bitcount_t bytepos)
+{
+ bytes_output = bytepos;
+ ByteposTimecode (bytes_output, current_SCR);
+ if (start_of_new_pack) {
+ psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+ pack_header_ptr = &pack_header;
+ if (include_sys_header)
+ sys_header_ptr = &sys_header;
+ else
+ sys_header_ptr = NULL;
+
+ } else
+ pack_header_ptr = NULL;
+}
+
+/*
+ Stream syntax parameters.
+*/
+
+
+/******************************************************************
+
+ Initialisation of stream syntax paramters based on selected
+ user options.
+******************************************************************/
+
+
+// TODO: this mixes class member parameters with opt_ globals...
+
+void
+OutputStream::InitSyntaxParameters ()
+{
+ video_buffer_size = 0;
+ seg_starts_with_video = false;
+ audio_buffer_size = 4 * 1024;
+
+ switch (opt_mux_format) {
+ case MPEG_FORMAT_VCD:
+ opt_data_rate = 75 * 2352; /* 75 raw CD sectors/sec */
+ video_buffer_size = 46 * 1024;
+ opt_VBR = 0;
+
+ case MPEG_FORMAT_VCD_NSR: /* VCD format, non-standard rate */
+ mjpeg_info ("Selecting VCD output profile");
+ if (video_buffer_size == 0)
+ video_buffer_size = opt_buffer_size * 1024;
+ vbr = opt_VBR;
+ opt_mpeg = 1;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = 0;
+ always_sys_header_in_pack = 0;
+ sector_transport_size = 2352; /* Each 2352 bytes with 2324 bytes payload */
+ transport_prefix_sectors = 30;
+ sector_size = 2324;
+ buffers_in_video = 1;
+ always_buffers_in_video = 0;
+ buffers_in_audio = 1; // This is needed as otherwise we have
+ always_buffers_in_audio = 1; // to stuff the packer header which
+ // must be 13 bytes for VCD audio
+ vcd_zero_stuffing = 20; // The famous 20 zero bytes for VCD
+ // audio sectors.
+ dtspts_for_all_vau = false;
+ sector_align_iframeAUs = false;
+ timestamp_iframe_only = false;
+ seg_starts_with_video = true;
+ break;
+
+ case MPEG_FORMAT_MPEG2:
+ mjpeg_info ("Selecting generic MPEG2 output profile");
+ opt_mpeg = 2;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = 1;
+ always_sys_header_in_pack = 0;
+ sector_transport_size = 2048; /* Each 2352 bytes with 2324 bytes payload */
+ transport_prefix_sectors = 0;
+ sector_size = 2048;
+ video_buffer_size = 234 * 1024;
+ buffers_in_video = 1;
+ always_buffers_in_video = 0;
+ buffers_in_audio = 1;
+ always_buffers_in_audio = 1;
+ vcd_zero_stuffing = 0;
+ dtspts_for_all_vau = 0;
+ sector_align_iframeAUs = false;
+ timestamp_iframe_only = false;
+ video_buffers_iframe_only = false;
+ vbr = opt_VBR;
+ break;
+
+ case MPEG_FORMAT_SVCD:
+ opt_data_rate = 150 * 2324;
+ video_buffer_size = 230 * 1024;
+
+ case MPEG_FORMAT_SVCD_NSR: /* Non-standard data-rate */
+ mjpeg_info ("Selecting SVCD output profile");
+ if (video_buffer_size == 0)
+ video_buffer_size = opt_buffer_size * 1024;
+ opt_mpeg = 2;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = 0;
+ always_sys_header_in_pack = 0;
+ sector_transport_size = 2324;
+ transport_prefix_sectors = 0;
+ sector_size = 2324;
+ vbr = true;
+ buffers_in_video = 1;
+ always_buffers_in_video = 0;
+ buffers_in_audio = 1;
+ always_buffers_in_audio = 0;
+ vcd_zero_stuffing = 0;
+ dtspts_for_all_vau = 0;
+ sector_align_iframeAUs = true;
+ seg_starts_with_video = true;
+ timestamp_iframe_only = false;
+ video_buffers_iframe_only = false;
+ break;
+
+ case MPEG_FORMAT_VCD_STILL:
+ opt_data_rate = 75 * 2352; /* 75 raw CD sectors/sec */
+ vbr = false;
+ opt_mpeg = 1;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = 0;
+ always_sys_header_in_pack = 0;
+ sector_transport_size = 2352; /* Each 2352 bytes with 2324 bytes payload */
+ transport_prefix_sectors = 0;
+ sector_size = 2324;
+ buffers_in_video = 1;
+ always_buffers_in_video = 0;
+ buffers_in_audio = 1;
+ always_buffers_in_audio = 0;
+ vcd_zero_stuffing = 20;
+ dtspts_for_all_vau = 1;
+ sector_align_iframeAUs = true;
+ timestamp_iframe_only = false;
+ video_buffers_iframe_only = false;
+ if (opt_buffer_size == 0)
+ opt_buffer_size = 46;
+ else if (opt_buffer_size > 220) {
+ mjpeg_error_exit1 ("VCD stills has max. permissible video buffer size of 220KB");
+ } else {
+ /* Add a margin for sequence header overheads for HR stills */
+ /* So the user simply specifies the nominal size... */
+ opt_buffer_size += 4;
+ }
+ video_buffer_size = opt_buffer_size * 1024;
+ break;
+
+ case MPEG_FORMAT_SVCD_STILL:
+ mjpeg_info ("Selecting SVCD output profile");
+ if (opt_data_rate == 0)
+ opt_data_rate = 150 * 2324;
+ video_buffer_size = 230 * 1024;
+ opt_mpeg = 2;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = 0;
+ always_sys_header_in_pack = 0;
+ sector_transport_size = 2324;
+ transport_prefix_sectors = 0;
+ sector_size = 2324;
+ vbr = true;
+ buffers_in_video = 1;
+ always_buffers_in_video = 0;
+ buffers_in_audio = 1;
+ always_buffers_in_audio = 0;
+ vcd_zero_stuffing = 0;
+ dtspts_for_all_vau = 0;
+ sector_align_iframeAUs = true;
+ timestamp_iframe_only = false;
+ video_buffers_iframe_only = false;
+ break;
+
+ case MPEG_FORMAT_DVD:
+ mjpeg_info ("Selecting DVD output profile (INCOMPLETE!!!!)");
+ opt_data_rate = 1260000;
+ opt_mpeg = 2;
+ packets_per_pack = 1;
+ sys_header_in_pack1 = false; // Handle by control packets
+ always_sys_header_in_pack = false;
+ sector_transport_size = 2048;
+ transport_prefix_sectors = 0;
+ sector_size = 2048;
+ video_buffer_size = 232 * 1024;
+ buffers_in_video = true;
+ always_buffers_in_video = false;
+ buffers_in_audio = true;
+ always_buffers_in_audio = false;
+ vcd_zero_stuffing = 0;
+ dtspts_for_all_vau = 0;
+ sector_align_iframeAUs = true;
+ timestamp_iframe_only = true;
+ video_buffers_iframe_only = true;
+ vbr = true;
+ if (opt_max_segment_size == 0)
+ opt_max_segment_size = 2000 * 1024 * 1024;
+ break;
+
+ default: /* MPEG_FORMAT_MPEG1 - auto format MPEG1 */
+ mjpeg_info ("Selecting generic MPEG1 output profile");
+ opt_mpeg = 1;
+ vbr = opt_VBR;
+ packets_per_pack = opt_packets_per_pack;
+ always_sys_header_in_pack = opt_always_system_headers;
+ sys_header_in_pack1 = 1;
+ sector_transport_size = opt_sector_size;
+ transport_prefix_sectors = 0;
+ sector_size = opt_sector_size;
+ if (opt_buffer_size == 0) {
+ opt_buffer_size = 46;
+ }
+ video_buffer_size = opt_buffer_size * 1024;
+ buffers_in_video = 1;
+ always_buffers_in_video = 1;
+ buffers_in_audio = 0;
+ always_buffers_in_audio = 1;
+ vcd_zero_stuffing = 0;
+ dtspts_for_all_vau = 0;
+ sector_align_iframeAUs = false;
+ timestamp_iframe_only = false;
+ video_buffers_iframe_only = false;
+ break;
+ }
+
+}
+
+/**
+ * Compute the number of run-in sectors needed to fill up the buffers to
+ * suit the type of stream being muxed.
+ *
+ * For stills we have to ensure an entire buffer is loaded as we only
+ * ever process one frame at a time.
+ * @returns the number of run-in sectors needed to fill up the buffers to suit the type of stream being muxed.
+ */
+
+unsigned int
+OutputStream::RunInSectors ()
+{
+ vector < ElementaryStream * >::iterator str;
+ unsigned int sectors_delay = 1;
+
+ for (str = vstreams.begin (); str < vstreams.end (); ++str) {
+
+ if (MPEG_STILLS_FORMAT (opt_mux_format)) {
+ sectors_delay += (unsigned int) (1.02 * (*str)->BufferSize ()) / sector_size + 2;
+ } else if (vbr)
+ sectors_delay += 3 * (*str)->BufferSize () / (4 * sector_size);
+ else
+ sectors_delay += 5 * (*str)->BufferSize () / (6 * sector_size);
+ }
+ sectors_delay += astreams.size ();
+ return sectors_delay;
+}
+
+/**
+ Initializes the output stream. Traverses the input files and calculates their payloads.
+ Estimates the multiplex rate. Estimates the neccessary stream delay for the different substreams.
+ */
+
+
+void
+OutputStream::Init (vector < ElementaryStream * >*strms, PS_Stream *strm)
+{
+ vector < ElementaryStream * >::iterator str;
+ clockticks delay;
+ unsigned int sectors_delay;
+
+ Pack_struc dummy_pack;
+ Sys_header_struc dummy_sys_header;
+ Sys_header_struc *sys_hdr;
+ unsigned int nominal_rate_sum;
+
+ packets_left_in_pack = 0; /* Suppress warning */
+ video_first = false;
+
+ estreams = strms;
+
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ switch ((*str)->Kind ()) {
+ case ElementaryStream::audio:
+ astreams.push_back (*str);
+ break;
+ case ElementaryStream::video:
+ vstreams.push_back (*str);
+ break;
+ default:
+ break;
+ }
+
+ completed.push_back (false);
+ }
+
+ mjpeg_info ("SYSTEMS/PROGRAM stream:");
+ this->psstrm = strm;
+
+ psstrm->Init (opt_mpeg, sector_size, opt_max_segment_size);
+
+ /* These are used to make (conservative) decisions
+ about whether a packet should fit into the recieve buffers...
+ Audio packets always have PTS fields, video packets needn'.
+ TODO: Really this should be encapsulated in Elementary stream...?
+ */
+ psstrm->CreatePack (&dummy_pack, 0, mux_rate);
+ if (always_sys_header_in_pack) {
+ vector < MuxStream * >muxstreams;
+ AppendMuxStreamsOf (*estreams, muxstreams);
+ psstrm->CreateSysHeader (&dummy_sys_header, mux_rate, !vbr, 1, true, true, muxstreams);
+ sys_hdr = &dummy_sys_header;
+ } else
+ sys_hdr = NULL;
+
+ nominal_rate_sum = 0;
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ switch ((*str)->Kind ()) {
+ case ElementaryStream::audio:
+ (*str)->SetMaxPacketData (psstrm->PacketPayload (**str, NULL, NULL,
+ false, true, false));
+ (*str)->SetMinPacketData (psstrm->PacketPayload (**str, sys_hdr, &dummy_pack,
+ always_buffers_in_audio, true, false));
+
+ break;
+ case ElementaryStream::video:
+ (*str)->SetMaxPacketData (psstrm->PacketPayload (**str, NULL, NULL,
+ false, false, false));
+ (*str)->SetMinPacketData (psstrm->PacketPayload (**str, sys_hdr, &dummy_pack,
+ always_buffers_in_video, true, true));
+ break;
+ default:
+ mjpeg_error_exit1 ("INTERNAL: Only audio and video payload calculations implemented!");
+
+ }
+
+ if ((*str)->NominalBitRate () == 0 && opt_data_rate == 0)
+ mjpeg_error_exit1
+ ("Variable bit-rate stream present: output stream (max) data-rate *must* be specified!");
+ nominal_rate_sum += (*str)->NominalBitRate ();
+
+ }
+
+ /* Attempt to guess a sensible mux rate for the given video and *
+ audio estreams-> This is a rough and ready guess for MPEG-1 like
+ formats. */
+
+
+ dmux_rate = static_cast < int >(1.015 * nominal_rate_sum);
+
+ dmux_rate = (dmux_rate / 50 + 25) * 50;
+
+ mjpeg_info ("rough-guess multiplexed stream data rate : %07d", dmux_rate * 8);
+ if (opt_data_rate != 0)
+ mjpeg_info ("target data-rate specified : %7d", opt_data_rate * 8);
+
+ if (opt_data_rate == 0) {
+ mjpeg_info ("Setting best-guess data rate.");
+ } else if (opt_data_rate >= dmux_rate) {
+ mjpeg_info ("Setting specified specified data rate: %7d", opt_data_rate * 8);
+ dmux_rate = opt_data_rate;
+ } else if (opt_data_rate < dmux_rate) {
+ mjpeg_warn ("Target data rate lower than computed requirement!");
+ mjpeg_warn ("N.b. a 20%% or so discrepancy in variable bit-rate");
+ mjpeg_warn ("streams is common and harmless provided no time-outs will occur");
+ dmux_rate = opt_data_rate;
+ }
+
+ mux_rate = dmux_rate / 50;
+
+ /* To avoid Buffer underflow, the DTS of the first video and audio AU's
+ must be offset sufficiently forward of the SCR to allow the buffer
+ time to fill before decoding starts. Calculate the necessary delays...
+ */
+
+ sectors_delay = RunInSectors ();
+
+ ByteposTimecode (static_cast < bitcount_t > (sectors_delay * sector_transport_size), delay);
+
+
+ video_delay = delay + static_cast < clockticks > (opt_video_offset * CLOCKS / 1000);
+ audio_delay = delay + static_cast < clockticks > (opt_audio_offset * CLOCKS / 1000);
+ mjpeg_info ("Sectors = %d Video delay = %lld Audio delay = %lld",
+ sectors_delay, video_delay / 300, audio_delay / 300);
+
+
+ //
+ // Now that all mux parameters are set we can trigger parsing
+ // of actual input stream data and calculation of associated
+ // PTS/DTS by causing the read of the first AU's...
+ //
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ (*str)->NextAU ();
+ }
+
+
+ //
+ // Now that we have both output and input streams initialised and
+ // data-rates set we can make a decent job of setting the maximum
+ // STD buffer delay in video streams.
+ //
+
+ for (str = vstreams.begin (); str < vstreams.end (); ++str) {
+ static_cast < VideoStream * >(*str)->SetMaxStdBufferDelay (dmux_rate);
+ }
+
+
+
+ /* Let's try to read in unit after unit and to write it out into
+ the outputstream. The only difficulty herein lies into the
+ buffer management, and into the fact the the actual access
+ unit *has* to arrive in time, that means the whole unit
+ (better yet, packet data), has to arrive before arrival of
+ DTS. If both buffers are full we'll generate a padding packet
+
+ Of course, when we start we're starting a new segment with no
+ bytes output...
+ */
+
+ ByteposTimecode (sector_transport_size, ticks_per_sector);
+ seg_state = start_segment;
+ running_out = false;
+}
+
+/**
+ Prints the current status of the substreams.
+ @param level the desired log level
+ */
+void
+OutputStream::MuxStatus (log_level_t level)
+{
+ vector < ElementaryStream * >::iterator str;
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ switch ((*str)->Kind ()) {
+ case ElementaryStream::video:
+ mjpeg_log (level,
+ "Video %02x: buf=%7d frame=%06d sector=%08d",
+ (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->au->dorder, (*str)->nsec);
+ break;
+ case ElementaryStream::audio:
+ mjpeg_log (level,
+ "Audio %02x: buf=%7d frame=%06d sector=%08d",
+ (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->au->dorder, (*str)->nsec);
+ break;
+ default:
+ mjpeg_log (level,
+ "Other %02x: buf=%7d sector=%08d",
+ (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->nsec);
+ break;
+ }
+ }
+ if (!vbr)
+ mjpeg_log (level, "Padding : sector=%08d", pstrm.nsec);
+
+
+}
+
+
+/**
+ Append input substreams to the output multiplex stream.
+ */
+void
+OutputStream::AppendMuxStreamsOf (vector < ElementaryStream * >&elem, vector < MuxStream * >&mux)
+{
+ vector < ElementaryStream * >::iterator str;
+ for (str = elem.begin (); str < elem.end (); ++str) {
+ mux.push_back (static_cast < MuxStream * >(*str));
+ }
+}
+
+/******************************************************************
+ Program start-up packets. Generate any irregular packets
+needed at the start of the stream...
+ Note: *must* leave a sensible in-stream system header in
+ sys_header.
+ TODO: get rid of this grotty sys_header global.
+******************************************************************/
+void
+OutputStream::OutputPrefix ()
+{
+ vector < MuxStream * >vmux, amux, emux;
+ AppendMuxStreamsOf (vstreams, vmux);
+ AppendMuxStreamsOf (astreams, amux);
+ AppendMuxStreamsOf (*estreams, emux);
+
+ /* Deal with transport padding */
+ SetPosAndSCR (bytes_output + transport_prefix_sectors * sector_transport_size);
+
+ /* VCD: Two padding packets with video and audio system headers */
+ split_at_seq_end = !opt_multifile_segment;
+
+ switch (opt_mux_format) {
+ case MPEG_FORMAT_VCD:
+ case MPEG_FORMAT_VCD_NSR:
+
+ /* Annoyingly VCD generates seperate system headers for
+ audio and video ... DOH... */
+ if (astreams.size () > 1 || vstreams.size () > 1 ||
+ astreams.size () + vstreams.size () != estreams->size ()) {
+ mjpeg_error_exit1 ("VCD man only have max. 1 audio and 1 video stream");
+ }
+ /* First packet carries video-info-only sys_header */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, vmux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ OutputPadding (false);
+
+ /* Second packet carries audio-info-only sys_header */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, amux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ OutputPadding (true);
+ break;
+
+ case MPEG_FORMAT_SVCD:
+ case MPEG_FORMAT_SVCD_NSR:
+ /* First packet carries sys_header */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, true, true, true, emux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ OutputPadding (false);
+ break;
+
+ case MPEG_FORMAT_VCD_STILL:
+ split_at_seq_end = false;
+ /* First packet carries small-still sys_header */
+ /* TODO No support mixed-mode stills sequences... */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, false, false, true, true, emux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ OutputPadding (false);
+ break;
+
+ case MPEG_FORMAT_SVCD_STILL:
+ /* TODO: Video only at present */
+ /* First packet carries video-info-only sys_header */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, vmux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ OutputPadding (false);
+ break;
+
+ case MPEG_FORMAT_DVD:
+ /* A DVD System header is a weird thing. We seem to need to
+ include buffer info about streams 0xb8, 0xb9, 0xbf even if
+ they're not physically present but the buffers for the actual
+ video streams aren't included.
+
+ TODO: I have no idead about MPEG audio streams if present...
+ */
+ {
+ DummyMuxStream dvd_0xb9_strm_dummy (0xb9, 1, video_buffer_size);
+ DummyMuxStream dvd_0xb8_strm_dummy (0xb8, 0, 4096);
+ DummyMuxStream dvd_0xbf_strm_dummy (0xbf, 1, 2048);
+
+ vector < MuxStream * >dvdmux;
+ vector < MuxStream * >::iterator muxstr;
+ dvdmux.push_back (&dvd_0xb9_strm_dummy);
+ dvdmux.push_back (&dvd_0xb8_strm_dummy);
+ unsigned int max_priv1_buffer = 0;
+
+ for (muxstr = amux.begin (); muxstr < amux.end (); ++muxstr) {
+ // We mux *many* substreams on PRIVATE_STR_1
+ // we set the system header buffer size to the maximum
+ // of all those we find
+ if ((*muxstr)->stream_id == PRIVATE_STR_1 && (*muxstr)->BufferSize () > max_priv1_buffer) {
+ max_priv1_buffer = (*muxstr)->BufferSize ();
+ } else
+ dvdmux.push_back (*muxstr);
+ }
+
+ DummyMuxStream dvd_priv1_strm_dummy (PRIVATE_STR_1, 1, max_priv1_buffer);
+
+ if (max_priv1_buffer > 0)
+ dvdmux.push_back (&dvd_priv1_strm_dummy);
+
+ dvdmux.push_back (&dvd_0xbf_strm_dummy);
+ psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, false, true, true, dvdmux);
+ sys_header_ptr = &sys_header;
+ pack_header_ptr = &pack_header;
+ /* It is then followed up by a pair of PRIVATE_STR_2 packets which
+ we keep empty 'cos we don't know what goes there...
+ */
+ }
+ break;
+
+ default:
+ /* Create the in-stream header in case it is needed */
+ psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, false, true, true, emux);
+ }
+}
+
+/******************************************************************
+ Program shutdown packets. Generate any irregular packets
+ needed at the end of the stream...
+
+******************************************************************/
+
+void
+OutputStream::OutputSuffix ()
+{
+ psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+ psstrm->CreateSector (&pack_header, NULL, 0, pstrm, false, true, 0, 0, TIMESTAMPBITS_NO);
+}
+
+/******************************************************************
+
+ Main multiplex iteration.
+ Opens and closes all needed files and manages the correct
+ call od the respective Video- and Audio- packet routines.
+ The basic multiplexing is done here. Buffer capacity and
+ Timestamp checking is also done here, decision is taken
+ wether we should genereate a Video-, Audio- or Padding-
+ packet.
+******************************************************************/
+
+
+
+bool
+OutputStream::OutputMultiplex ()
+{
+ for (;;) {
+ bool completion = true;
+
+ for (str = estreams->begin (); str < estreams->end (); ++str)
+ completion &= (*str)->MuxCompleted ();
+ if (completion)
+ break;
+
+ /* A little state-machine for handling the transition from one
+ segment to the next
+ */
+ bool runout_incomplete;
+ VideoStream *master;
+
+ switch (seg_state) {
+
+ /* Audio and slave video access units at end of segment.
+ If there are any audio AU's whose PTS implies they
+ should be played *before* the video AU starting the
+ next segement is presented we mux them out. Once
+ they're gone we've finished this segment so we write
+ the suffix switch file, and start muxing a new segment.
+ */
+ case runout_segment:
+ runout_incomplete = false;
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ runout_incomplete |= !(*str)->RunOutComplete ();
+ }
+
+ if (runout_incomplete)
+ break;
+
+ /* Otherwise we write the stream suffix and start a new
+ stream file */
+ OutputSuffix ();
+ psstrm->NextFile ();
+
+ running_out = false;
+ seg_state = start_segment;
+
+ /* Starting a new segment.
+ We send the segment prefix, video and audio reciever
+ buffers are assumed to start empty. We reset the segment
+ length count and hence the SCR.
+
+ */
+
+ case start_segment:
+ mjpeg_info ("New sequence commences...");
+ SetPosAndSCR (0);
+ MuxStatus (LOG_INFO);
+
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ (*str)->AllDemuxed ();
+ }
+
+ packets_left_in_pack = packets_per_pack;
+ start_of_new_pack = true;
+ include_sys_header = sys_header_in_pack1;
+ buffers_in_video = always_buffers_in_video;
+ video_first = seg_starts_with_video & (vstreams.size () > 0);
+ OutputPrefix ();
+
+ /* Set the offset applied to the raw PTS/DTS of AU's to
+ make the DTS of the first AU in the master (video) stream
+ precisely the video delay plus whatever time we wasted in
+ the sequence pre-amble.
+
+ The DTS of the remaining streams are set so that
+ (modulo the relevant delay offset) they maintain the
+ same relative timing to the master stream.
+
+ */
+
+ clockticks ZeroSCR;
+
+ if (vstreams.size () != 0)
+ ZeroSCR = vstreams[0]->au->DTS;
+ else
+ ZeroSCR = (*estreams)[0]->au->DTS;
+
+ for (str = vstreams.begin (); str < vstreams.end (); ++str)
+ (*str)->SetSyncOffset (video_delay + current_SCR - ZeroSCR);
+ for (str = astreams.begin (); str < astreams.end (); ++str)
+ (*str)->SetSyncOffset (audio_delay + current_SCR - ZeroSCR);
+ pstrm.nsec = 0;
+ for (str = estreams->begin (); str < estreams->end (); ++str)
+ (*str)->nsec = 0;
+ seg_state = mid_segment;
+ //for( str = estreams->begin(); str < estreams->end(); ++str )
+ //{
+ //mjpeg_info("STREAM %02x: SCR=%lld mux=%d reqDTS=%lld",
+ //(*str)->stream_id,
+ //current_SCR /300,
+ //(*str)->MuxPossible(current_SCR),
+ //(*str)->RequiredDTS()/300
+ //);
+ //}
+ break;
+
+ case mid_segment:
+ /* Once we exceed our file size limit, we need to
+ start a new file soon. If we want a single stream we
+ simply switch.
+
+ Otherwise we're in the last gop of the current segment
+ (and need to start running streams out ready for a
+ clean continuation in the next segment).
+ TODO: runout_PTS really needs to be expressed in
+ sync delay adjusted units...
+ */
+
+ master = vstreams.size () > 0 ? static_cast < VideoStream * >(vstreams[0]) : 0;
+ if (psstrm->FileLimReached ()) {
+ if (opt_multifile_segment || master == 0)
+ psstrm->NextFile ();
+ else {
+ if (master->NextAUType () == IFRAME) {
+ seg_state = runout_segment;
+ runout_PTS = master->NextRequiredPTS ();
+ mjpeg_debug ("Running out to (raw) PTS %lld SCR=%lld",
+ runout_PTS / 300, current_SCR / 300);
+ running_out = true;
+ seg_state = runout_segment;
+ }
+ }
+ } else if (master != 0 && master->EndSeq ()) {
+ if (split_at_seq_end && master->Lookahead () != 0) {
+ if (!master->SeqHdrNext () || master->NextAUType () != IFRAME) {
+ mjpeg_error_exit1 ("Sequence split detected %d but no following sequence found...",
+ master->NextAUType ());
+ }
+
+ runout_PTS = master->NextRequiredPTS ();
+ mjpeg_debug ("Running out to %lld SCR=%lld", runout_PTS / 300, current_SCR / 300);
+ MuxStatus (LOG_INFO);
+ running_out = true;
+ seg_state = runout_segment;
+ }
+ }
+ break;
+
+ }
+
+ padding_packet = false;
+ start_of_new_pack = (packets_left_in_pack == packets_per_pack);
+
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ (*str)->DemuxedTo (current_SCR);
+ }
+
+
+
+ //
+ // Find the ready-to-mux stream with the most urgent DTS
+ //
+ ElementaryStream *despatch = 0;
+ clockticks earliest = 0;
+
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ /* DEBUG
+ mjpeg_info("STREAM %02x: SCR=%lld mux=%d reqDTS=%lld",
+ (*str)->stream_id,
+ current_SCR /300,
+ (*str)->MuxPossible(current_SCR),
+ (*str)->RequiredDTS()/300
+ );
+ */
+ if ((*str)->MuxPossible (current_SCR) &&
+ (!video_first || (*str)->Kind () == ElementaryStream::video)
+ ) {
+ if (despatch == 0 || earliest > (*str)->RequiredDTS ()) {
+ despatch = *str;
+ earliest = (*str)->RequiredDTS ();
+ }
+ }
+ }
+
+ if (underrun_ignore > 0)
+ --underrun_ignore;
+
+ if (despatch) {
+ despatch->OutputSector ();
+ video_first = false;
+ if (current_SCR >= earliest && underrun_ignore == 0) {
+ mjpeg_warn ("Stream %02x: data will arrive too late sent(SCR)=%lld required(DTS)=%d",
+ despatch->stream_id, current_SCR / 300, (int) earliest / 300);
+ MuxStatus (LOG_WARN);
+ // Give the stream a chance to recover
+ underrun_ignore = 300;
+ ++underruns;
+ if (underruns > 10 && !opt_ignore_underrun) {
+ mjpeg_error_exit1 ("Too many frame drops -exiting");
+ }
+ }
+ if (despatch->nsec > 50 && despatch->Lookahead () != 0 && !running_out)
+ despatch->UpdateBufferMinMax ();
+ padding_packet = false;
+
+ } else {
+ //
+ // If we got here no stream could be muxed out.
+ // We therefore generate padding packets if necessary
+ // usually this is because reciever buffers are likely to be
+ // full.
+ //
+ if (vbr) {
+ //
+ // VBR: For efficiency we bump SCR up to five times or
+ // until it looks like buffer status will change
+ NextPosAndSCR ();
+ clockticks next_change = static_cast < clockticks > (0);
+
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ clockticks change_time = (*str)->bufmodel.NextChange ();
+
+ if (next_change == 0 || change_time < next_change)
+ next_change = change_time;
+ }
+ unsigned int bumps = 5;
+
+ while (bumps > 0 && next_change > current_SCR + ticks_per_sector) {
+ NextPosAndSCR ();
+ --bumps;
+ }
+ } else {
+ // Just output a padding packet
+ OutputPadding (false);
+ }
+ padding_packet = true;
+ }
+
+ /* Update the counter for pack packets. VBR is a tricky
+ case as here padding packets are "virtual" */
+
+ if (!(vbr && padding_packet)) {
+ --packets_left_in_pack;
+ if (packets_left_in_pack == 0)
+ packets_left_in_pack = packets_per_pack;
+ }
+
+ MuxStatus (LOG_DEBUG);
+ /* Unless sys headers are always required we turn them off after the first
+ packet has been generated */
+ include_sys_header = always_sys_header_in_pack;
+
+ pcomp = completed.begin ();
+ str = estreams->begin ();
+ while (str < estreams->end ()) {
+ if (!(*pcomp) && (*str)->MuxCompleted ()) {
+ mjpeg_info ("STREAM %02x completed @ %d.", (*str)->stream_id, (*str)->au->dorder);
+ MuxStatus (LOG_DEBUG);
+ (*pcomp) = true;
+ }
+ ++str;
+ ++pcomp;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+void
+OutputStream::Close ()
+{
+ // Tidy up
+ OutputSuffix ();
+ psstrm->Close ();
+ mjpeg_info ("Multiplex completion at SCR=%lld.", current_SCR / 300);
+ MuxStatus (LOG_INFO);
+ for (str = estreams->begin (); str < estreams->end (); ++str) {
+ (*str)->Close ();
+ if ((*str)->nsec <= 50)
+ mjpeg_info ("BUFFERING stream too short for useful statistics");
+ else
+ mjpeg_info ("BUFFERING min %d Buf max %d", (*str)->BufferMin (), (*str)->BufferMax ());
+ }
+
+ if (underruns > 0) {
+ mjpeg_error ("foo");
+ mjpeg_error_exit1 ("MUX STATUS: Frame data under-runs detected!");
+ } else {
+ mjpeg_info ("foo");
+ mjpeg_info ("MUX STATUS: no under-runs detected.");
+ }
+}
+
+/**
+ Calculate the packet payload of the output stream at a certain timestamp.
+@param strm the output stream
+@param buffers the number of buffers
+@param PTSstamp presentation time stamp
+@param DTSstamp decoding time stamp
+ */
+unsigned int
+OutputStream::PacketPayload (MuxStream & strm, bool buffers, bool PTSstamp, bool DTSstamp)
+{
+ return psstrm->PacketPayload (strm, sys_header_ptr, pack_header_ptr, buffers, PTSstamp, DTSstamp)
+ - strm.StreamHeaderSize ();
+}
+
+/***************************************************
+
+ WritePacket - Write out a normal packet carrying data from one of
+ the elementary stream being muxed.
+@param max_packet_data_size the maximum packet data size allowed
+@param strm output mux stream
+@param buffers ?
+@param PTSstamp presentation time stamp of the packet
+@param DTSstamp decoding time stamp of the packet
+@param timestamps ?
+@param returns the written bytes/packets (?)
+***************************************************/
+
+unsigned int
+OutputStream::WritePacket (unsigned int max_packet_data_size,
+ MuxStream & strm,
+ bool buffers, clockticks PTS, clockticks DTS, uint8_t timestamps)
+{
+ unsigned int written = psstrm->CreateSector (pack_header_ptr,
+ sys_header_ptr,
+ max_packet_data_size,
+ strm,
+ buffers,
+ false,
+ PTS,
+ DTS,
+ timestamps);
+
+ NextPosAndSCR ();
+ return written;
+}
+
+/***************************************************
+ *
+ * WriteRawSector - Write out a packet carrying data for
+ * a control packet with irregular content.
+@param rawsector data for the raw sector
+@param length length of the raw sector
+ ***************************************************/
+
+void
+OutputStream::WriteRawSector (uint8_t * rawsector, unsigned int length)
+{
+ //
+ // Writing raw sectors when packs stretch over multiple sectors
+ // is a recipe for disaster!
+ //
+ assert (packets_per_pack == 1);
+ psstrm->RawWrite (rawsector, length);
+ NextPosAndSCR ();
+
+}
+
+
+
+/******************************************************************
+ OutputPadding
+
+ generates Pack/Sys Header/Packet information for a
+ padding stream and saves the sector
+
+ We have to pass in a special flag to cope with appalling mess VCD
+ makes of audio packets (the last 20 bytes being dropped thing) 0 =
+ Fill the packet completetely. This include "audio packets" that
+ include no actual audio, only a system header and padding.
+@param vcd_audio_pad flag for VCD audio padding
+******************************************************************/
+
+
+void
+OutputStream::OutputPadding (bool vcd_audio_pad)
+{
+ if (vcd_audio_pad)
+ psstrm->CreateSector (pack_header_ptr, sys_header_ptr,
+ 0, vcdapstrm, false, false, 0, 0, TIMESTAMPBITS_NO);
+ else
+ psstrm->CreateSector (pack_header_ptr, sys_header_ptr,
+ 0, pstrm, false, false, 0, 0, TIMESTAMPBITS_NO);
+ ++pstrm.nsec;
+ NextPosAndSCR ();
+
+}
+
+ /******************************************************************
+ * OutputGOPControlSector
+ * DVD System headers are carried in peculiar sectors carrying 2
+ * PrivateStream2 packets. We're sticking 0's in the packets
+ * as we have no idea what's supposed to be in there.
+ *
+ * Thanks to Brent Byeler who worked out this work-around.
+ *
+ ******************************************************************/
+
+void
+OutputStream::OutputDVDPriv2 ()
+{
+ uint8_t *packet_size_field;
+ uint8_t *index;
+ uint8_t sector_buf[sector_size];
+ unsigned int tozero;
+
+ assert (sector_size == 2048);
+ PS_Stream::BufferSectorHeader (sector_buf, pack_header_ptr, &sys_header, index);
+ PS_Stream::BufferPacketHeader (index, PRIVATE_STR_2, 2, // MPEG 2
+ false, // No buffers
+ 0, 0, 0, // No timestamps
+ 0, TIMESTAMPBITS_NO, packet_size_field, index);
+ tozero = sector_buf + 1024 - index;
+ memset (index, 0, tozero);
+ index += tozero;
+ PS_Stream::BufferPacketSize (packet_size_field, index);
+
+ PS_Stream::BufferPacketHeader (index, PRIVATE_STR_2, 2, // MPEG 2
+ false, // No buffers
+ 0, 0, 0, // No timestamps
+ 0, TIMESTAMPBITS_NO, packet_size_field, index);
+ tozero = sector_buf + 2048 - index;
+ memset (index, 0, tozero);
+ index += tozero;
+ PS_Stream::BufferPacketSize (packet_size_field, index);
+
+ WriteRawSector (sector_buf, sector_size);
+}
+
+
+/*
+ * Local variables:
+ * c-file-style: "stroustrup"
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */