(*****************************************************************************

  Liquidsoap, a programmable audio stream generator.
  Copyright 2003-2011 Savonet team

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details, fully stated in the COPYING
  file at the root of the liquidsoap distribution.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 *****************************************************************************)

 (** Ogg stream demuxer *)

 (** This module provides a functional abstract API to 
   * decode and seek in Ogg streams.
   *
   * Decoders are also provided in ocaml-vorbis,
   * ocaml-speex, ocaml-schroedinger, ocaml-flac and
   * ocaml-theora.
   * 
   * Functions in this module are not thread safe! *)

(** {2 Decoding} *)

(** {3 Types} *)

(** Type of an ogg stream decoder. *)
type t

(** Type for callbacks used to acess encoded data. *)
type callbacks =
  { 
    read   : int -> string*int;
    seek   : (int -> int) option;
    tell   : (unit -> int) option
  }

(** Type for a decodable track. 
  * First element is a string describing
  * the decoder used to decode the track.
  * Second element is the serial number
  * associated to the [Ogg.Stream.t] logical
  * stream used to pull data packets for that
  * track. *)
type track =
  Audio_track of (string*nativeint) |
  Video_track of (string*nativeint)

(** Type for standard tracks (see [get_standard_tracks] below). *)
type standard_tracks =
  { mutable audio_track : track option;
    mutable video_track : track option }

(** Type for metadata. First element
  * is a string describing the vendor, second
  * element is a list of metadata of the form:
  * [(label,value)]. *)
type metadata = string*((string*string) list)

(** Type for audio information. *)
type audio_info =
  { 
    channels : int;
    sample_rate : int
  }

(** Type for audio data. *)
type audio_data = (float array array)

(** Type of a video plane. *)
type video_plane = (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t

(** Supported video formats. *)
type video_format =
   | Yuvj_420   (* Planar YCbCr 4:2:0. Each component is an uint8_t,
                 * luma and chroma values are full range (0x00 .. 0xff) *)
   | Yuvj_422   (* Planar YCbCr 4:2:2. Each component is an uint8_t,
                 * luma and chroma values are full range (0x00 .. 0xff) *)
   | Yuvj_444   (* Planar YCbCr 4:4:4. Each component is an uint8_t,
                 * luma and chroma values are full range (0x00 .. 0xff) *)
(** Type for video information. *)
type video_info =
 {
    fps_numerator   : int;
    fps_denominator : int;
    width           : int; (** Width of the Y' luminance plane *)
    height          : int; (** Height of the luminance plane *) 
 }

(** Type for video data. *)
type video_data =
 {
    format    : video_format;
    frame_width  : int;
    frame_height : int;
    y_stride  : int; (** Length, in bytes, per line *)
    uv_stride : int; (** Length, in bytes, per line *)
    y : video_plane; (** luminance data *)
    u : video_plane; (** Cb data *)
    v : video_plane; (** Cr data *)
 }

(** {3 Exceptions } *)

exception Invalid_stream
exception Not_available

(* This exception has a different semantics than [Ogg.End_of_stream].
 * [Ogg.End_of_stream] is raised when end of data has been reached,
 * while this exception is raised when end of a logical stream has
 * been reached.. *)
exception End_of_stream

(** {3 Initialization functions } *)

(** Initiate a decoder with the given callbacks. 
  * [log] is an optional functioned used to 
  * return logged messages during the deocding
  * process. *)
val init : ?log:(string->unit) -> callbacks -> t 

(** Initiate a decoder from a given file name. *)
val init_from_file : ?log:(string->unit) -> string -> t*Unix.file_descr

(** Initate a decoder from a given [Unix.file_descriptor] *)
val init_from_fd : ?log:(string->unit) -> Unix.file_descr -> t

(** Get the Ogg.Sync handler associated to 
  * the decoder. Use only if know what you are doing. *)
val get_ogg_sync : t -> Ogg.Sync.t

(** Reset encoder, try to parse a new sequentialized stream.
  * To use when end_of_stream has been reached. *)
val reset : t -> unit

(** Consume all remaining pages of the current
  * stream. This function may be called to skip
  * a sequentialized stream but it may be quite
  * CPU intensive if there are many pages remaining.. 
  * 
  * [eos dec] is [true] after this call. *)
val abort : t -> unit

(** [true] if the decoder has reached the end of each
  * logical streams and all data has been decoded.
  *
  * If you do not plan on decoding some data, 
  * you should use [drop_track] to indicate it
  * to the decoder. Otherwise, [eos] will return
  * [false] until you have decoded all data. *)
val eos : t -> bool

(** Get all decodable tracks available. *)
val get_tracks : t -> track list

(** Get the first available audio and
  * video tracks and drop the other one. *)
val get_standard_tracks : t -> standard_tracks

(** Update a given record of standard tracks. You should
  * use this after a [reset] to update the standard tracks
  * with the newly created tracks. *)
val update_standard_tracks : t -> standard_tracks -> unit

(** Remove all tracks of the given type. *)
val drop_track : t -> track -> unit

(** {3 Information functions} *)

(** Get informations about the
  * audio track. *)
val audio_info : t -> track -> audio_info * metadata

(** Get informations about the
  * video track. *)
val video_info : t -> track -> video_info * metadata

(** Get the sample_rate of the track
  * of that type. Returns a pair [(numerator,denominator)]. *)
val sample_rate : t -> track -> (int*int)

(** Get track absolute position. *)
val get_track_position : t -> track -> float

(** Get absolute position in the stream. *)
val get_position : t -> float

(** {3 Seeking functions} *)

(** Returns [true] if the decoder
  * can be used with the [seek] function. *)
val can_seek : t -> bool

(** Seek to an absolute or relative position in seconds.
  *
  * Raises [Not_available] if seeking is
  * not possible.
  *
  * Raises [End_of_stream] if the end of
  * current stream has been reached while
  * seeking. You may call [reset] in this
  * situation to see if there is a new seqentialized
  * stream available. 
  * 
  * Returns the time actually reached, either in 
  * relative time or absolute time. *)
val seek : ?relative:bool -> t -> float -> float

(** {3 Decoding functions} *)

(** Decode audio data, if possible. 
  * Decoded data is passed to the second argument. 
  *
  * Raises [End_of_stream] if all stream have ended.
  * In this case, you can try [reset] to see if there is a
  * new sequentialized stream. *)
val decode_audio : t -> track -> (audio_data -> unit) -> unit

(** Decode video data, if possible. 
  * Decoded data is passed to the second argument. 
  *
  * Raises [End_of_stream] if all streams have ended.
  * In this case, you can try [reset] to see if there is a
  * new sequentialized stream. *)
val decode_video : t -> track -> (video_data -> unit) -> unit 

(** {2 Implementing decoders} *)

(** {3 Types } *)

(** Generic type for a decoder. *)
type ('a,'b) decoder =
  {
    name: string;
    info : unit -> 'a*metadata;
    decode : ('b -> unit) -> unit ;
    restart : Ogg.Stream.t -> unit;
    (** This function is called after seeking
      * to notify the decoder of the new [Ogg.Stream.t]
      * that is should use to pull data packets. *)
    samples_of_granulepos : Int64.t -> Int64.t
  }

(** Type for a generic logical stream decoder. *)
type decoders =
    | Video of (video_info,video_data) decoder
    | Audio of (audio_info,audio_data) decoder
    | Unknown

(** Type used to register a new decoder. First
  * element is a function used to check if the initial [Ogg.Stream.packet]
  * of an [Ogg.Stream.t] matches the format decodable by this decoder.
  * Second element is a function that instanciates the actual decoder
  * using the initial [Ogg.Stream.t] used to pull data packets for the
  * decoder. *)
type register_decoder = (Ogg.Stream.packet -> bool) * (Ogg.Stream.t -> decoders)

(** {3 Functions} *)

(** Register a new decoder. *)
val ogg_decoders : (string,register_decoder) Hashtbl.t
