summaryrefslogtreecommitdiffstats
path: root/ext/mplex/multplex.cc
diff options
context:
space:
mode:
authorDavid Schleef <ds@schleef.org>2003-07-27 05:24:15 +0000
committerDavid Schleef <ds@schleef.org>2003-07-27 05:24:15 +0000
commitd1f62e8a4f976e11634d2f9187821e8686cec76d (patch)
tree7aeb45681425dc71469510056d67f4aa20d3574b /ext/mplex/multplex.cc
parenta10432d76cb431d03e27b97d790d2ba7ae34f4b8 (diff)
downloadgst-plugins-bad-d1f62e8a4f976e11634d2f9187821e8686cec76d.tar.gz
gst-plugins-bad-d1f62e8a4f976e11634d2f9187821e8686cec76d.tar.bz2
gst-plugins-bad-d1f62e8a4f976e11634d2f9187821e8686cec76d.zip
Moved to gst-libs/ext/mplex
Original commit message from CVS: Moved to gst-libs/ext/mplex
Diffstat (limited to 'ext/mplex/multplex.cc')
-rw-r--r--ext/mplex/multplex.cc1126
1 files changed, 0 insertions, 1126 deletions
diff --git a/ext/mplex/multplex.cc b/ext/mplex/multplex.cc
deleted file mode 100644
index 10a49af8..00000000
--- a/ext/mplex/multplex.cc
+++ /dev/null
@@ -1,1126 +0,0 @@
-
-#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"
-#include <cassert>
-#include "glib.h"
-
-
-/*******************************************************************
- 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 = %d Audio delay = %d",
- sectors_delay, (int)video_delay / 300, (int)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 %d SCR=%d",
- (int)runout_PTS / 300, (int)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 %d SCR=%d", (int)runout_PTS / 300, (int)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)=%d required(DTS)=%d",
- despatch->stream_id, (int) 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=%d.", (int) 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;
- unsigned int tozero;
-
- sector_buf = g_new0(uint8_t, sector_size);
-
- 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);
-
- g_free(sector_buf);
-}
-
-
-/*
- * Local variables:
- * c-file-style: "stroustrup"
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */