The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Ogg::Theora::LibTheora - XS Interface for calling Theora Video Codec functions in Perl.

DESCRIPTION

Ogg::Theora::LibTheora is a glue between theora/theora.h theora/theoraenc.h and theora/theoradec.h . Please read the XS code to understand the glue implementation.

SYNOPSIS ENCODE

Encoding raw RGB files to create a theora video file.

  use Ogg::Theora::LibTheora;
  use Ogg::LibOgg ':all';

  my $op = make_ogg_packet();
  my $og = make_ogg_page();
  my $os = make_ogg_stream_state();
  my $oy = make_ogg_sync_state();

  ogg_sync_init($oy); ## should be == 0
  ogg_stream_init($os, 10101); ## should be == 0 and 10101 is a random serial number

  #########################################################################################################
  # (1) Fill in a th_info structure with details on the format of the video you wish to encode.           #
  # (2) Allocate a th_enc_ctx handle with th_encode_alloc().                                              #
  # (3) Perform any additional encoder configuration required with th_encode_ctl().                       #
  # (4) Repeatedly call th_encode_flushheader() to retrieve all the header packets.                       #
  # (5) For each uncompressed frame:                                                                      #
  #        (5.a) Submit the uncompressed frame via th_encode_ycbcr_in()                                   #
  #        (5.b) Repeatedly call th_encode_packetout() to retrieve any video data packets that are ready. #
  # (6) Call th_encode_free() to release all encoder memory.                                              #
  #########################################################################################################

  my $th_setup_info_addr = 0;
  my $th_info = Ogg::Theora::LibTheora::make_th_info();
  Ogg::Theora::LibTheora::th_info_init($th_info);

  my $w = 320;                  # width
  my $h = 240;                  # height

  Ogg::Theora::LibTheora::set_th_info($th_info, {'frame_width' => $w, 'frame_height' => $h});

  my $th_comment = Ogg::Theora::LibTheora::make_th_comment();
  Ogg::Theora::LibTheora::th_comment_init($th_comment);
  Ogg::Theora::LibTheora::th_comment_add($th_comment, "title=test video");
  Ogg::Theora::LibTheora::th_comment_init($th_comment);

  my $filename = "t/theora_encode.ogg";
  open OUT, ">", "$filename" or die "can't open $filename for writing [$!]";
  binmode OUT;

  my $th_enc_ctx = Ogg::Theora::LibTheora::th_encode_alloc($th_info);

  my $status = 1;
  do {
    $status = Ogg::Theora::LibTheora::th_encode_flushheader($th_enc_ctx, $th_comment, $op);
    if ($status > 0) {
      ogg_stream_packetin($os, $op) == 0 or warn "ogg_stream_packetin returned -1\n"
    } elsif ($status == Ogg::Theora::LibTheora::TH_EFAULT) {
      warn "TH_EFAULT\n"
    }
  } while ($status != 0);

  save_page();

  foreach ((1..5)) {
    add_image("t/enc_pic1.raw");  ## raw files are raw RGB data files
    add_image("t/enc_pic2.raw");
    add_image("t/enc_pic3.raw");
  }

  ogg_stream_flush($os, $og);

  Ogg::Theora::LibTheora::th_encode_free($th_enc_ctx);

  sub save_page {
    ## forms packets to pages 
    if (ogg_stream_pageout($os, $og) != 0) {
      my $h_page = get_ogg_page($og);
      ## writes the header and body 
    } else {
      # pass, we don't have to worry about insufficient data
    }
  }
  
  sub add_image {
    my ($name) = shift;
    open IN, "$name" or die "can't open [$name] $!\n";
    binmode IN;
    local $/ = undef;
    my $str = <IN>;
    close IN;
  
    Ogg::Theora::LibTheora::rgb_th_encode_ycbcr_in($th_enc_ctx, $str, $w, $h) == 0 or warn ("Error th_encode_ycbcr_in");
  
    my $n;
    do {
      $n = Ogg::Theora::LibTheora::th_encode_packetout($th_enc_ctx, 0, $op);
      $n == TH_EFAULT and warn ("($n) TH_EFAULT th_encode_packetout");
    } while (0);
  
    ogg_stream_packetin($os, $op) == 0 or warn ("Internal Error 'ogg_stream_packetin");
  
    save_page();
  }

SYNOPSIS DECODE

Decoding a theora video file to generate the raw RGB files. (here we generate only 1 raw file)

  use strict;
  use Ogg::LibOgg ':all';

  use Ogg::Theora::LibTheora;


  ## Make Ogg Structures
  my $op = make_ogg_packet();
  my $og = make_ogg_page();
  my $os = make_ogg_stream_state();
  my $oy = make_ogg_sync_state();

  my $filename = "t/theora.ogg";
  open IN, $filename or die "can't open [$filename] : $!";

  ## Ogg Sync Init
  ogg_sync_init($oy);

  ## read a page (wrapper for ogg_sync_pageout)
  ogg_read_page(*IN, $oy, $og);

  my $slno = ogg_page_serialno($og);

  ## Initializes the Ogg Stream State struct
  ogg_stream_init($os, $slno);

  ## add complete page to the bitstream, o create a valid ogg_page struct
  ## after calling ogg_sync_pageout (read_page does ogg_sync_pageout)
  ogg_stream_pagein($os, $og);

  my $th_comment = Ogg::Theora::LibTheora::make_th_comment();
  Ogg::Theora::LibTheora::th_comment_init($th_comment);
  my $th_info = Ogg::Theora::LibTheora::make_th_info();
  Ogg::Theora::LibTheora::th_info_init($th_info);


  ###############################################################################################
  # (1) Parse the header packets by repeatedly calling th_decode_headerin().                    #
  # (2) Allocate a th_dec_ctx handle with th_decode_alloc().                                    #
  # (3) Call th_setup_free() to free any memory used for codec setup information.               #
  # (4) Perform any additional decoder configuration with th_decode_ctl().                      #
  # (5) For each video data packet:                                                             #
  #     (5.a) Submit the packet to the decoder via th_decode_packetin().                        #
  #     (5.b) Retrieve the uncompressed video data via th_decode_ycbcr_out().                   #
  # (6) Call th_decode_free() to release all decoder memory.                                    #
  ###############################################################################################

  ## Decode Header and parse the stream till the first VIDEO packet gets in
  my $th_setup_info_addr = 0;
  my $ret = undef;
  Ogg::Theora::LibTheora::th_packet_isheader($op);
  Ogg::Theora::LibTheora::th_packet_iskeyframe($op);

  do {
    ($ret, $th_setup_info_addr) = Ogg::Theora::LibTheora::th_decode_headerin($th_info, $th_comment, $th_setup_info_addr, $op);
    ## $ret > 0 indicates that a Theora header was successfully processed.
    readPacket() if $ret != 0;
  } while ($ret != 0); ## ret == 0 means, first video data packet was encountered

  ## th_decode_alloc
  my $th_dec_ctx = Ogg::Theora::LibTheora::th_decode_alloc($th_info, $th_setup_info_addr);

  ## th_setup_free
  Ogg::Theora::LibTheora::th_setup_free($th_setup_info_addr);

  ## Make th_ycbcr_buffer
  my $th_ycbcr_buffer = Ogg::Theora::LibTheora::make_th_ycbcr_buffer();


  ## th_decode_packetin
  my $gpos = 0;
  $ret = undef;
  ($ret, $gpos) = Ogg::Theora::LibTheora::th_decode_packetin($th_dec_ctx, $op, $gpos);

  ## th_decode_ycbcr_out
  Ogg::Theora::LibTheora::th_decode_ycbcr_out($th_dec_ctx, $th_ycbcr_buffer);

  my $rgb_buf = Ogg::Theora::LibTheora::ycbcr_to_rgb_buffer($th_ycbcr_buffer);

  open OUT, ">", "t/dec_pic1.raw" or diag( "can't open $!");
  binmode OUT;
  print OUT $rgb_buf;
  close OUT;

  ## th_decode_free
  Ogg::Theora::LibTheora::th_decode_free($th_dec_ctx);

  Ogg::Theora::LibTheora::th_info_clear($th_info);

  close IN;


  sub readPacket {
    while (ogg_stream_packetout($os, $op) == 0) {
      if (not defined ogg_read_page(*IN, $oy, $og)) {
        return undef
      }
      ogg_stream_pagein($os, $og);
    }
  }

EXPORT

Only constants are exported by DEFAULT

  use Ogg::Theora::LibTheora ':all'; # to export everything to current namespace

Exportable constants

  OC_BADHEADER
  OC_BADPACKET
  OC_CS_ITU_REC_470BG
  OC_CS_ITU_REC_470M
  OC_CS_NSPACES
  OC_CS_UNSPECIFIED
  OC_DISABLED
  OC_DUPFRAME
  OC_EINVAL
  OC_FAULT
  OC_IMPL
  OC_NEWPACKET
  OC_NOTFORMAT
  OC_PF_420
  OC_PF_422
  OC_PF_444
  OC_PF_RSVD
  OC_VERSION
  TH_CS_ITU_REC_470BG
  TH_CS_ITU_REC_470M
  TH_CS_NSPACES
  TH_CS_UNSPECIFIED
  TH_DECCTL_GET_PPLEVEL_MAX
  TH_DECCTL_SET_GRANPOS
  TH_DECCTL_SET_PPLEVEL
  TH_DECCTL_SET_STRIPE_CB
  TH_DECCTL_SET_TELEMETRY_BITS
  TH_DECCTL_SET_TELEMETRY_MBMODE
  TH_DECCTL_SET_TELEMETRY_MV
  TH_DECCTL_SET_TELEMETRY_QI
  TH_DUPFRAME
  TH_EBADHEADER
  TH_EBADPACKET
  TH_EFAULT
  TH_EIMPL
  TH_EINVAL
  TH_ENCCTL_2PASS_IN
  TH_ENCCTL_2PASS_OUT
  TH_ENCCTL_GET_SPLEVEL
  TH_ENCCTL_GET_SPLEVEL_MAX
  TH_ENCCTL_SET_BITRATE
  TH_ENCCTL_SET_DUP_COUNT
  TH_ENCCTL_SET_HUFFMAN_CODES
  TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE
  TH_ENCCTL_SET_QUALITY
  TH_ENCCTL_SET_QUANT_PARAMS
  TH_ENCCTL_SET_RATE_BUFFER
  TH_ENCCTL_SET_RATE_FLAGS
  TH_ENCCTL_SET_SPLEVEL
  TH_ENCCTL_SET_VP3_COMPATIBLE
  TH_ENOTFORMAT
  TH_EVERSION
  TH_NDCT_TOKENS
  TH_NHUFFMAN_TABLES
  TH_PF_420
  TH_PF_422
  TH_PF_444
  TH_PF_NFORMATS
  TH_PF_RSVD
  TH_RATECTL_CAP_OVERFLOW
  TH_RATECTL_CAP_UNDERFLOW
  TH_RATECTL_DROP_FRAMES

Functions (malloc)

http://www.theora.org/doc/libtheora-1.0/annotated.html

make_th_info

Creates a memory allocation for th_info.

-Input: Void

-Output: Memory Pointer

make_th_huff_code

Creates a memory allocation for th_huff_code.

-Input: void

-Output: Memory Pointer

make_th_img_plane

Creates a memory allocation for th_img_plane.

-Input: void

-Output: Memory Pointer

make_th_quant_info

Creates a memory allocation for th_quant_info.

-Input: void

-Output: Memory Pointer

make_th_quant_ranges

Creates a memory allocation for th_quant_ranges.

-Input: void

-Output: Memory Pointer

make_th_stripe_callback

Creates a memory allocation for th_stripe_callback.

-Input: void

-Output: Memory Pointer

make_th_ycbcr_buffer

Creates a memory allocation for th_ycbcr_buffer.

-Input: void

-Output: Memory Pointer

make_th_comment

Creates a memory allocation for th_comment.

-Input: void

-Output: Memory Pointer

Functions (Basic shared functions)

http://www.theora.org/doc/libtheora-1.0/group__basefuncs.html

th_version_number

Retrieves the library version number.

-Input: void

-Output: ogg_uint32_t (IV)

th_version_string

Retrieves a human-readable string to identify the library vendor and version.

-Input: void

-Output: const char * (T_PV)

th_packet_isheader

Determines whether a Theora packet is a header or not.

-Input: _op An ogg_packet containing encoded Theora data.

-Output: 1 packet is a header packet, 0 packet is a video data packet.

th_granule_frame

Converts a granule position to an absolute frame index, starting at 0.

-Input: void * _encdec (previously allocated th_enc_ctx or th_dec_ctx handle), ogg_int64_t _granpos (granule position to convert).

-Output: absolute frame index corresponding to _granpos, -1 on error.

th_granule_time

Converts a granule position to an absolute time in seconds.

-Input: void * _encdec (previously allocated th_enc_ctx or th_dec_ctx handle), ogg_int64_t _granpos (granule position to convert).

-Output: absolute time in seconds corresponding to _granpos, -1 on error.

th_packet_iskeyframe

Determines whether a theora packet is a key frame or not.

-Input: _op An ogg_packet containing encoded Theora data.

-Output: 1 packet is a key frame, 0 packet is a delta frame, -1 packet is not a video data packet.

Functions (Manipulating Header Data)

th_comment_init

Initialize a th_comment structure.

-Input: th_comment *

-Output: void

th_info_init

Initializes a th_info structure.

-Input: th_info

-Output: void

th_info_clear

Clears a th_info structure.

-Input: th_info

-Output: void

th_comment_add

Add a comment to an initialized th_comment structure.

-Input: th_comment, char * (null-terminated UTF-8 string containing the comment in "TAG=the value" form).

-Output: void

th_comment_add_tag

Add a comment to an initialized th_comment structure.

-Input: th_comment, char * (null-terminated string containing the tag associated with the comment), char * (corresponding value as a null-terminated string).

th_comment_query_count

Look up the number of instances of a tag.

-Input: th_comment, char * (tag to look up).

-Output: int (number on instances of this particular tag)

th_comment_query

Look up a comment value by its tag.

-Input: th_comment, char * (tag to look-up) int (instance of the tag, it starts from 0)

-Output: char * if matched pointer to the queried tag's value, NULL if no matching tag is found

Functions (For Decoding)

http://www.theora.org/doc/libtheora-1.0/group__decfuncs.html

th_decode_headerin

Decodes the header packets of a Theora stream.

-Input: th_info, th_comment, th_setup_info, (initialized to NULL on the first call & returned value be passed on subsequent calls) ogg_packet

-Output: 0 first video data packet was encountered after all required header packets were parsed, TH_EFAULT if one of _info, _tc, or _setup was NULL, TH_EBADHEADER _op was NULL, TH_EVERSION not decodable with current libtheoradec version, TH_ENOTFORMAT not a Theora header

th_decode_alloc

Allocates a decoder instance.

-Input: th_info, th_setup_info

-Output: th_dec_ctx

th_setup_free

Releases all storage used for the decoder setup information.

-Input: th_setup_info

-Output: void

th_decode_packetin

Submits a packet containing encoded video data to the decoder.

-Input: th_dec_ctx, ogg_packet, ogg_int64_t gran_pos, returns the granule position of the decoded packet

-Output: 0 success, TH_DUPFRAME packet represented a dropped (0-byte) frame, TH_EFAULT _dec or _op was NULL, TH_EBADPACKET _op does not contain encoded video data, TH_EIMPL video data uses bitstream features which this library does not support.

th_decode_ycbcr_out

Outputs the next available frame of decoded Y'CbCr data.

-Input: th_dec_ctx, th_ycbcr_buffer (video buffer structure to fill in)

-Output: 0 Success

th_decode_free

Frees an allocated decoder instance.

-Input: th_dec_ctx

-Output: void

th_decode_ctl

Decoder control function. (i haven't tested this)

-Input: th_dec_ctx, int _req (control code to process), void * _buf (parameters for this control code), size_t _buf_sz (size of the parameter buffer)

-Output: int (not documented)

Functions (for Encoding)

http://www.theora.org/doc/libtheora-1.0/group__encfuncs.html

th_encode_alloc

Allocates an encoder instance.

-Input: th_info.

-Output: th_enc_ctx handle, NULL (if the encoding parameters were invalid).

th_encode_flushheader

-Input: th_enc_ctx, th_comment, ogg_packet.

-Output: > 1 (indicates that a header packet was successfully produced), 0 (no packet was produced, and no more header packets remain), TH_EFAULT (_enc, _comments, or _op was NULL).

th_encode_ycbcr_in

Submits an uncompressed frame to the encoder. (if you don't have ycbcr buffer you can try using the *unoptimized* rgb_th_encode_ycbcr_in, better you write your own).

-Input: th_enc_ctx, th_ycbcr_buffer

-Output: 0 Success, TH_EFAULT _enc or _ycbcr is NULL, TH_EINVAL buffer size does not match the frame size encoder was initialized.

th_encode_packetout

Retrieves encoded video data packets.

-Input: th_enc_ctx, int (non-zero value if no more uncompressed frames will be submitted), ogg_packet.

-Output: > 0 a video data packet was successfully produced, 0 no packet was produced, and no more encoded video data remains, TH_EFAULT _enc or _op was NULL.

th_encode_free

Frees an allocated encoder instance.

-Input: th_enc_ctx

-Output: void

Miscellaneous Functions

These functions are not found in libtheora*, but is written by the XS author to simplify few tasks.

get_th_info

Returns a HashRef with th_info struct values.

-Input: th_info

-Output: HashRef

ycbcr_to_rgb_buffer

reads the data from the ycbcr buffer and converts to its equivalent rgb buffer. (this is NOT an optimized code, there will be better ycbcr to rgb convertors, some intel gpu processors have mnemonic that does the conversion)

-Input: th_ycbcr_buffer

-Output: RGB string

get_th_comment

return an array of comments

-Input: th_comment

-Output: array of comments

set_th_info

sets the th_info structure to default values unless specified in hash. frame_width and frame_height is mandatory.

-Input: Hash of elements

-Output: void

rgb_th_encode_ycbcr_in

Converts a rgb to ycbcr buffer. (this is not an optimized code)

-Input: th_enc_ctx char * (rgb string), width, height.

-Output: th_ycbcr_buffer

get_th_ycbcr_buffer_info

Returns an arrayref of hashrefs containing width, height, stride and data_pointer for each plane (issue#1)

-Input: th_ycbcr_buffer

-Output: arrayref

get_th_ycbcr_buffer_ptr

Returns an data pointer for specified plane index (0 - Y, 1 - Cb, 2 - Cr)

-Input: th_ycbcr_buffer index

-Output: pointer

get_th_ycbcr_buffer_data

Returns an data for specified plane index (0 - Y, 1 - Cb, 2 - Cr)

-Input: th_ycbcr_buffer index

-Output: string - use unpack to get numbers

CAVEATS

This Modules expects the Theora file to be contained in an Ogg container (which true for most of the theora videos at the time of writing this module). Few of the miscellaneous functions like rgb_th_encode_ycbcr_in, ycbcr_to_rgb_buffer are not optimized. This module seems to give Segmentation Fault if the version of libtheora is pre-1.0. In my system (Mac OS X, 10.5.8) when i wrote this module, I was using libtheora @1.1.1_0 (active) and libogg @1.1.4_0 (active).

SEE ALSO

Ogg::LibOgg, Ogg::LibOgg

Theora Documentation, http://www.theora.org/doc/libtheora-1.0/

You can find the code for this module and few examples at https://github.com/vigith/Ogg-Theora-LibTheora

AUTHOR

Vigith Maurice, <vigith@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2011,2012 by Vigith Maurice, www.vigith.com <vigith@cpan.com>

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.9 or, at your option, any later version of Perl 5 you may have available.