fi.helsinki.cs.ohtu.mpeg2
Class ProgramStreamMuxer

java.lang.Object
  extended by fi.helsinki.cs.ohtu.mpeg2.ProgramStreamMuxer
All Implemented Interfaces:
PacketizerListener

public class ProgramStreamMuxer
extends java.lang.Object
implements PacketizerListener

Implements an MPEG-2 program stream multiplexer. It supports both constant and variable bitrate multiplexing.

Multiplexing consists roughly of the following phases:

This class implements the packet scheduling and multiplex generation. For high-level stream handling, see AudioStream and VideoStream. A generic packetizer for elementary streams is AVPacketizer.


The multiplexer generates program stream in fixed sized chunks (the exact size can be set). This is very useful if the stream has to be aligned to storage medium sectors (as is with DVD and VCD). In general, it makes the multiplexing process easier to implement. As a downside, the use of chunks makes the multiplexer a little stupid: Every now and then there will be some padding just to keep up the alignment. Thus a successful multiplex requires a higher bitrate than really necessary.

Multiplexing special formats, like for a DVD or VCD, is not supported out of the box.

A chunk contains always at most one PES packet of one elementary stream. A PES packet containing data might contain stuffing bytes. The data packet might be followed by a padding packet in order to to match with the chunk size. Some chunks are purely for padding: they contain no real data. Those are needed when there is not useful data available and constant bit rate has to be maintained.

An example of a 2048 byte chunk with a pack header and some padding:

   +----------+-------------------------+-----------+
   | pack hdr |     data PES packet     |  padding  |
   +----------+-------------------------+-----------+

   |------------------- 2048 bytes -----------------|
 

The use of this multiplexer is divided in two phases: initialization and stream generation. The initialization phase starts when a ProgramStreamMuxer instance is created. During the initialization program stream parameters can be adjusted and input stream added. The multiplexer transitions from initialization to stream generation when the first piece of input data arrives. The stream generation phase continues until the last open input stream is closed.

IOExceptions are handled in a synchronous manner. Any caught IOException is stored. The stored exception can be polled later. The rationale for this is to keep the gory details of IO out of packetization and streaming classes. Otherwise an IOException might pop out of any method writing data to the multiplexer. The current implementation does not permit continuing multiplexing after an IOException has been caught.

This multiplexer is not thread-safe.


Here is an example how to multiplex a program stream of 512 kbit/s:

     BitOutputStream bout = new BitOutputStream(...);
     ProgramStreamMuxer psmux = new ProgramStreamMuxer(bout, 512000);
     AudioStream as = new AudioStream();
     VideoStream vs = new VideoStream();

     psmux.addStream(as.getPacketizer(0));
     psmux.addStream(vs.getPacketizer(0));

     ...

     for (...) {
         ...

         /* encode audio */
         ...
         as.write(audioFrame);

         /* encode video */
         ...
         vs.write(pictureHeader, pictureCodingExtHeader);
         vs.write(sliceHeader);
         vs.write(...);
         ...

         ...
     }

     as.close();
     vs.close();

     /* The multiplexer does not close the output stream. */
     bout.close();
 


Nested Class Summary
private static class ProgramStreamMuxer.StreamDesc
           
 
Field Summary
private  BitOutputStream bout
           
private  long bytesOut
           
private  long chunkCount
           
private  int chunkSize
           
private  int chunksPerPack
           
private static int DEFAULT_CHUNK
           
private static double DEFAULT_PACK_RATE
           
private  boolean initing
           
private  java.io.IOException ioe
           
private  java.util.List<ProgramStreamMuxer.StreamDesc> streams
           
private  SystemHeader syshdr
           
private  boolean syshdrOnce
           
 
Constructor Summary
ProgramStreamMuxer(BitOutputStream stream, int bitrate)
          Creates a program stream multiplexer.
ProgramStreamMuxer(BitOutputStream stream, int bitrate, int chunkSize, double packRate)
          Creates a program stream multiplexer.
 
Method Summary
 void addStream(AVPacketizer avp)
          Adds a stream to this multiplex.
 void addStream(AVPacketizer avp, long tboff)
          Adds a stream to this multiplex.
private  long bytesToTicks(long bcount)
          Gets how many system clock ticks it takes to transmit the given number of bytes.
private  void calcDelays()
          Calculates startup delays for the streams and adjusts timebase offsets accordingly.
private  float getChunkRate()
          Returns the chunk rate.
 int getChunksPerPack()
          Returns the pack rate in chunks per pack.
 float getPackRate()
          Gets the pack rate.
 boolean isFixedRate()
          Returns whether the multiplex rate is fixed.
 boolean isSystemHeaderOnce()
          Gets whether the system header is written only once, along with the first pack header.
 void packetDataAvail(AVPacketizer p)
          Acknowledges this multiplexer that a paketizer has some data available.
 java.io.IOException pollIOException()
          Returns pending IOException, or returns null, if no exception has been caught.
private  void schedule()
          Determines from which input stream to pull data into the multiplex.
private  long scrNow()
          Returns the current SCR.
 void setChunksPerPack(int chunksPerPack)
          Sets the pack rate in chunks per pack.
 void setFixedRate(boolean fixedRate)
          Sets whether a fixed rate multiplex is generated.
 void setPackRate(double packRate)
          Sets the pack rate.
 void setSystemHeaderOnce(boolean syshdrOnce)
          Sets whether the system header is written only once, along the first pack header.
private  boolean writeChunk(AVPacketizer avp)
          Writes a chunk of data.
private  void writePadding()
          Writes a padding chunk.
private  void writePaddingPacket(int size)
          Writes a padding PES packet.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

DEFAULT_CHUNK

private static final int DEFAULT_CHUNK
See Also:
Constant Field Values

DEFAULT_PACK_RATE

private static final double DEFAULT_PACK_RATE
See Also:
Constant Field Values

chunkSize

private final int chunkSize

chunksPerPack

private int chunksPerPack

chunkCount

private long chunkCount

bytesOut

private long bytesOut

syshdrOnce

private boolean syshdrOnce

initing

private boolean initing

bout

private final BitOutputStream bout

syshdr

private final SystemHeader syshdr

streams

private final java.util.List<ProgramStreamMuxer.StreamDesc> streams

ioe

private java.io.IOException ioe
Constructor Detail

ProgramStreamMuxer

public ProgramStreamMuxer(BitOutputStream stream,
                          int bitrate)
Creates a program stream multiplexer.

Parameters:
stream - the stream to write the multiplex into
bitrate - the bitrate of the multiplexed stream, in bits/s

ProgramStreamMuxer

public ProgramStreamMuxer(BitOutputStream stream,
                          int bitrate,
                          int chunkSize,
                          double packRate)
Creates a program stream multiplexer.

Parameters:
stream - the stream to write the multiplex into
bitrate - the bitrate of the multiplexed stream, in bits/s
chunkSize - the chunk size
packRate - the pack rate, in packs per second
Method Detail

getPackRate

public float getPackRate()
Gets the pack rate.

Returns:
the pack rate, in packs per second

getChunksPerPack

public int getChunksPerPack()
Returns the pack rate in chunks per pack. This is the exact value the multiplexer uses behind the scenes.

Returns:
the pack rate in chunks per pack

isFixedRate

public boolean isFixedRate()
Returns whether the multiplex rate is fixed. If the rate is fixed, padding packets are generated, if needed, to match with the target bitrate.

Fixed rate multiplexing is the default.

Returns:
true if the multiplex rate is fixed

isSystemHeaderOnce

public boolean isSystemHeaderOnce()
Gets whether the system header is written only once, along with the first pack header.

Returns:
true if the system header is written only once

setPackRate

public void setPackRate(double packRate)
Sets the pack rate. It has to be at least 1.5 packs/s (or more precisely one pack every 700 ms). If the gap between packs is any longer, many players begin to loose synchronization. The eventual pack rate will be at least packRate but no more than the chunk rate.

Parameters:
packRate - the pack rate, in packs per second

setChunksPerPack

public void setChunksPerPack(int chunksPerPack)
Sets the pack rate in chunks per pack. This defines exactly when this multiplexer generates pack headers.

Parameters:
chunksPerPack - the pack rate in chunks per pack

setFixedRate

public void setFixedRate(boolean fixedRate)
Sets whether a fixed rate multiplex is generated.

Parameters:
fixedRate - true if the multiplex is fixed rate
See Also:
isFixedRate()

setSystemHeaderOnce

public void setSystemHeaderOnce(boolean syshdrOnce)
Sets whether the system header is written only once, along the first pack header.

Parameters:
syshdrOnce - true if the system header is written only once

pollIOException

public java.io.IOException pollIOException()
Returns pending IOException, or returns null, if no exception has been caught. At the moment, a pending exception is not cleared when this method is called.

Returns:
the pending IOException or null

addStream

public void addStream(AVPacketizer avp,
                      long tboff)
Adds a stream to this multiplex.

Parameters:
avp - the packetizer for the stream to be added
tboff - the time base offset, in ticks of the 90kHz clock
Throws:
java.lang.IllegalStateException - if called during stream generation

addStream

public void addStream(AVPacketizer avp)
Adds a stream to this multiplex.

Parameters:
avp - the packetizer for the stream to be added
Throws:
java.lang.IllegalStateException - if called during stream generation

packetDataAvail

public void packetDataAvail(AVPacketizer p)
Acknowledges this multiplexer that a paketizer has some data available.

Specified by:
packetDataAvail in interface PacketizerListener
Parameters:
p - the packetizer which has data available

schedule

private void schedule()
               throws java.io.IOException
Determines from which input stream to pull data into the multiplex. The scheduling will continue as long as there are data available.

Throws:
java.io.IOException

writeChunk

private boolean writeChunk(AVPacketizer avp)
                    throws java.io.IOException
Writes a chunk of data.

Parameters:
sp - the packetizer to read a data packet from
Returns:
true if a chunk was written
Throws:
java.io.IOException

writePaddingPacket

private void writePaddingPacket(int size)
                         throws java.io.IOException
Writes a padding PES packet.

Parameters:
size - the size of the padding packet, has to be at least 7
Throws:
java.io.IOException

writePadding

private void writePadding()
                   throws java.io.IOException
Writes a padding chunk.

Throws:
java.io.IOException

scrNow

private long scrNow()
Returns the current SCR.

Returns:
the current SCR

getChunkRate

private float getChunkRate()
Returns the chunk rate.

Returns:
the chunk rate, in chunks per second

calcDelays

private void calcDelays()
Calculates startup delays for the streams and adjusts timebase offsets accordingly. Playback cannot begin immediately as the decoder buffers are empty. The delay lets the buffers to fill enough to keep up with playback.


bytesToTicks

private long bytesToTicks(long bcount)
Gets how many system clock ticks it takes to transmit the given number of bytes.

Parameters:
bcount - the number of bytes
Returns:
the number of system clock ticks