From 2f0cc8ec30e8c26fed7b2c9ac2614c9a7655fb65 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sat, 26 Jul 2003 03:01:58 +0000 Subject: Moved from gst-plugins/ext/mplex/. See that directory for older changelogs. Original commit message from CVS: Moved from gst-plugins/ext/mplex/. See that directory for older changelogs. --- gst-libs/ext/mplex/multplex.cc | 1121 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1121 insertions(+) create mode 100644 gst-libs/ext/mplex/multplex.cc (limited to 'gst-libs/ext/mplex/multplex.cc') diff --git a/gst-libs/ext/mplex/multplex.cc b/gst-libs/ext/mplex/multplex.cc new file mode 100644 index 00000000..dcdc97ba --- /dev/null +++ b/gst-libs/ext/mplex/multplex.cc @@ -0,0 +1,1121 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include "videostrm.hh" +#include "outputstream.hh" +#include + + +/******************************************************************* + 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: + */ -- cgit v1.2.1