+ hb_job_t *job;
+ double pts; // end time of next muxing chunk
+ double interleave; // size (in 90KHz ticks) of media chunks we mux
+ uint32_t ntracks; // total number of tracks we're muxing
+ uint32_t eof; // bitmask of track with eof
+ uint32_t rdy; // bitmask of tracks ready to output
+ uint32_t allEof; // valid bits in eof (all tracks)
+ uint32_t allRdy; // valid bits in rdy (audio & video tracks)
+ hb_track_t *track[32]; // array of tracks to mux ('ntrack' elements)
+ // NOTE- this array could be dynamically allocated
+ // but the eof & rdy logic has to be changed to
+ // handle more than 32 tracks anyway so we keep
+ // it simple and fast.
+} hb_mux_t;
+
+// The muxer handles two different kinds of media: Video and audio tracks
+// are continuous: once they start they generate continuous, consecutive
+// sequence of bufs until they end. The muxer will time align all continuous
+// media tracks so that their data will be well interleaved in the output file.
+// (Smooth, low latency playback with minimal player buffering requires that
+// data that's going to be presented close together in time also be close
+// together in the output file). Since HB's audio and video encoders run at
+// different speeds, the time-aligning involves buffering *all* the continuous
+// media tracks until a frame with a timestamp beyond the current alignment
+// point arrives on the slowest fifo (usually the video encoder).
+//
+// The other kind of media, subtitles, close-captions, vobsubs and
+// similar tracks, are intermittent. They generate frames sporadically or on
+// human time scales (seconds) rather than near the video frame rate (milliseconds).
+// If intermittent sources were treated like continuous sources huge sections of
+// audio and video would get buffered waiting for the next subtitle to show up.
+// To keep this from happening the muxer doesn't wait for intermittent tracks
+// (essentially it assumes that they will always go through the HB processing
+// pipeline faster than the associated video). They are still time aligned and
+// interleaved at the appropriate point in the output file.
+
+// This routine adds another track for the muxer to process. The media input
+// stream will be read from HandBrake fifo 'fifo'. Buffers read from that
+// stream will be time-aligned with all the other media streams then passed
+// to the container-specific 'mux' routine with argument 'mux_data' (see
+// routine OutputTrackChunk). 'is_continuous' must be 1 for an audio or video
+// track and 0 otherwise (see above).
+
+static void add_mux_track( hb_mux_t *mux, hb_fifo_t *fifo, hb_mux_data_t *mux_data,
+ int is_continuous )
+{
+ int max_tracks = sizeof(mux->track) / sizeof(*(mux->track));
+ if ( mux->ntracks >= max_tracks )
+ {
+ hb_error( "add_mux_track: too many tracks (>%d)", max_tracks );
+ return;
+ }
+
+ hb_track_t *track = calloc( sizeof( hb_track_t ), 1 );
+ track->fifo = fifo;
+ track->mux_data = mux_data;
+ track->mf.flen = 8;
+ track->mf.fifo = calloc( sizeof(track->mf.fifo[0]), track->mf.flen );
+
+ int t = mux->ntracks++;
+ mux->track[t] = track;
+ mux->allEof |= 1 << t;
+ mux->allRdy |= is_continuous << t;
+}
+
+static void mf_push( hb_track_t *track, hb_buffer_t *buf )
+{
+ uint32_t mask = track->mf.flen - 1;
+ uint32_t in = track->mf.in;
+ if ( ( ( in + 1 ) & mask ) == ( track->mf.out & mask ) )
+ {
+ // fifo is full - expand it to double the current size.
+ // This is a bit tricky because when we change the size
+ // it changes the modulus (mask) used to convert the in
+ // and out counters to fifo indices. Since existing items
+ // will be referenced at a new location after the expand
+ // we can't just realloc the fifo. If there were
+ // hundreds of fifo entries it would be worth it to have code
+ // for each of the four possible before/after configurations
+ // but these fifos are small so we just allocate a new chunk
+ // of memory then do element by element copies using the old &
+ // new masks then free the old fifo's memory..
+ track->mf.flen *= 2;
+ uint32_t nmask = track->mf.flen - 1;
+ hb_buffer_t **nfifo = malloc( track->mf.flen * sizeof(*nfifo) );
+ int indx = track->mf.out;
+ while ( indx != track->mf.in )
+ {
+ nfifo[indx & nmask] = track->mf.fifo[indx & mask];
+ ++indx;
+ }
+ free( track->mf.fifo );
+ track->mf.fifo = nfifo;
+ mask = nmask;
+ }
+ track->mf.fifo[in & mask] = buf;
+ track->mf.in = in + 1;
+}
+
+static hb_buffer_t *mf_pull( hb_track_t *track )
+{
+ hb_buffer_t *b = NULL;
+ if ( track->mf.out != track->mf.in )
+ {
+ // the fifo isn't empty
+ b = track->mf.fifo[track->mf.out & (track->mf.flen - 1)];
+ ++track->mf.out;
+ }
+ return b;
+}
+
+static hb_buffer_t *mf_peek( hb_track_t *track )
+{
+ return track->mf.out == track->mf.in ?
+ NULL : track->mf.fifo[track->mf.out & (track->mf.flen - 1)];
+}
+
+static void MoveToInternalFifos( hb_mux_t *mux )
+{
+ int i;
+ int discard = mux->job->pass != 0 && mux->job->pass != 2;