diff options
Diffstat (limited to 'doc/hackersguide/stream.docbook')
-rw-r--r-- | doc/hackersguide/stream.docbook | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/doc/hackersguide/stream.docbook b/doc/hackersguide/stream.docbook new file mode 100644 index 000000000..b9690d9fe --- /dev/null +++ b/doc/hackersguide/stream.docbook @@ -0,0 +1,656 @@ +<chapter id="stream"> + <title>xine's stream layer</title> + + <sect1> + <title>Input layer</title> + <para> + Many media players expect streams to be stored within files on + some local medium. In actual fact, media may be streamed over a + network (e.g. via HTTP or RTP), encoded onto a specialized medium + (e.g. DVD), etc. To allow you to access all this media, xine supports + the concept of an "input plugin". The tasks performed by an + input plugin are: + <itemizedlist> + <listitem> + <para> + Validation of Media Resource Locators (MRLs). + </para> + </listitem> + <listitem> + <para> + MRL specific session management (e.g. opening and closing local files). + </para> + </listitem> + <listitem> + <para> + Reading blocks/specific numbers of bytes from the input device. + </para> + </listitem> + </itemizedlist> + </para> + <para> + In addition to these tasks, the input plugin may keep track of some + input device-specific state information (e.g. a DVD plugin may keep + track of navigational state data such as current title/chapter). + </para> + <para> + There are two classes of input device which xine recognizes. + Byte-oriented devices can, upon request, return an arbitary + non-zero number of bytes from a stream. Examples of such devices + are files or network streams. Block-oriented devices, however, have + a prefered block or "frame"-size. An example of such a device is + a DVD where data is stored in logical blocks of 2048 bytes. One may + pass the hint to xine that the plugin is block-oriented by setting the + INPUT_CAP_BLOCK capability. Note that this is only a hint and + xine does not guarantee that all requests to the plugin will + be purely block based. + </para> + <sect2> + <title>Writing a xine input plugin</title> + <para> + An input plugin provides API functions which allow the engine to + access the data source the plugin encapsulates. The input plugin API + is declared in <filename>input/input_plugin.h</filename>. + </para> + <para> + An input plugin exports a public function of the form: + <programlisting> void *input_init_plugin(xine_t *xine, void *data);</programlisting> + This function initializes an input plugin class object with the + following functions: + </para> + <para> + <programlisting> char *get_description(input_class_t *this_gen);</programlisting> + This function returns a plaintext, one-line string describing the plugin. + </para> + <para> + <programlisting> char *get_identifier(input_class_t *this_gen);</programlisting> + This function returns a shorter identifier describing the plugin. + </para> + <para> + <programlisting> xine_mrl_t **get_dir(input_class_t *this_gen, const char *filename, int *nFiles);</programlisting> + Retrieves a directory listing from the plugin. This function is optional. + </para> + <para> + <programlisting> char **get_autoplay_list(input_class_t *this_gen, int *num_files);</programlisting> + Retrieves the autoplay playlist from the plugin. This function is optional. + </para> + <para> + <programlisting> int eject_media(input_class_t *this_gen);</programlisting> + Ejects the medium. This function is optional. + </para> + <para> + <programlisting> void dispose(input_class_t *this_gen);</programlisting> + This function frees the memory used by the input plugin class object. + </para> + <para> + <programlisting> input_plugin_t *get_instance(input_class_t *class_gen, xine_stream_t *stream, const char *mrl);</programlisting> + The plugin should try, if it can handle the specified MRL and return an + instance of itself if so. If not, NULL should be returned. When a new MRL + is to be played, xine engine asks all the available input plugins one by + one if they can handle the MRL. + Note that input plugins are not guaranteed to be queried + in any particular order and the first input plugin to claim an MRL + gets control so try not to duplicate MRLs already found within xine. + </para> + <para> + <programlisting> int open(input_plugin_t *this_gen);</programlisting> + You should do any device-specific initialisation within this function. + </para> + <para> + <programlisting> uint32_t get_capabilities(input_plugin_t *this_gen);</programlisting> + Returns a bit mask describing the input device's capabilities. + You may logically OR the <varname>INPUT_CAP_*</varname> constants together to get + a suitable bit-mask (via the '|' operator). + </para> + <para> + <programlisting> off_t read(input_plugin_t *this_gen, char *buf, off_t nlen);</programlisting> + Reads a specified number of bytes into a buffer and returns the number of bytes actually copied. + </para> + <para> + <programlisting> buf_element_t *read_block(input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t len);</programlisting> + Should the input plugin set the block-oriented hint and if the + demuxer supports it, this function will be called to read a block directly + into a xine buffer from the buffer pool. + </para> + <para> + <programlisting> off_t seek(input_plugin_t *this_gen, off_t offset, int origin);</programlisting> + This function is called by xine when it is required that subsequent + reads come from another part of the stream. + </para> + <para> + <programlisting> off_t get_current_pos(input_plugin_t *this_gen);</programlisting> + Returns the current position within a finite length stream. + </para> + <para> + <programlisting> off_t get_length(input_plugin_t *this_gen);</programlisting> + Similarly this function returns the length of the stream. + </para> + <para> + <programlisting> uint32_t get_blocksize(input_plugin_t *this_gen);</programlisting> + Returns the device's prefered block-size if applicable. + </para> + <para> + <programlisting> char *get_mrl(input_plugin_t *this_gen);</programlisting> + Returns the current MRL. + </para> + <para> + <programlisting> int get_optional_data(input_plugin_t *this_gen, void *data, int data_type);</programlisting> + This function allows the input to advertise extra information that is + not available through other API functions. See <varname>INPUT_OPTIONAL_*</varname> defines. + </para> + <para> + <programlisting> void dispose(input_plugin_t *this_gen);</programlisting> + This function closes all resources and frees the input_plugin_t object. + </para> + </sect2> + </sect1> + + <sect1> + <title>Demuxer layer</title> + <para> + This section is designed to familiarize a programmer with general demuxer + concepts and how they apply to the xine multimedia library. + </para> + <sect2> + <title>Introduction to demuxer theory</title> + <para> + xine's demuxer layer is responsible for taking apart multimedia files or + streams so that the engine can decode them and present them to the user. + "Demuxer" is short for demultiplexor, which is the opposite of + multiplexing. This refers to the process of combining 2 or more things + into one. Multimedia streams usually, at a minimum, multiplex an audio + stream and a video stream together into one stream. Sometimes, there are + multiple audio streams (e.g., for multiple language tracks). Sometimes, + there is a subtitle data stream multiplexed into the multimedia stream. + </para> + <para> + There are many different multimedia formats in existence and there are + varying strategies for demuxing different types of multimedia files. + Formats in the MPEG family, for example, are designed to allow easy + playback from almost any place within the file. Many formats cannot deal + with this circumstance and at least need to be demuxed from the beginning + of the stream and played through to the end. Some formats, such as MPEG and + AVI, have marker information before every chunk in the stream. Other + formats, such as Apple Quicktime, are required to have a master index that + contains all information for taking apart a file. Many game-oriented + multimedia formats are designed strictly for playing from start to finish + without any regard to random seeking within the file. + </para> + </sect2> + <sect2> + <title>Input considerations</title> + <para> + A xine demuxer interacts with xine's input layer in order to receive + data. The underlying input plugin might be a file, a network stream, or + a block-oriented disc storage device like a DVD. A file input offers the + most flexibility in being able to read either blocks of data or individual + bytes, and being able to seek freely. Other input plugins may not allow the + demuxer to seek (such as stdin or certain network streams). Some input + plugins only allow the demuxer to read blocks of data and not individual + bytes (such as the CD-DA input plugin). The demuxer needs to check the + capabilities of the underlying input plugin before attempting to seek + around. + </para> + </sect2> + <sect2> + <title>Seeking Policy</title> + <para> + If possible, it is desirable that a demuxer can seek randomly through + the stream. This is easier for some file formats and essentially impossible + for other formats. xine's seeking API function allows a seek target to be + specified in terms of a ratio from 0 to 65535, or time in milliseconds from 0. + Time-based seeking is useful for specifying, e.g., a 1-minute jump forward + or backward in a stream. With the ratio-based seeking, the demuxer can + interpret the ratio value in the domain he sees most fit. This can also be + some sort of time or a simple file offset. + </para> + <para> + If a multimedia stream has video, there generally needs to be a way to + identify keyframes in the stream in order to facilitate seeking. Many + game-oriented formats fall over in this area as they carry no keyframe + information aside from the implicit assumption that the first frame is a + keyframe. + </para> + <para> + In a stream with video, a seek operation should always jump to a keyframe. + xine Policy: When the seek target is between 2 keyframes, jump to the + earlier keyframe. E.g., if there are keyframes at stream offsets 10000 and + 20000, and the user requests a seek to offset 18000, choose the keyframe + at offset 10000. + </para> + <para> + Note that there can be difficulties when the audio and video streams are + not tightly interleaved. In many formats, the audio frames are several + time units ahead of the video frames for the purpose of pre-buffering. + This is a typical scenario in the middle of a stream: + <programlisting> + audio frame @ time 10 + video frame @ time 8 + audio frame @ time 11 + video frame @ time 9 + audio frame @ time 12 + keyframe @ time 10 + audio frame @ time 13</programlisting> + If the demuxer seeks to the keyframe @ time 10, the next audio chunk will + have a timestamp of 13, which is well ahead of where the video is. While + the xine engine will eventually recover, it will make playback choppy for + a few seconds after the seek. One strategy for dealing with this situation + is to seek back to the nearest keyframe before the requested seek and then + seek back to find the audio frame with the nearest timestamp before the + keyframe. In this example, that would mean seeking back to [af@time 10]. + Then, demux the chunks in order, but skip the video frames until the next + keyframe is encountered. + </para> + </sect2> + <sect2> + <title>Writing a xine demuxer</title> + <para> + A demuxer plugin provides API functions which allow the engine to + initialize demuxing, dispatch data chunks to the engine, seek within the + stream, get the stream length, among other functions. The demuxer API + is declared in <filename>demuxers/demux.h</filename>. + </para> + <para> + Writing a new xine demuxer is largely a process of using other demuxers as + references and understanding how they interact with the engine. This + section will give a brief overview of each API function. + </para> + <para> + A demuxer plugin exports a public function of the form: + <programlisting> void *demux_wc3movie_init_plugin(xine_t *xine, void *data);</programlisting> + This function initializes a demuxer plugin class object with 6 + demuxer-specific functions. These functions mainly provide information + that a frontend can use to build user-friendly features. These functions + include: + </para> + <para> + <programlisting> char *get_description(demux_class_t *this_gen);</programlisting> + This function returns a plaintext, one-line string describing the plugin. + </para> + <para> + <programlisting> char *get_identifier(demux_class_t *this_gen);</programlisting> + This function returns a shorter identifier describing the plugin. + </para> + <para> + <programlisting> char *get_extensions(demux_class_t *this_gen);</programlisting> + This function returns a string with the file extensions that this demuxer + is known to use. For example, Microsoft .WAV files use "wav". If there are + multiple known extensions, separate each extension with a space. For + example, Apple Quicktime has the extensions "mov qt mp4". + </para> + <para> + <programlisting> char *get_mimetypes(demux_class_t *this_gen)</programlisting> + This function returns a string with the MIME types that this demuxer is + known to use. Multiple MIME type specifications should be separated with a + semicolon (;). For example, Apple Quicktime uses several MIME types: + <programlisting> + return "video/quicktime: mov,qt: Quicktime animation;" + "video/x-quicktime: mov,qt: Quicktime animation;" + "application/x-quicktimeplayer: qtl: Quicktime list;";</programlisting> + </para> + <para> + <programlisting> void class_dispose(demux_class_t *this_gen);</programlisting> + This function frees the memory used by the demuxer plugin class object. + </para> + <para> + <programlisting> demux_plugin_t *open_plugin(demux_class_t *class_gen, xine_stream_t *stream, input_plugin_t *input_gen);</programlisting> + This function is invoked by the xine engine to determine if the demuxer is + able to handle a particular multimedia stream. The engine can specify if + the demuxer is supposed to check the stream by content (validate the actual + stream data and see if it is of the expected type), by extension (check the + name of the MRL and see if the file extension is correct), or explicitly + (the engine is passing on a user request to force this demuxer to be used). + </para> + <para> + The order in which the engine queries the available demuxers is determined + by the priority stated in the demuxer_info_t, which is attached to every + demuxer's plugin info structure. Demuxers with higher priority values are + called before those with lower priority. The order amongst demuxers of + equal priority is undefined. The idea behind this is to have the demuxers + for high-level container formats have high priorities, while the raw format + demuxers have low priorities. This way, a stream of a high-level container + format with a beginning that happens to look like a low-level raw format is + still handled by the correct demuxer, because it is queried first. + </para> + <para> + NOTE: In the course of checking the stream by content, care must be taken + not to consume bytes out of a non-seekable stream. If the stream is + non-seekable, use the input plugin's preview buffer facility to get a cache + of the first few bytes. If the stream is seekable, reset the stream before + operating on the data (you do not know where some other demuxer left the + stream positioned). + </para> + <para> + If the demuxer can handle the stream, it creates a new demux_plugin_t + structure and initializes the main demuxer functions which are called by + the engine to do the tough demuxing duty. These functions include: + </para> + <para> + <programlisting> void demux_send_headers(demux_plugin_t *this_gen);</programlisting> + This function generally reads the headers of the stream, does whatever it + has to do to figure out what audio and video codecs are used in the file, + and asks the xine engine to initialize the correct decoders with the + proper parameters (like width and height for video, sample rate and + channels for audio). + </para> + <para> + <programlisting> int demux_send_chunk(demux_plugin_t *this_gen);</programlisting> + This function reads data from the stream and sends it to the appropriate + decoder. This is where the bulk of the demuxing work is performed. Despite + the name, the function is actually free to send as much data as it wants + to, or as much as it can. A good policy is to send an entire chunk of + compressed audio or video data and then return. The chunk is likely large + enough that it will have to be broken up into multiple xine buffers. If + a chunk of audio is 20000 bytes large, and the engine is returning + 4096-byte buffers, send 4 full buffers and 1 partial buffer to the audio + decoder and then return. + </para> + <para> + <programlisting> int demux_seek(demux_plugin_t *this_gen, off_t start_pos, int start_time, int playing);</programlisting> + This function is called by the engine to request stream repositioning. + This function should be implemented if possible. See the section on + "Seeking Policy" for more information. A seek operation should reposition + the demuxer's internal accounting variables to be ready to start + dispatching chunks from the new position when the xine engine calls + demux_send_chunk() again. If seeking is not feasible, the function quietly + returns and the demuxer's position is unaffected. + </para> + <para> + <programlisting> void demux_dispose(demux_plugin_t *this_gen);</programlisting> + This function frees the demux_plugin_t object. + </para> + <para> + <programlisting> int demux_get_status(demux_plugin_t *this_gen);</programlisting> + This function returns the current internal status of the demuxer. There + are 2 states: DEMUX_OK, for when the demuxer is demuxing or ready to demux, + and DEMUX_FINISHED, for when the demuxer has reached the end of the stream + or has encountered some sort of error. + </para> + <para> + <programlisting> int demux_get_stream_length(demux_plugin_t *this_gen);</programlisting> + This function returns the length (time duration) of the stream in + milliseconds. If the length of the stream cannot be determined, return 0. + </para> + <para> + <programlisting> uint32_t demux_get_capabilities(demux_plugin_t *this_gen);</programlisting> + This function returns an array of bit flags indicating special features of + the demuxer. See <varname>DEMUX_CAP_*</varname> defines. + </para> + <para> + <programlisting> int demux_get_optional_data(demux_plugin_t *this_gen, void *data, int data_type);</programlisting> + This function allows the demuxer to advertise extra information that is + not available through other API functions. See <varname>DEMUX_OPTIONAL_*</varname> defines. + </para> + </sect2> + <sect2> + <title>Buffer types</title> + <para> + Demuxer must send data to decoders using two fifos names <varname>video_fifo</varname> + and <varname>audio_fifo</varname>. Both are available at <varname>stream</varname> + level. The following code fragment shows how it's done. + </para> + <programlisting> + buf_element_t *buf; + + buf = stream->video_fifo->buffer_pool_alloc(stream->video_fifo); + buf->type = BUF_CONTROL_START; + stream->video_fifo->put(stream->video_fifo, buf);</programlisting> + <para> + Buffers must have set the <varname>type</varname> field as shown. All buffer types are + defined in <filename>xine-engine/buffer.h</filename>. + </para> + <para> + The control buffer types are very important and must be sent by all kinds of demuxers. + They tell decoders to start/stop their operations and inform metronom about + discontinuities, either relative or absolute. There is also a reset buffer + type that must be sent when demuxers are seeking as a "warm restart" indication to + the decoders. + </para> + <para> + To help finding out buffer types for known codecs, functions from <filename>buffer_types.c</filename> + may be used to convert "FOURCC" codes or audio format tags (as used in AVI files) to the xine + byffer type: + <programlisting> buf->type = fourcc_to_buf_video((void*)this->avi->bih.biCompression);</programlisting> + </para> + </sect2> + </sect1> + + <sect1> + <title>Decoder layer</title> + <para> + This section is designed to familiarize a programmer with basic audio + and video decoding concepts and how they apply to the xine decoder API. + </para> + <sect2> + <title>Audio and video decoders</title> + <para> + Audio and video data requires an enormous amount of storage. Thus, the + raw data is encoded using a variety of compression techniques which + drastically reduces the amount of space required to transmit and store the + data. Before playback, the compressed data needs to be decoded. + </para> + <para> + The process of decoding data is rather straightforward in a computer + science sense: An array of encoded data is fed into a decoder and the + decoder outputs an array of decoded data which is ready to be presented + to the user (either displayed on the screen or played through the + speakers). + </para> + </sect2> + <sect2> + <title>Video output formats</title> + <para> + Raw video data comes in a variety of formats, most commonly in RGB and + YUV. xine's output layer currently only accepts data in YV12 format (a.k.a. + YUV 4:2:0 planar) or YUY2 format (a.k.a. YUV 4:2:2 packed). If the output + format is a RGB space, the data must be converted to an acceptable YUV + format before being dispatched to the video output unit. xine has a number + of support functions to facilitate converting RGB to YUV. + </para> + </sect2> + <sect2> + <title>Audio output formats</title> + <para> + Raw audio data equates to uncompressed PCM audio. xine's audio output + modules expect 8-bit PCM data to be unsigned and 16-bit PCM data to be + signed and in little endian format. When there is more than one channel, + the channel data is interleaved. For example, stereo data is interleaved + as left sample, right sample: LRLRLRLR. If there are 4 or 6 channels, the + same interleaving applies: 123456123456. + </para> + </sect2> + <sect2> + <title>Writing a xine decoder</title> + <para> + Writing a new xine decoder for an audio or video format entails + accumulating a buffer of encoded data, performing the necessary operations + for decoding and then passing it on the appropriate output module. The + best reference for understanding the decoder API is the various decoding + modules available. In particular, xine has example video and audio + decoders named <filename>src/libxinevdec/foovideo.c</filename> and + <filename>src/libxineadec/fooaudio.c</filename>, respectively. + </para> + <para> + This section will give a brief overview of each API function. + The decoder API is declared in <filename>src/xine-engine/video_decoder.h</filename> + and <filename>src/xine-engine/audio_decoder.h</filename>. + </para> + <para> + A decoder plugin must, like every plugin, export a public array of + plugin_info_t types. The array usually has 2 entries: The first contains + the plugin information regarding the decoder and the second entry is + a terminating NULL entry. However, there may be more entries. + Each entry contains 6 fields: + <itemizedlist> + <listitem> + <para> + <varname>plugin type</varname>: Either PLUGIN_VIDEO_DECODER or PLUGIN_AUDIO_DECODER. + </para> + </listitem> + <listitem> + <para> + <varname>API</varname>: The plugin API revision that this plugin adheres to. + </para> + </listitem> + <listitem> + <para> + <varname>name</varname>: A character string that identifies the plugin. + </para> + </listitem> + <listitem> + <para> + <varname>version</varname>: #define'd as XINE_VERSION_CODE. + </para> + </listitem> + <listitem> + <para> + <varname>supported types</varname>: A structure that defines the buffer types that this plugin can handle. + </para> + </listitem> + <listitem> + <para> + <varname>init function</varname>: The function that the xine engine calls in order to initialize this decoder plugin. + </para> + </listitem> + </itemizedlist> + The supported types field is a decoder_info_t structure. This struct + combines a list of buffer types that the plugin can handle, along with + a relative default priority. The priority allows xine to have multiple + plugins that can handle one data type and the plugin with the highest + priority takes precedence. The code defines the default priority, which + can be overriden by the user. + The list of buffer types is an array of uint32_t types from the list of + buffer types defined in <filename>src/xine-engine/buffer.h</filename>. + </para> + <para> + <programlisting> void *init_plugin(xine_t *xine, void *data);</programlisting> + This function allocates a plugin class and initializes a set of functions + for the xine engine to invoke. These functions include: + </para> + <para> + <programlisting> char *get_identifier(video_decoder_class_t *this);</programlisting> + <programlisting> char *get_identifier(audio_decoder_class_t *this);</programlisting> + This function returns a brief character string identifying the plugin. + </para> + <para> + <programlisting> char *get_description(video_decoder_class_t *this);</programlisting> + <programlisting> char *get_description(audio_decoder_class_t *this);</programlisting> + This function returns a slightly longer description of the plugin. + </para> + <para> + <programlisting> void dispose_class(video_decoder_class_t *this);</programlisting> + <programlisting> void dispose_class(audio_decoder_class_t *this);</programlisting> + This function frees the resources allocated by the plugin class. + </para> + <para> + <programlisting> video_decoder_t *open_plugin(video_decoder_class_t *class_gen, xine_stream_t *stream);</programlisting> + <programlisting> audio_decoder_t *open_plugin(audio_decoder_class_t *class_gen, xine_stream_t *stream);</programlisting> + This function initializes the decoder plugin's private state. It also + initializes and returns either an audio_decoder_t or a video_decoder_t for + the engine. The decoder_t contains a number of functions that the plugin + invokes to handle data decoding. These functions include: + </para> + <para> + <programlisting> void decode_data(video_decoder_t *this_gen, buf_element_t *buf);</programlisting> + <programlisting> void decode_data(audio_decoder_t *this_gen, buf_element_t *buf);</programlisting> + This function performs the bulk of the decoding work. The xine engine + delivers buffers (xine_buffer_t data types) to this function and it is up + to this function to assemble the parts of the buffer, decode the data, and + send the decoded data to the proper output unit. The constraint is that + you must never call a port function of the output port when the port has + not been opened by you. (See the <function>open()</function> and + <function>close()</function> functions of <type>xine_video_port_t</type> + and <type>xine_audio_port_t</type>.) + </para> + <para> + A buffer has a <varname>decoder_flags</varname> field which can have + a number of flags set. The first buffer that a decoder receives ought + to have the BUF_FLAG_HEADER flag set. This indicates that the buffer + content contains the essential setup information for decoding + (width, height, etc. for video; sample rate, channels, etc. for audio). + </para> + <para> + If the BUF_FLAG_HEADER flag is not set, the content of the buffer should + be accumulated in a private buffer until a buffer with a + BUF_FLAG_FRAME_END flag is set. This indicates that the entire chunk has + been transmitted to the decoder and is ready to be decoded. Fetch either + an empty video frame or audio buffer from the appropriate output unit. Perform + the appropriate decoding operations and set the pts for the output buffer + (and the duration, a.k.a. video_step, for video). Dispatch the decoded + data to the output and reset the internal buffer accumulation accounting. + </para> + <para> + <programlisting> void flush(video_decoder_t *this_gen);</programlisting> + <programlisting> void flush(audio_decoder_t *this_gen);</programlisting> + This function is called when either the xine engine flushes the stream, e.g., + after a seek operation or when decoding runs too slow and frames arrive in + the output loops fast enough. Decoders should release everything they have + already decoded, drop the rest and wait for new input. + </para> + <para> + <programlisting> void reset(video_decoder_t *this_gen);</programlisting> + <programlisting> void reset(audio_decoder_t *this_gen);</programlisting> + This function is called when the xine engine resets the stream. + Decoders should get ready to receive data that has nothing to do + with the one it worked on up to now. + </para> + <para> + <programlisting> void discontinuity(video_decoder_t *this_gen);</programlisting> + <programlisting> void discontinuity(audio_decoder_t *this_gen);</programlisting> + This function is called when the xine engine encounters a pts + discontinuity. Decoders should forget all timestamping information + they might have accumulated from the stream to not confuse metronom. + </para> + <para> + <programlisting> void dispose(video_decoder_t *this_gen);</programlisting> + <programlisting> void dispose(audio_decoder_t *this_gen);</programlisting> + This function frees the resources used by the decoder plugin. + </para> + </sect2> + <sect2> + <title>SPU decoder</title> + <para> + A lot written above also applies for subpicture unit (SPU) decoders. The + SPU decoder API is declared in <filename>src/xine-engine/spu_decoder.h</filename>. + Details on the data, SPU decoders are expected to output, see the section on + <link linkend="osd">overlays and OSD</link>. + </para> + <para> + However, there are some differences to consider. At first, unlike audio and + video, subtitles do not form a continuous stream. The decoder will therefore + only be called once in a while. The metronom call for timestamping, + which for audio and video is done by the engine, has to be done manually for SPU: + <programlisting> vpts = metronom->got_spu_packet(metronom, buf->pts);</programlisting> + </para> + <para> + Another difference is that while both audio and video decoders are automatically + blocked in their <function>get_buffer()</function>/<function>get_frame()</function> + methods when the output cannot take any more data, this does not work for SPU, + because it could take minutes before the next free slot becomes available and we must not + block the decoder thread for that long since it might be shared with a video decoder. + But when a SPU decoder does not share the thread and we let it run without any + blocking, it will overflow the available overlay slots very soon. Since SPU + decoders should not have to know, whether they share the thread or not, a helper + function <function>_x_spu_decoder_sleep()</function> is provided, which, when told + the timestamp of the next overlay, will wait long enough to not overflow the + overlay slots, but short enough to not hinder a video decoder in the same thread. + </para> + <para> + There are also two functions in the SPU decoder API, which have not been discussed above: + </para> + <para> + <programlisting> int get_interact_info(spu_decoder_t *this_gen, void *data);</programlisting> + Since SPUs are sometimes (on DVDs for example) used for user interaction like menu + highlights, this function can be called to get <varname>data</varname> filled with + the current interaction information. The caller and the decoder have to agree on + what this is exactly. With DVDs, you can get a copy of the current NAV packet here. + </para> + <para> + <programlisting> void set_button(spu_decoder_t *this_gen, int32_t button, int32_t mode);</programlisting> + Also for interaction, you can ask the decoder here to change the + current highlighting. + </para> + </sect2> + </sect1> + +</chapter> |