[PATCH] enable mplex to multiplex subtitle streams
[email protected] Tue, 09 Mar 2010 07:05:22 +0100
| Newsgroups | gmane.comp.video.mjpeg.devel |
|---|---|
| Message-ID | <[email protected]> |
Hello,
you may want to consider the attached patch for muxing subtitle streams for
mplex:
The patch enables mplex to work off private stream 1 (ps1) subtitles as found
when extracted from a dvd.
e.g.:$ tcextract -i chapter_5.vob -a 0x21 -x ps1 > chp5_1.ps1
$ mplex -d 1000:32 -f 8 -o test.vob chp5.mpv chp5_0a.mpa chp5_0.ps1
The "-d 1000:32" parameter forces the ps1 stream to be delayed for 1000 90kHz
ticks before the first subtitle stream starts, and the mux'ed stream id is 32
instead of 33 (0x21)(syntax: -d <dts-shift>:<subtitle-streamid>] [,
<dts>[:...] ...], see help for details)
You can use the patch as alternative to spumux - if you know how to provide an
alternative palette to dvdauthor (-p parameter);-).
This patch can NOT replace the menu generation feature of spumux and cannot
(yet) work with more common subtitle formats like sami,srt,mpsub or similar.
Regards,
Roland
P.S.: Sorry, my mailclient sucks.
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Mjpeg-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mjpeg-developer
subtitles.diff
(text/plain, 19.7 KB)
diff -Naur mplex/Makefile.am mplex.new/Makefile.am
--- mplex/Makefile.am 2007-02-20 03:14:58.000000000 +0100
+++ mplex.new/Makefile.am 2010-03-08 20:12:36.000000000 +0100
@@ -41,6 +41,7 @@
systems.cpp \
videostrm_in.cpp \
videostrm_out.cpp \
+ subpstream.cpp \
$(ZALPHA_FILES)
libmplex_includedir = $(pkgincludedir)/mplex
diff -Naur mplex/ac3strm_in.cpp mplex.new/ac3strm_in.cpp
--- mplex/ac3strm_in.cpp 2004-07-15 07:03:05.000000000 +0200
+++ mplex.new/ac3strm_in.cpp 2010-03-08 06:12:44.000000000 +0100
@@ -236,9 +236,9 @@
header_skip = 5; // Initially skipped past 5 bytes of header
- num_frames++;
+ num_frames++;
access_unit.start = AU_start;
- access_unit.length = framesize;
+ access_unit.length = framesize;
mjpeg_info( "AC3 frame size = %d", framesize );
bit_rate = ac3_bitrate_index[framesize_code>>1];
samples_per_second = ac3_frequency[frequency];
@@ -288,6 +288,9 @@
bs.SeekFwdBits(skip);
prev_offset = AU_start;
AU_start = bs.bitcount();
+
+
+
if( AU_start - prev_offset != access_unit.length*8 )
{
mjpeg_warn( "Discarding incomplete final frame AC3 stream %d!",
@@ -382,6 +385,7 @@
AC3Stream::ReadPacketPayload(uint8_t *dst, unsigned int to_read)
{
bitcount_t read_start = bs.GetBytePos();
+
// Remember to change StreamHeaderLen if you write a different
// length re-using this code...
unsigned int bytes_read = bs.GetBytes( dst+4, to_read-4 );
diff -Naur mplex/audiostrm.hpp mplex.new/audiostrm.hpp
--- mplex/audiostrm.hpp 2005-10-13 04:49:23.000000000 +0200
+++ mplex.new/audiostrm.hpp 2010-03-08 06:20:38.000000000 +0100
@@ -182,9 +182,10 @@
class SUBPStream : public AudioStream
{
public:
- SUBPStream(IBitStream &ibs,Multiplexor &into );
+ SUBPStream(IBitStream &ibs,SubtitleStreamParams* params,Multiplexor &into );
virtual void Init(const int stream_num);
virtual void Close();
+ static bool Probe(IBitStream &bs );
// TODO: rough and ready measure...
virtual unsigned int NominalBitRate() {return 50*1024;}
virtual unsigned int ReadPacketPayload(uint8_t *dst, unsigned int to_read);
@@ -192,9 +193,8 @@
private:
- void OutputHdrInfo();
virtual void FillAUbuffer(unsigned int frames_to_buffer);
-
+ bool ParseAUBitwise();
static const unsigned int default_buffer_size;
/* State variables for scanning source bit-stream */
@@ -202,8 +202,10 @@
unsigned int samples_per_second;
unsigned int bit_rate;
unsigned int stream_num;
- unsigned int frequency ;
unsigned int num_frames;
+ int64_t initial_offset; // DTS of first Subp.
+ SubtitleStreamParams* parms;
+ int8_t sub_stream_id; // substream_id
};
diff -Naur mplex/interact.cpp mplex.new/interact.cpp
--- mplex/interact.cpp 2006-01-15 15:19:23.000000000 +0100
+++ mplex.new/interact.cpp 2010-03-08 06:12:44.000000000 +0100
@@ -45,6 +45,7 @@
"LPCM audio",
"DTS audio",
"MPEG video",
+ "MPEG Subtitle",
"Z Alpha channel"
};
@@ -79,6 +80,7 @@
run_in_frames = 0; // Select default run-in...
audio_tracks = 0;
video_tracks = 0;
+ subtitle_tracks = 0;
lpcm_tracks = 0;
#ifdef ZALPHA
z_alpha_tracks = 0;
@@ -186,6 +188,18 @@
}
bs->UndoChanges( undo );
+
+ if( SUBPStream::Probe( *bs ) )
+ {
+ mjpeg_info ("File %s looks like an Subpicture stream.",
+ bs->StreamName());
+ bs->UndoChanges( undo );
+ streams.push_back( new JobStream( bs, SUBP_STREAM) );
+ ++subtitle_tracks;
+ continue;
+ }
+
+
#ifdef ZALPHA
if( ZAlphaStream::Probe( *bs ) )
{
@@ -222,6 +236,11 @@
{
lpcm_param.push_back(LpcmParams::Default(mux_format));
}
+ for( i = subtitle_params.size(); i < subtitle_tracks; ++i )
+ {
+ subtitle_params.push_back(SubtitleStreamParams::Default(mux_format));
+ }
+
//
// Set standard values if the selected profile implies this...
@@ -234,9 +253,10 @@
}
}
- mjpeg_info( "Found %d audio streams and %d video streams",
+ mjpeg_info( "Found %d audio streams, %d video streams and %d subtitle streams",
audio_tracks,
- video_tracks
+ video_tracks,
+ subtitle_tracks
);
}
diff -Naur mplex/interact.hpp mplex.new/interact.hpp
--- mplex/interact.hpp 2006-02-01 23:23:01.000000000 +0100
+++ mplex.new/interact.hpp 2010-03-08 06:23:47.000000000 +0100
@@ -54,6 +54,7 @@
int video_offset; // A/V sync offset. Always one 0 and the
// other positive. Specified in
int audio_offset; // MPEG-2 CLOCKS: 1/(90000*300)-th sec
+
unsigned int sector_size;
bool VBR; // Force VBR even if profile suggests CBR
bool CBR; // Force CBR even if profile suggests VBR
@@ -92,7 +93,8 @@
AC3_AUDIO,
LPCM_AUDIO,
DTS_AUDIO,
- MPEG_VIDEO
+ MPEG_VIDEO,
+ SUBP_STREAM,
#ifdef ZALPHA
,
Z_ALPHA
@@ -129,8 +131,10 @@
vector<JobStream *> streams;
vector<LpcmParams *> lpcm_param;
vector<VideoParams *> video_param;
+ vector<SubtitleStreamParams*> subtitle_params;
unsigned int audio_tracks;
unsigned int video_tracks;
+ unsigned int subtitle_tracks;
unsigned int lpcm_tracks;
#ifdef ZALPHA
unsigned int z_alpha_tracks;
diff -Naur mplex/main.cpp mplex.new/main.cpp
--- mplex/main.cpp 2008-11-30 06:43:46.000000000 +0100
+++ mplex.new/main.cpp 2010-03-08 06:23:57.000000000 +0100
@@ -252,7 +252,7 @@
bool ParseLpcmOpt( const char *optarg );
bool ParseWorkaroundOpt( const char *optarg );
bool ParseTimeOffset( const char *optarg );
-
+ bool ParseSubtitleOptions( const char *optarg );
static const char short_options[];
@@ -263,7 +263,7 @@
};
const char CmdLineMultiplexJob::short_options[] =
- "o:i:b:r:O:v:f:l:s:S:p:W:L:R:VCMh";
+ "o:i:b:r:O:v:f:l:s:S:p:W:L:R:VCMhd:";
#if defined(HAVE_GETOPT_LONG)
struct option CmdLineMultiplexJob::long_options[] =
{
@@ -286,6 +286,7 @@
{ "sector-size", 1, 0, 's' },
{ "workarounds", 1, 0, 'W' },
{ "help", 0, 0, '?' },
+ { "subpicture-delay", 1, 0, 'd' },
{ 0, 0, 0, 0 }
};
#endif
@@ -408,6 +409,14 @@
Usage(argv[0]);
}
break;
+
+ case 'd' :
+ if( ! ParseSubtitleOptions( optarg ) )
+ {
+ mjpeg_error( "Illegal Subtitle option(s): %s", optarg );
+ Usage(argv[0]);
+ }
+ break;
case '?' :
default :
Usage(argv[0]);
@@ -450,8 +459,10 @@
"--lpcm-params | -L samppersec:chan:bits [, samppersec:chan:bits]\n"
"--mux-limit|-l num\n"
" Multiplex only num seconds of material (default 0=multiplex all)\n"
- "--sync-offset|-O num ms|s|mpt\n"
+ "--sync-offset|-O num ms|s|mpt|c\n"
" Specify offset of timestamps (video-audio) in mSec\n"
+ "--subpicture-delay|-d delay [ms|s|mpt|c] [:stream-id] [, delay[:stream-id]]\n"
+ " Specify offset of timestamps (video-subpicture) in msec (default) sec, mpt or clock-ticks\n"
"--sector-size|-s num\n"
" Specify sector size in bytes for generic formats [256..16384]\n"
"--vbr|-V\n"
@@ -468,7 +479,7 @@
" Maximum size of output file(s) in Mbyte (default: 0) (no limit)\n"
"--ignore-seqend-markers|-M\n"
" Don't switch to a new output file if a sequence end marker\n"
- " is encountered ithe input video.\n"
+ " is encountered in the input video.\n"
"--vdr-index|-i <vdr-index-filename>\n"
" Generate a VDR index file with the output stream\n"
"--workaround|-W workaround [, workaround ]\n"
@@ -477,6 +488,45 @@
exit (1);
}
+bool CmdLineMultiplexJob::ParseSubtitleOptions( const char *optarg )
+{
+ double f;
+ double persecond=1000.0;
+ const char *e;
+ uint64_t subtitle_offset;
+ uint8_t stream_id;
+ e=optarg-1;
+ do
+ {
+ e++;
+ subtitle_offset =0;
+ persecond=1000.0;
+ stream_id=0x20; // default
+ f=strtod(e,const_cast<char**>(&e));
+ if( *e ) {
+ while(isspace(*e)) e++;
+ if(!strcmp(e,"ms")) { persecond=1000.0; e+=2;}
+ else if(!strcmp(e,"s")) {persecond=1.0; e++;}
+ else if(!strcmp(e,"mpt")){ persecond=90000.0;e+=3;}
+ else if(!strcmp(e,"c")){ persecond=CLOCKS;e++;}
+ }
+ subtitle_offset = static_cast<int>(f*CLOCKS/(persecond));
+ if( subtitle_offset < 0 )
+ subtitle_offset = 0; // always positive
+ if (*e ==':'){
+ ++e;
+ int nr;
+ sscanf(e,"%hhd%n",&stream_id,&nr);
+ e+=nr;
+ mjpeg_info("Stream will be mapped to 0x%02hhX",stream_id);
+ }
+
+ subtitle_params.push_back(SubtitleStreamParams::Checked(subtitle_offset,stream_id));
+ } while (*e == ',');
+
+ return true;
+
+}
bool CmdLineMultiplexJob::ParseLpcmOpt( const char *optarg )
{
@@ -608,6 +658,7 @@
if(!strcmp(e,"ms")) persecond=1000.0;
else if(!strcmp(e,"s")) persecond=1.0;
else if(!strcmp(e,"mpt")) persecond=90000.0;
+ else if(!strcmp(e,"c")) persecond=CLOCKS;
else
return false;
}
diff -Naur mplex/multiplexor.cpp mplex.new/multiplexor.cpp
--- mplex/multiplexor.cpp 2010-03-06 08:05:48.000000000 +0100
+++ mplex.new/multiplexor.cpp 2010-03-08 20:11:25.000000000 +0100
@@ -393,10 +393,10 @@
unsigned int audio_track = 0;
unsigned int video_track = 0;
+ unsigned int subp_track = 0;
std::vector<VideoParams *>::iterator vidparm = job.video_param.begin();
std::vector<LpcmParams *>::iterator lpcmparm = job.lpcm_param.begin();
-
-
+ std::vector<SubtitleStreamParams *>::iterator subpparm = job.subtitle_params.begin();
std::vector<JobStream *>::iterator i;
for( i = job.streams.begin() ; i < job.streams.end() ; ++i )
{
@@ -461,6 +461,17 @@
++audio_track;
}
break;
+ case SUBP_STREAM :
+ {
+ // we use audios stream as base class
+ SUBPStream *subpStrm = new SUBPStream( *(*i)->bs, *subpparm,*this);
+ subpStrm ->Init ( subp_track );
+ estreams.push_back(subpStrm );
+ astreams.push_back(subpStrm );
+ ++subpparm;
+ ++subp_track;
+ }
+ break;
#ifdef ZALPHA
// just copies the video parameters from the first video stream
case Z_ALPHA :
@@ -1074,7 +1085,7 @@
std::vector<bool> completed;
std::vector<bool>::iterator pcomp;
std::vector<ElementaryStream *>::iterator str;
-
+
unsigned int packets_left_in_pack = 0; /* Suppress warning */
bool padding_packet;
bool video_first = true;
diff -Naur mplex/stream_params.hpp mplex.new/stream_params.hpp
--- mplex/stream_params.hpp 2007-02-20 03:14:58.000000000 +0100
+++ mplex.new/stream_params.hpp 2010-03-08 06:12:49.000000000 +0100
@@ -23,7 +23,8 @@
#ifndef __STREAM_PARAMS_HH__
#define __STREAM_PARAMS_HH__
-
+#include <config.h>
+#include <mjpeg_types.h>
class LpcmParams
{
public:
@@ -57,6 +58,22 @@
unsigned int decode_buffer_size;
};
+class SubtitleStreamParams
+{
+public:
+ static SubtitleStreamParams *Checked(const int64_t stream_offset,int8_t stream_id){ return new SubtitleStreamParams (stream_offset,stream_id);} ;
+ static SubtitleStreamParams *Default(unsigned int mux_format) { return Checked(0,-1);};
+ bool Force(unsigned int mux_format);
+ inline int64_t Offset() { return _stream_offset; }
+ inline int8_t StreamId() { return _stream_id; }
+private:
+ SubtitleStreamParams(const int64_t stream_offset, int8_t stream_id)
+ {_stream_offset = stream_offset;
+ _stream_id = stream_id;};
+
+ int64_t _stream_offset;
+ int8_t _stream_id;
+};
//
diff -Naur mplex/subpstream.cpp mplex.new/subpstream.cpp
--- mplex/subpstream.cpp 1970-01-01 01:00:00.000000000 +0100
+++ mplex.new/subpstream.cpp 2010-03-08 20:18:11.000000000 +0100
@@ -0,0 +1,305 @@
+//
+// C++ Implementation: subpstream
+//
+// Description:
+//
+//
+// Author: grotti <[email protected]>, (C) 2010
+//
+// Copyright: See COPYING file that comes with this distribution
+// Most of the code is stolen from ac3stream.cpp
+//
+//
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "audiostrm.hpp"
+#include "interact.hpp"
+#include "multiplexor.hpp"
+// minimum version code for subtitle stream that
+// can be handled by the program
+#define MIN_VERSION_CODE 0x00030000
+// common part of the subtitle header struct for major version 3
+// taken from subtitle2vobsub
+
+typedef struct {
+
+ unsigned int header_length;
+ unsigned int header_version;
+ unsigned int payload_length;
+
+ unsigned int lpts;
+ double rpts;
+
+} subtitle_header_v3_t;
+
+typedef struct {
+ char marker[8];
+ subtitle_header_v3_t header;
+} vobsub_header;
+
+static unsigned int minor_version(unsigned int version)
+{
+ // bit 0-15 contain the minor version number
+ return version & 0xffff;
+}
+
+// get the major version number from the version code
+static unsigned int major_version(unsigned int version)
+{
+ // bit 16-31 contain the major version number
+ return version >> 16;
+}
+
+
+static const char* SUBPHEADER="SUBTITLE";
+
+SUBPStream::SUBPStream(IBitStream &ibs, SubtitleStreamParams* subpparm, Multiplexor &into) :
+ AudioStream( ibs, into ),parms(subpparm)
+
+{
+
+ num_frames =0;
+ initial_offset = -1;
+}
+
+
+
+
+bool SUBPStream::Probe(IBitStream &bs )
+{
+ //char *last_dot = strrchr( bs, '.' );
+ char buffer[20];
+ bs.GetBytes((uint8_t*)buffer,strlen(SUBPHEADER));
+if (strncmp(buffer,SUBPHEADER,strlen(SUBPHEADER)) == 0)
+ return true;
+return false;
+
+
+}
+
+void SUBPStream::Init(const int stream_num)
+{
+MuxStream::Init( PRIVATE_STR_1,
+ 1, // Buffer scale
+ 8192, // default buffer size
+ false,
+ muxinto.buffers_in_audio,
+ muxinto.always_buffers_in_audio
+ );
+ mjpeg_info ("Scanning for header info: Subpicture stream %02x (%s)",
+ stream_num,
+ bs.StreamName()
+ );
+ sub_stream_id =parms->StreamId();
+
+ ParseAUBitwise();
+
+}
+
+bool SUBPStream::ParseAUBitwise()
+{
+ vobsub_header vobsub;
+ uint8_t* ptr = reinterpret_cast<uint8_t*> (&vobsub);
+ subtitle_header_v3_t &header = vobsub.header;
+ char buffer[20];
+ int i;
+ for (i=0; i<sizeof(vobsub); i++)
+ *(ptr+i) = bs.GetBits(8);
+
+ if (strncmp(vobsub.marker,SUBPHEADER,strlen(SUBPHEADER)) != 0)
+ {
+ mjpeg_error( "Subtitle: expected header %s!",SUBPHEADER);
+ return false;
+
+ }
+
+ if (major_version(header.header_version) != major_version(MIN_VERSION_CODE))
+ {
+ mjpeg_error( "Subtitle: expected version 0x%08X, got version 0x%08X while reading subtitle header!",MIN_VERSION_CODE,header.header_version);
+ return false;
+ }
+ int16_t skip_len = header.header_length-sizeof(header);
+ if (skip_len)
+ {
+ assert (skip_len>0);
+ bs.SeekFwdBits(skip_len);
+ }
+ uint8_t subpid;
+ prev_offset = AU_start;
+ AU_start = bs.bitcount();
+ // packet starts here...
+ subpid = bs.GetBits(8);
+
+
+ access_unit.start = AU_start;
+ access_unit.length = header.payload_length;
+ if(header.rpts > 0){
+
+ access_unit.PTS=header.rpts*300.0*90000.0;
+ if (initial_offset == -1)
+ {
+ if (sub_stream_id == -1)
+ sub_stream_id = subpid;
+ mjpeg_info( "SUBTITLE id 0x%02X => 0x%02X", subpid,sub_stream_id);
+ initial_offset = access_unit.PTS;
+ mjpeg_info("Stream offset is : %lld (PTS)",access_unit.PTS);
+ mjpeg_info("Initial offset is : %lld (PTS)",initial_offset);
+ mjpeg_info("Cmd line offset is: %lld (PTS)",parms->Offset());
+ initial_offset -= parms->Offset();
+ mjpeg_info("Adjustment offset : %lld (PTS)",initial_offset);
+ }
+ access_unit.PTS-=initial_offset;
+ mjpeg_debug("Subtitle: appending PTS/DTS (int64_t) %lld",access_unit.PTS);
+
+ } else {
+ // calculate the time from lpts
+ mjpeg_info( "Subtitle: fallback to lpts", subpid);
+ access_unit.PTS= (double)(header.lpts/300)/90000.0;
+ }
+
+ access_unit.DTS = access_unit.PTS;
+ access_unit.dorder = decoding_order;
+ mjpeg_debug("appending PTS/DTS %lld",access_unit.DTS);
+ decoding_order++;
+ aunits.Append( access_unit );
+
+
+ bs.SeekFwdBits(header.payload_length-1);
+ num_frames++;
+ return true;
+}
+
+void SUBPStream::Close()
+{
+ stream_length = AU_start >> 3;
+ mjpeg_info ("SUBTITLE STATISTICS: 0x%02x", sub_stream_id);
+ mjpeg_info ("Subtitle stream length %lld bytes.", stream_length);
+ mjpeg_info ("Nr. of subtitle packets:%d", num_frames);
+
+}
+
+void SUBPStream::FillAUbuffer(unsigned int frames_to_buffer)
+{
+ uint32_t discont_ctr;
+ last_buffered_AU += frames_to_buffer;
+ mjpeg_debug( "Scanning %d Subpicture frames to frame %d",
+ frames_to_buffer, last_buffered_AU );
+ prev_offset = AU_start;
+ AU_start = bs.bitcount();
+ while (decoding_order < last_buffered_AU && !bs.eos()
+ && !muxinto.AfterMaxPTS(access_unit.PTS))
+ {
+ if (!ParseAUBitwise())
+ break;
+
+ }
+
+ last_buffered_AU = decoding_order;
+ eoscan = bs.eos() || muxinto.AfterMaxPTS(access_unit.PTS);
+}
+
+unsigned int SUBPStream::ReadPacketPayload(uint8_t *dst, unsigned int to_read)
+{
+ bitcount_t read_start = bs.GetBytePos();
+ mjpeg_debug( "SUBPStream called: ReadPacketPayload at 0x%08lld", read_start);
+ vobsub_header vobsub;
+ subtitle_header_v3_t &header = vobsub.header;
+ bs.GetBytes((uint8_t*) &vobsub,sizeof(vobsub));
+ int16_t skip_len = header.header_length-sizeof(header);
+ if (strncmp(vobsub.marker,SUBPHEADER,strlen(SUBPHEADER)) != 0)
+ {
+ mjpeg_error( "Subtitle: expected header %s!",SUBPHEADER);
+ return 0;
+
+ }
+
+ assert (skip_len>=0);
+ if (skip_len)
+ {
+ uint8_t* b = (uint8_t* ) malloc(skip_len);
+
+ bs.GetBytes(b,skip_len);
+ free (b);
+ }
+ unsigned int bytes_read = bs.GetBytes( dst, header.payload_length );
+ bs.Flush( read_start );
+ dst[0] = sub_stream_id;
+ clockticks decode_time;
+
+ unsigned int first_header =
+ (new_au_next_sec || au_unsent > bytes_read )
+ ? 0
+ : au_unsent;
+
+ // BUG BUG BUG: how do we set the 1st header pointer if we have
+ // the *middle* part of a large frame?
+ assert( first_header+2 <= to_read );
+
+ unsigned int syncwords = 0;
+ unsigned int bytes_muxed = bytes_read;
+
+ if (bytes_muxed == 0 || MuxCompleted() )
+ {
+ goto completion;
+ }
+
+
+ /* Work through what's left of the current AU and the following AU's
+ updating the info until we reach a point where an AU had to be
+ split between packets.
+ NOTE: It *is* possible for this loop to iterate.
+
+ The DTS/PTS field for the packet in this case would have been
+ given the that for the first AU to start in the packet.
+
+ */
+
+
+ decode_time = RequiredDTS();
+ mjpeg_debug("SUBPStream: Required DTS is %lld",decode_time);
+ while (au_unsent < bytes_muxed)
+ {
+ // BUG BUG BUG: if we ever had odd payload / packet size we might
+ // split an AC3 frame in the middle of the syncword!
+ assert( bytes_muxed > 1 );
+ bufmodel.Queued(au_unsent, decode_time);
+ bytes_muxed -= au_unsent;
+ if( new_au_next_sec )
+ ++syncwords;
+ if( !NextAU() )
+ {
+ goto completion;
+ }
+ new_au_next_sec = true;
+ decode_time = RequiredDTS();
+ mjpeg_debug("Required DTS is %lld (while-loop)",decode_time);
+ };
+
+ // We've now reached a point where the current AU overran or
+ // fitted exactly. We need to distinguish the latter case
+ // so we can record whether the next packet starts with an
+ // existing AU or not - info we need to decide what PTS/DTS
+ // info to write at the start of the next packet.
+
+ if (au_unsent > bytes_muxed)
+ {
+ if( new_au_next_sec )
+ ++syncwords;
+ bufmodel.Queued( bytes_muxed, decode_time);
+ au_unsent -= bytes_muxed;
+ new_au_next_sec = false;
+ }
+ else // if (au_unsent == bytes_muxed)
+ {
+ bufmodel.Queued(bytes_muxed, decode_time);
+ if( new_au_next_sec )
+ ++syncwords;
+ new_au_next_sec = NextAU();
+ }
+completion:
+
+ return bytes_read;
+}