Updated libtheora from 1.1 to 1.1.1, we kept the CMakeLists.txt

--HG--
branch : update-to-libtheora-1.1.1
This commit is contained in:
EntranceJew
2016-02-12 11:29:23 -05:00
parent cda9f55e33
commit be26fc29ae
340 changed files with 125491 additions and 51558 deletions
@@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#else
typedef long DWORD;
#endif
/*extremely crude app to dump vp3 frames from an avi file*/
/*filenames are hardcoded*/
#include "avilib.h"
int main(int argc, const char **argv)
{
FILE * f = fopen("outfile.vp3", "wb");
char * buffer;
int olength;
int length;
avi_t *avifile;
int chunksize;
int frame;
int frames;
int keyframegap = 0;
int maxkeyframegap = 0;
DWORD initialticks;
int framew = 0;
int frameh = 0;
double framerate = 0.0f;
double fps_numerator, fps_denominator;
avifile = AVI_open_input_file("vp31.avi", 1);
frames = AVI_video_frames(avifile);
framew = AVI_video_width(avifile);
frameh = AVI_video_height(avifile);
framerate = AVI_frame_rate(avifile);
chunksize = AVI_max_video_chunk(avifile);
/* avilib only reports the max video chunk size if the file has an
idx table. We fall back to an arbitrary limit otherwise. Better
would be just to handle the chunks dynamically */
if (chunksize <= 0) chunksize = 131072;
buffer = malloc(chunksize);
printf("Frames(%d) Video(%dx%d) %3.2f fps\n",frames,framew, frameh,framerate);
printf("Video Compressor: %s", AVI_video_compressor(avifile));
fps_denominator = 1000000.0F;
fps_numerator = framerate * fps_denominator;
sprintf(buffer,"AVI2VP31R W%d H%d F%.0f:%.0f Ip A0:0\n", framew, frameh, fps_numerator, fps_denominator);
fwrite(buffer, strlen(buffer), 1, f);
for (frame = 0; frame < frames;) {
int keyframe;
olength = length;
length = AVI_frame_size(avifile, frame++);
if( !length ) {
length = olength;
}
AVI_read_frame(avifile, (char *) buffer, &keyframe);
fwrite("FRAME\n", 6, 1, f);
fwrite(&length, sizeof(int), 1, f);
fwrite(&keyframe, sizeof(int), 1, f);
printf("Frame size(%d) IsKeyframe(%d)\n", length, keyframe);
fwrite(buffer, 1, length, f);
if (!keyframe){
keyframegap++;
} else {
if (keyframegap>maxkeyframegap) maxkeyframegap=keyframegap;
keyframegap = 0;
}
}
fclose(f);
printf("Max keyframegap (%d)\n", maxkeyframegap);
free(buffer);
exit(0);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,309 @@
/*
* avilib.h
*
* Copyright (C) Thomas Östreich - June 2001
* multiple audio track support Copyright (C) 2002 Thomas Östreich
*
* Original code:
* Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
*
* This file is part of transcode, a linux video stream processing tool
*
* transcode 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, or (at your option)
* any later version.
*
* transcode 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.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifndef AVILIB_H
#define AVILIB_H
#define AVI_MAX_TRACKS 8
typedef struct
{
unsigned long key;
unsigned long pos;
unsigned long len;
} video_index_entry;
typedef struct
{
unsigned long pos;
unsigned long len;
unsigned long tot;
} audio_index_entry;
typedef struct track_s
{
long a_fmt; /* Audio format, see #defines below */
long a_chans; /* Audio channels, 0 for no audio */
long a_rate; /* Rate in Hz */
long a_bits; /* bits per audio sample */
long mp3rate; /* mp3 bitrate kbs*/
long audio_strn; /* Audio stream number */
long audio_bytes; /* Total number of bytes of audio data */
long audio_chunks; /* Chunks of audio data in the file */
char audio_tag[4]; /* Tag of audio data */
long audio_posc; /* Audio position: chunk */
long audio_posb; /* Audio position: byte within chunk */
long a_codech_off; /* absolut offset of audio codec information */
long a_codecf_off; /* absolut offset of audio codec information */
audio_index_entry *audio_index;
} track_t;
typedef struct
{
long fdes; /* File descriptor of AVI file */
long mode; /* 0 for reading, 1 for writing */
long width; /* Width of a video frame */
long height; /* Height of a video frame */
double fps; /* Frames per second */
char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */
char compressor2[8]; /* Type of compressor, 4 bytes + padding for 0 byte */
long video_strn; /* Video stream number */
long video_frames; /* Number of video frames */
char video_tag[4]; /* Tag of video data */
long video_pos; /* Number of next frame to be read
(if index present) */
unsigned long max_len; /* maximum video chunk present */
track_t track[AVI_MAX_TRACKS]; // up to AVI_MAX_TRACKS audio tracks supported
unsigned long pos; /* position in file */
long n_idx; /* number of index entries actually filled */
long max_idx; /* number of index entries actually allocated */
long v_codech_off; /* absolut offset of video codec (strh) info */
long v_codecf_off; /* absolut offset of video codec (strf) info */
unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */
video_index_entry *video_index;
unsigned long last_pos; /* Position of last frame written */
unsigned long last_len; /* Length of last frame written */
int must_use_index; /* Flag if frames are duplicated */
unsigned long movi_start;
int anum; // total number of audio tracks
int aptr; // current audio working track
} avi_t;
#define AVI_MODE_WRITE 0
#define AVI_MODE_READ 1
/* The error codes delivered by avi_open_input_file */
#define AVI_ERR_SIZELIM 1 /* The write of the data would exceed
the maximum size of the AVI file.
This is more a warning than an error
since the file may be closed safely */
#define AVI_ERR_OPEN 2 /* Error opening the AVI file - wrong path
name or file nor readable/writable */
#define AVI_ERR_READ 3 /* Error reading from AVI File */
#define AVI_ERR_WRITE 4 /* Error writing to AVI File,
disk full ??? */
#define AVI_ERR_WRITE_INDEX 5 /* Could not write index to AVI file
during close, file may still be
usable */
#define AVI_ERR_CLOSE 6 /* Could not write header to AVI file
or not truncate the file during close,
file is most probably corrupted */
#define AVI_ERR_NOT_PERM 7 /* Operation not permitted:
trying to read from a file open
for writing or vice versa */
#define AVI_ERR_NO_MEM 8 /* malloc failed */
#define AVI_ERR_NO_AVI 9 /* Not an AVI file */
#define AVI_ERR_NO_HDRL 10 /* AVI file has no has no header list,
corrupted ??? */
#define AVI_ERR_NO_MOVI 11 /* AVI file has no has no MOVI list,
corrupted ??? */
#define AVI_ERR_NO_VIDS 12 /* AVI file contains no video data */
#define AVI_ERR_NO_IDX 13 /* The file has been opened with
getIndex==0, but an operation has been
performed that needs an index */
/* Possible Audio formats */
#ifndef WAVE_FORMAT_PCM
#define WAVE_FORMAT_UNKNOWN (0x0000)
#define WAVE_FORMAT_PCM (0x0001)
#define WAVE_FORMAT_ADPCM (0x0002)
#define WAVE_FORMAT_IBM_CVSD (0x0005)
#define WAVE_FORMAT_ALAW (0x0006)
#define WAVE_FORMAT_MULAW (0x0007)
#define WAVE_FORMAT_OKI_ADPCM (0x0010)
#define WAVE_FORMAT_DVI_ADPCM (0x0011)
#define WAVE_FORMAT_DIGISTD (0x0015)
#define WAVE_FORMAT_DIGIFIX (0x0016)
#define WAVE_FORMAT_YAMAHA_ADPCM (0x0020)
#define WAVE_FORMAT_DSP_TRUESPEECH (0x0022)
#define WAVE_FORMAT_GSM610 (0x0031)
#define IBM_FORMAT_MULAW (0x0101)
#define IBM_FORMAT_ALAW (0x0102)
#define IBM_FORMAT_ADPCM (0x0103)
#endif
avi_t* AVI_open_output_file(char * filename);
void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor);
void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate);
int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe);
int AVI_dup_frame(avi_t *AVI);
int AVI_write_audio(avi_t *AVI, char *data, long bytes);
int AVI_append_audio(avi_t *AVI, char *data, long bytes);
long AVI_bytes_remain(avi_t *AVI);
int AVI_close(avi_t *AVI);
long AVI_bytes_written(avi_t *AVI);
avi_t *AVI_open_input_file(char *filename, int getIndex);
avi_t *AVI_open_fd(int fd, int getIndex);
int avi_parse_input_file(avi_t *AVI, int getIndex);
long AVI_audio_mp3rate(avi_t *AVI);
long AVI_video_frames(avi_t *AVI);
int AVI_video_width(avi_t *AVI);
int AVI_video_height(avi_t *AVI);
double AVI_frame_rate(avi_t *AVI);
char* AVI_video_compressor(avi_t *AVI);
int AVI_audio_channels(avi_t *AVI);
int AVI_audio_bits(avi_t *AVI);
int AVI_audio_format(avi_t *AVI);
long AVI_audio_rate(avi_t *AVI);
long AVI_audio_bytes(avi_t *AVI);
long AVI_audio_chunks(avi_t *AVI);
long AVI_max_video_chunk(avi_t *AVI);
long AVI_frame_size(avi_t *AVI, long frame);
long AVI_audio_size(avi_t *AVI, long frame);
int AVI_seek_start(avi_t *AVI);
int AVI_set_video_position(avi_t *AVI, long frame);
long AVI_get_video_position(avi_t *AVI, long frame);
long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe);
int AVI_set_audio_position(avi_t *AVI, long byte);
int AVI_set_audio_bitrate(avi_t *AVI, long bitrate);
long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes);
long AVI_audio_codech_offset(avi_t *AVI);
long AVI_audio_codecf_offset(avi_t *AVI);
long AVI_video_codech_offset(avi_t *AVI);
long AVI_video_codecf_offset(avi_t *AVI);
int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf,
char *audbuf, long max_audbuf,
long *len);
void AVI_print_error(char *str);
char *AVI_strerror();
char *AVI_syserror();
int AVI_scan(char *name);
int AVI_dump(char *name, int mode);
char *AVI_codec2str(short cc);
int AVI_file_check(char *import_file);
void AVI_info(avi_t *avifile);
uint64_t AVI_max_size();
int avi_update_header(avi_t *AVI);
int AVI_set_audio_track(avi_t *AVI, int track);
int AVI_get_audio_track(avi_t *AVI);
int AVI_audio_tracks(avi_t *AVI);
struct riff_struct
{
unsigned char id[4]; /* RIFF */
unsigned long len;
unsigned char wave_id[4]; /* WAVE */
};
struct chunk_struct
{
unsigned char id[4];
unsigned long len;
};
struct common_struct
{
unsigned short wFormatTag;
unsigned short wChannels;
unsigned long dwSamplesPerSec;
unsigned long dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample; /* Only for PCM */
};
struct wave_header
{
struct riff_struct riff;
struct chunk_struct format;
struct common_struct common;
struct chunk_struct data;
};
struct AVIStreamHeader {
long fccType;
long fccHandler;
long dwFlags;
long dwPriority;
long dwInitialFrames;
long dwScale;
long dwRate;
long dwStart;
long dwLength;
long dwSuggestedBufferSize;
long dwQuality;
long dwSampleSize;
};
#endif
@@ -0,0 +1,17 @@
Quick hack at a transcoder tool from VP3 to Theora
I actually built the avi2vp3 tool with codeWarrior, but it should compile under
VC as well. I have included a source avi file and the converted .vp3 output.
Output is a file with some header info matching YUVMPEG, and for each frame:
FRAME header block matching YUV2MPEG
long (Intel aligned) keyframeflag describing in frame is a keyframe
long (Intel aligned) fsize storing frame size in bytes
bytes[fsize] with binary frame data
The transcode tool is a modification of the current encoder. PUt it into the
win32/experimental subdirectory, and the paths should be correct. It produces
an apparently valid theora stream, but outputs garbage data. The code is packing
the binary frame data in a way that SHOULD work at least imo, but I am probably
missing some initialization issue (or vp3 is not transcodable to theora).
@@ -0,0 +1,121 @@
# Microsoft Developer Studio Project File - Name="transcoder" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=transcoder - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "transcoder.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "transcoder.mak" CFG="transcoder - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "transcoder - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "transcoder - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "transcoder - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\include" /I "..\..\..\..\vorbis\include" /I "..\..\..\..\ogg\include" /I "..\wincompat" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D GETOPT_API= /FD /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib theora_static.lib ogg_static.lib vorbis_static.lib vorbisenc_static.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"LIBCMT" /out:"transcoder.exe" /libpath:"..\..\Static_Release" /libpath:"..\..\..\..\ogg\win32\Static_Release" /libpath:"..\..\..\..\vorbis\win32\Vorbis_Static_Release" /libpath:"..\..\..\..\vorbis\win32\VorbisEnc_Static_Release"
!ELSEIF "$(CFG)" == "transcoder - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\..\include" /I "..\..\..\..\vorbis\include" /I "..\..\..\..\ogg\include" /I "..\wincompat" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D GETOPT_API= /FD /GZ /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib theora_static_d.lib ogg_static_d.lib vorbis_static_d.lib vorbisenc_static_d.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"LIBCD" /out:"transcoder.exe" /pdbtype:sept /libpath:"..\..\Static_Debug" /libpath:"..\..\..\..\ogg\win32\Static_Debug" /libpath:"..\..\..\..\vorbis\win32\Vorbis_Static_Debug" /libpath:"..\..\..\..\vorbis\win32\VorbisEnc_Static_Debug"
# SUBTRACT LINK32 /nodefaultlib
!ENDIF
# Begin Target
# Name "transcoder - Win32 Release"
# Name "transcoder - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=..\wincompat\getopt.c
# End Source File
# Begin Source File
SOURCE=..\wincompat\getopt_long.c
# End Source File
# Begin Source File
SOURCE=.\transcoder_example.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# Begin Group "library"
# PROP Default_Filter ""
# End Group
# Begin Source File
SOURCE=.\ReadMe.txt
# End Source File
# End Target
# End Project
@@ -0,0 +1,926 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2004 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: example encoder application; makes an Ogg Theora/Vorbis
file from YUV4MPEG2 and WAV input
last mod: $Id: transcoder_example.c,v 1.4 2004/03/20 00:14:04 tterribe Exp $
********************************************************************/
#define _GNU_SOURCE
#define _REENTRANT
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include <math.h>
#include "theora/theora.h"
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"
#ifdef _WIN32
/*supply missing headers and functions to Win32. going to hell, I know*/
#include <io.h>
#include <fcntl.h>
static double rint(double x)
{
if (x < 0.0)
return (double)(int)(x - 0.5);
else
return (double)(int)(x + 0.5);
}
#endif
/*Copied from vorbis/sharedbook.c*/
static int _ilog(unsigned int v){
int ret=0;
while(v){
ret++;
v>>=1;
}
return(ret);
}
const char *optstring = "o:a:A:v:V:";
struct option options [] = {
{"output",required_argument,NULL,'o'},
{"audio-rate-target",required_argument,NULL,'A'},
{"video-rate-target",required_argument,NULL,'V'},
{"audio-quality",required_argument,NULL,'a'},
{"video-quality",required_argument,NULL,'v'},
{NULL,0,NULL,0}
};
typedef struct TC_INSTANCE {
ogg_uint32_t LastKeyFrame ;
ogg_int64_t KeyFrameCount;
int ThisIsFirstFrame;
int ThisIsKeyFrame;
ogg_uint32_t CurrentFrame;
ogg_int64_t granulepos;
int keyframe_granule_shift;
char * in_bytes;
long in_bytecount;
ogg_uint32_t fps_denominator;
ogg_uint32_t fps_numerator;
oggpack_buffer opb_in;
oggpack_buffer opb_out;
} TC_INSTANCE;
/* You'll go to Hell for using globals. */
FILE *audio=NULL;
FILE *video=NULL;
int audio_ch=0;
int audio_hz=0;
float audio_q=.1;
int audio_r=-1;
int video_x=0;
int video_y=0;
int frame_x=0;
int frame_y=0;
int frame_x_offset=0;
int frame_y_offset=0;
int video_hzn=0;
int video_hzd=0;
int video_an=0;
int video_ad=0;
int video_r=-1;
int video_q=16;
char *vp3frame[2];
int framebytecount[2];
int frameiskey[2];
ogg_page audiopage;
ogg_page videopage;
static void usage(void){
fprintf(stderr,
"Usage: encoder_example [options] [audio_file] video_file\n\n"
"Options: \n\n"
" -o --output <filename.ogv> file name for encoded output;\n"
" If this option is not given, the\n"
" compressed data is sent to stdout.\n\n"
" -A --audio-rate-target <n> bitrate target for Vorbis audio;\n"
" use -a and not -A if at all possible,\n"
" as -a gives higher quality for a given\n"
" bitrate.\n\n"
" -V --video-rate-target <n> bitrate target for Theora video\n\n"
" -a --audio-quality <n> Vorbis quality selector from -1 to 10\n"
" (-1 yields smallest files but lowest\n"
" fidelity; 10 yields highest fidelity\n"
" but large files. '2' is a reasonable\n"
" default).\n\n"
" -v --video-quality <n> Theora quality selector fro 0 to 10\n"
" (0 yields smallest files but lowest\n"
" video quality. 10 yields highest\n"
" fidelity but large files).\n\n"
"encoder_example accepts only uncompressed RIFF WAV format audio and\n"
"YUV4MPEG2 uncompressed video.\n\n");
exit(1);
}
static void id_file(char *f){
FILE *test;
unsigned char buffer[80];
int ret;
/* open it, look for magic */
if(!strcmp(f,"-")){
/* stdin */
test=stdin;
}else{
test=fopen(f,"rb");
if(!test){
fprintf(stderr,"Unable to open file %s.\n",f);
exit(1);
}
}
ret=fread(buffer,1,4,test);
if(ret<4){
fprintf(stderr,"EOF determining file type of file %s.\n",f);
exit(1);
}
if(!memcmp(buffer,"RIFF",4)){
/* possible WAV file */
if(audio){
/* umm, we already have one */
fprintf(stderr,"Multiple RIFF WAVE files specified on command line.\n");
exit(1);
}
/* Parse the rest of the header */
ret=fread(buffer,1,4,test);
ret=fread(buffer,1,4,test);
if(ret<4)goto riff_err;
if(!memcmp(buffer,"WAVE",4)){
while(!feof(test)){
ret=fread(buffer,1,4,test);
if(ret<4)goto riff_err;
if(!memcmp("fmt",buffer,3)){
/* OK, this is our audio specs chunk. Slurp it up. */
ret=fread(buffer,1,20,test);
if(ret<20)goto riff_err;
if(memcmp(buffer+4,"\001\000",2)){
fprintf(stderr,"The WAV file %s is in a compressed format; "
"can't read it.\n",f);
exit(1);
}
audio=test;
audio_ch=buffer[6]+(buffer[7]<<8);
audio_hz=buffer[8]+(buffer[9]<<8)+
(buffer[10]<<16)+(buffer[11]<<24);
if(buffer[18]+(buffer[19]<<8)!=16){
fprintf(stderr,"Can only read 16 bit WAV files for now.\n");
exit(1);
}
/* Now, align things to the beginning of the data */
/* Look for 'dataxxxx' */
while(!feof(test)){
ret=fread(buffer,1,4,test);
if(ret<4)goto riff_err;
if(!memcmp("data",buffer,4)){
/* We're there. Ignore the declared size for now. */
ret=fread(buffer,1,4,test);
if(ret<4)goto riff_err;
fprintf(stderr,"File %s is 16 bit %d channel %d Hz RIFF WAV audio.\n",
f,audio_ch,audio_hz);
return;
}
}
}
}
}
fprintf(stderr,"Couldn't find WAVE data in RIFF file %s.\n",f);
exit(1);
}
if(!memcmp(buffer,"AVI2",4)){
/* possible AVI2VP31 format file */
/* read until newline, or 80 cols, whichever happens first */
int i;
for(i=0;i<79;i++){
ret=fread(buffer+i,1,1,test);
if(ret<1)goto yuv_err;
if(buffer[i]=='\n')break;
}
if(i==79){
fprintf(stderr,"Error parsing %s header; not a VP31 raw frames file?\n",f);
}
buffer[i]='\0';
if(!memcmp(buffer,"VP31",4)){
char interlace;
if(video){
/* umm, we already have one */
fprintf(stderr,"Multiple video files specified on command line.\n");
exit(1);
}
if(buffer[4]!='R'){
fprintf(stderr,"Incorrect file ; VP31 raw frames required.\n");
}
ret=sscanf(buffer,"VP31R W%d H%d F%d:%d I%c A%d:%d",
&frame_x,&frame_y,&video_hzn,&video_hzd,&interlace,
&video_an,&video_ad);
if(ret<7){
fprintf(stderr,"Error parsing AVI2VP31R header in file %s.\n",f);
exit(1);
}
if(interlace!='p'){
fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n");
exit(1);
}
video=test;
fprintf(stderr,"File %s is %dx%d %.02f fps VP31 video.\n",
f,frame_x,frame_y,(double)video_hzn/video_hzd);
return;
}
}
fprintf(stderr,"Input file %s is neither a WAV nor VP31 file.\n",f);
exit(1);
riff_err:
fprintf(stderr,"EOF parsing RIFF file %s.\n",f);
exit(1);
yuv_err:
fprintf(stderr,"EOF parsing VP31 file %s.\n",f);
exit(1);
}
int spinner=0;
char *spinascii="|/-\\";
void spinnit(void){
spinner++;
if(spinner==4)spinner=0;
fprintf(stderr,"\r%c",spinascii[spinner]);
}
int fetch_and_process_audio(FILE *audio,ogg_page *audiopage,
ogg_stream_state *vo,
vorbis_dsp_state *vd,
vorbis_block *vb,
int audioflag){
ogg_packet op;
int i,j;
while(audio && !audioflag){
/* process any audio already buffered */
spinnit();
if(ogg_stream_pageout(vo,audiopage)>0) return 1;
if(ogg_stream_eos(vo))return 0;
{
/* read and process more audio */
signed char readbuffer[4096];
int toread=4096/2/audio_ch;
int bytesread=fread(readbuffer,1,toread*2*audio_ch,audio);
int sampread=bytesread/2/audio_ch;
float **vorbis_buffer;
int count=0;
if(bytesread<=0){
/* end of file. this can be done implicitly, but it's
easier to see here in non-clever fashion. Tell the
library we're at end of stream so that it can handle the
last frame and mark end of stream in the output properly */
vorbis_analysis_wrote(vd,0);
}else{
vorbis_buffer=vorbis_analysis_buffer(vd,sampread);
/* uninterleave samples */
for(i=0;i<sampread;i++){
for(j=0;j<audio_ch;j++){
vorbis_buffer[j][i]=((readbuffer[count+1]<<8)|
(0x00ff&(int)readbuffer[count]))/32768.f;
count+=2;
}
}
vorbis_analysis_wrote(vd,sampread);
}
while(vorbis_analysis_blockout(vd,vb)==1){
/* analysis, assume we want to use bitrate management */
vorbis_analysis(vb,NULL);
vorbis_bitrate_addblock(vb);
/* weld packets into the bitstream */
while(vorbis_bitrate_flushpacket(vd,&op))
ogg_stream_packetin(vo,&op);
}
}
}
return audioflag;
}
int theora_transcode_packetout( TC_INSTANCE *ttc, int last_p, ogg_packet *op){
long bytes=ttc->in_bytecount;
if(!bytes)return(0);
op->packet=ttc->in_bytes;
op->bytes=bytes;
op->b_o_s=0;
op->e_o_s=last_p;
op->packetno=ttc->CurrentFrame;
op->granulepos=ttc->granulepos;
return 1;
}
void TranscodeKeyFrame(TC_INSTANCE *ttc){
/* Keep track of the total number of Key Frames Coded */
ttc->KeyFrameCount += 1;
ttc->LastKeyFrame = 1;
}
void TranscodeFrame(TC_INSTANCE *ttc){
ttc->LastKeyFrame++;
}
void TranscodeFirstFrame(TC_INSTANCE *ttc){
/* Keep track of the total number of Key Frames Coded. */
ttc->KeyFrameCount = 1;
ttc->LastKeyFrame = 1;
}
int theora_transcode_bufferin( TC_INSTANCE *ttc, int isKeyFrame, char * bytes, int bytecount){
/*transcode: record keyframe flag*/
ttc->ThisIsKeyFrame = isKeyFrame;
/* Special case for first frame */
if ( ttc->ThisIsFirstFrame ){
ttc->ThisIsFirstFrame = 0;
ttc->ThisIsKeyFrame = 0;
} else if ( ttc->ThisIsKeyFrame ) {
TranscodeKeyFrame(ttc);
ttc->ThisIsKeyFrame = 0;
} else {
/* Compress the frame. */
TranscodeFrame( ttc );
}
/*need to pack info here*/
{
int frame_type;
long total_bits;
long total_words;
int frac_bits;
oggpackB_readinit(&ttc->opb_in,bytes,bytecount);
oggpackB_reset(&ttc->opb_out);
/*Mark as video frame.*/
oggpackB_write(&ttc->opb_out,0,1);
/*Copy frame type.*/
frame_type=oggpackB_read1(&ttc->opb_in);
oggpackB_write(&ttc->opb_out,frame_type,1);
/*Skip an unused bit in the VP32 header.*/
oggpackB_adv1(&ttc->opb_in);
/*Copy Q multiplier.*/
oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,6),6);
/*VP3 has no per-block Q multipliers*/
oggpackB_write(&ttc->opb_out,0,1);
/*If the frame is a base/key/golden frame, copy a few extra bits.*/
if(frame_type==0){
/*These 13 bits are not included in a Theora frame header.
They were 0's and VP3 version info in VP32.*/
oggpackB_adv(&ttc->opb_in,13);
/*Copy the key frame type and the spare configuration bits.*/
oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,3),3);
}
/*Copy the rest of the bits over.*/
total_bits=bytecount*8-oggpack_bits(&ttc->opb_in);
frac_bits=(int)(total_bits&31);
if(frac_bits){
oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,frac_bits),
frac_bits);
}
total_words=total_bits>>5;
while(total_words-->0){
oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,32),32);
}
ttc->in_bytecount = oggpackB_bytes(&ttc->opb_out);
ttc->in_bytes = oggpackB_get_buffer(&ttc->opb_out);
}
/* Update stats variables. */
ttc->CurrentFrame++;
ttc->granulepos=
((ttc->CurrentFrame-ttc->LastKeyFrame-1)<<ttc->keyframe_granule_shift)+
ttc->LastKeyFrame-1;
return 0;
}
//static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len)
int theora_transcoder_init(theora_info * ti, TC_INSTANCE * ttc){
memset(ttc, 0, sizeof(*ttc));
ttc->granulepos = -1;
ttc->keyframe_granule_shift=_ilog(ti->keyframe_frequency_force-1);
ttc->LastKeyFrame = 0;
ttc->KeyFrameCount = 0;
ttc->ThisIsFirstFrame = 1;
ttc->ThisIsKeyFrame = 0;
ttc->CurrentFrame = 1;
ttc->in_bytes = 0;
ttc->in_bytecount = 0;
ttc->fps_denominator = ti->fps_denominator;
ttc->fps_numerator = ti->fps_numerator;
oggpackB_writeinit(&ttc->opb_out);
return 0;
}
int fetch_and_process_video(FILE *video,ogg_page *videopage,
ogg_stream_state *to,
TC_INSTANCE *ttc,
int videoflag){
/* You'll go to Hell for using static variables */
static int state=-1;
ogg_packet op;
int i;
int keyframeflag, framelength;
if(state==-1){
/* initialize the double frame buffer */
state=0;
}
/* is there a video page flushed? If not, work until there is. */
while(!videoflag){
spinnit();
if(ogg_stream_pageout(to,videopage)>0) return 1;
if(ogg_stream_eos(to)) return 0;
{
/* read and process more video */
/* video strategy reads one frame ahead so we know when we're
at end of stream and can mark last video frame as such
(vorbis audio has to flush one frame past last video frame
due to overlap and thus doesn't need this extra work */
/* have two frame buffers full (if possible) before
proceeding. after first pass and until eos, one will
always be full when we get here */
for(i=state;i<2;i++){
char c,frame[6];
int ret=fread(frame,1,6,video);
/* match and skip the frame header */
if(ret<6)break;
if(memcmp(frame,"FRAME",5)){
fprintf(stderr,"Loss of framing in VP31 input data\n");
exit(1);
}
if(frame[5]!='\n'){
int j;
for(j=0;j<79;j++)
if(fread(&c,1,1,video)&&c=='\n')break;
if(j==79){
fprintf(stderr,"Error parsing VP31 frame header\n");
exit(1);
}
}
/*read the length*/
ret=fread(&framelength, sizeof(int), 1, video);
/*read the keyframeflag*/
ret=fread(&keyframeflag, sizeof(int), 1, video);
vp3frame[i] = malloc(framelength);
framebytecount[i] = framelength;
frameiskey[i] = keyframeflag;
/* read the frame */
ret=fread((char *) vp3frame[i], sizeof(char), framelength, video);
if(ret!=framelength) break;
state++;
}
if(state<1){
/* can't get here unless VP31 stream has no video */
fprintf(stderr,"Video input contains no frames.\n");
exit(1);
}
/* Theora is a one-frame-in,one-frame-out system; submit a frame
for compression and pull out the packet */
//theora_encode_YUVin(td,&yuv);
theora_transcode_bufferin( ttc, frameiskey[0], vp3frame[0], framebytecount[0]);
/* if there's only one frame, it's the last in the stream */
if(state<2)
theora_transcode_packetout(ttc,1,&op);
else
theora_transcode_packetout(ttc,0,&op);
ogg_stream_packetin(to,&op);
{
signed char *temp=vp3frame[0];
vp3frame[0]=vp3frame[1];
vp3frame[1] = temp;
free(temp);
framebytecount[0]= framebytecount[1];
frameiskey[0] = frameiskey[1];
state--;
}
}
}
return videoflag;
}
/* returns, in seconds, absolute time of current packet in given
logical stream */
double transcode_granule_time(TC_INSTANCE *ttc,ogg_int64_t granulepos){
if(granulepos>=0){
ogg_int64_t iframe=granulepos>>ttc->keyframe_granule_shift;
ogg_int64_t pframe=granulepos-(iframe<<ttc->keyframe_granule_shift);
return (iframe+pframe)*
((double)ttc->fps_denominator/ttc->fps_numerator);
}
return(-1);
}
int main(int argc,char *argv[]){
int c,long_option_index,ret;
ogg_stream_state to; /* take physical pages, weld into a logical
stream of packets */
ogg_stream_state vo; /* take physical pages, weld into a logical
stream of packets */
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
ogg_packet op; /* one raw packet of data for decode */
theora_state td;
theora_info ti;
theora_comment tc;
vorbis_info vi; /* struct that stores all the static vorbis bitstream
settings */
vorbis_comment vc; /* struct that stores all the user comments */
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
int audioflag=0;
int videoflag=0;
int akbps=0;
int vkbps=0;
ogg_int64_t audio_bytesout=0;
ogg_int64_t video_bytesout=0;
double timebase;
FILE* outfile = stdout;
TC_INSTANCE ttc;
#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
/* if we were reading/writing a file, it would also need to in
binary mode, eg, fopen("file.wav","wb"); */
/* Beware the evil ifdef. We avoid these where we can, but this one we
cannot. Don't add any more, you'll probably go to hell if you do. */
_setmode( _fileno( stdin ), _O_BINARY );
_setmode( _fileno( stdout ), _O_BINARY );
#endif
while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
switch(c){
case 'o':
outfile=fopen(optarg,"wb");
if(outfile==NULL){
fprintf(stderr,"Unable to open output file '%s'\n", optarg);
exit(1);
}
break;;
case 'a':
audio_q=atof(optarg)*.099;
if(audio_q<-.1 || audio_q>1){
fprintf(stderr,"Illegal audio quality (choose -1 through 10)\n");
exit(1);
}
audio_r=-1;
break;
case 'v':
video_q=rint(atof(optarg)*6.3);
if(video_q<0 || video_q>63){
fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
exit(1);
}
video_r=0;
break;
case 'A':
audio_r=atof(optarg)*1000;
if(audio_q<0){
fprintf(stderr,"Illegal audio quality (choose > 0 please)\n");
exit(1);
}
audio_q=-99;
break;
case 'V':
video_r=rint(atof(optarg)*1000);
if(video_r<45000 || video_r>2000000){
fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n");
exit(1);
}
video_q=0;
break;
default:
usage();
}
}
while(optind<argc){
/* assume that anything following the options must be a filename */
id_file(argv[optind]);
optind++;
}
/* yayness. Set up Ogg output stream */
srand(time(NULL));
ogg_stream_init(&vo,rand());
ogg_stream_init(&to,rand()); /* oops, add one ot the above */
/* Set up Theora encoder */
if(!video){
fprintf(stderr,"No video files submitted for compression?\n");
exit(1);
}
/* Theora has a divisible-by-sixteen restriction for the encoded video size */
/* scale the frame size up to the nearest /16 and calculate offsets */
video_x=((frame_x + 15) >>4)<<4;
video_y=((frame_y + 15) >>4)<<4;
frame_x_offset=(video_x-frame_x)/2;
frame_y_offset=(video_y-frame_y)/2;
theora_info_init(&ti);
ti.width=video_x;
ti.height=video_y;
ti.frame_width=frame_x;
ti.frame_height=frame_y;
ti.offset_x=frame_x_offset;
ti.offset_y=frame_y_offset;
ti.fps_numerator=video_hzn;
ti.fps_denominator=video_hzd;
ti.aspect_numerator=video_an;
ti.aspect_denominator=video_ad;
ti.colorspace=OC_CS_UNSPECIFIED;
ti.target_bitrate=video_r;
ti.quality=video_q;
ti.dropframes_p=0;
ti.quick_p=1;
ti.keyframe_auto_p=1;
ti.keyframe_frequency=32768;
ti.keyframe_frequency_force=32768;
ti.keyframe_data_target_bitrate=video_r*1.5;
ti.keyframe_auto_threshold=80;
ti.keyframe_mindistance=8;
ti.noise_sensitivity=1;
theora_encode_init(&td,&ti);
theora_transcoder_init(&ti, &ttc);
theora_info_clear(&ti);
/* initialize Vorbis too, assuming we have audio to compress. */
if(audio){
vorbis_info_init(&vi);
if(audio_q>-99)
ret = vorbis_encode_init_vbr(&vi,audio_ch,audio_hz,audio_q);
else
ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,audio_r,-1);
if(ret){
fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n"
"the requested quality or bitrate.\n\n");
exit(1);
}
vorbis_comment_init(&vc);
vorbis_analysis_init(&vd,&vi);
vorbis_block_init(&vd,&vb);
}
/* write the bitstream header packets with proper page interleave */
/* first packet will get its own page automatically */
theora_encode_header(&td,&op);
ogg_stream_packetin(&to,&op);
if(ogg_stream_pageout(&to,&og)!=1){
fprintf(stderr,"Internal Ogg library error.\n");
exit(1);
}
fwrite(og.header,1,og.header_len,outfile);
fwrite(og.body,1,og.body_len,outfile);
/* create the remaining theora headers */
theora_comment_init(&tc);
theora_encode_comment(&tc,&op);
ogg_stream_packetin(&to,&op);
theora_encode_tables(&td,&op);
ogg_stream_packetin(&to,&op);
if(audio){
ogg_packet header;
ogg_packet header_comm;
ogg_packet header_code;
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
ogg_stream_packetin(&vo,&header); /* automatically placed in its own
page */
if(ogg_stream_pageout(&vo,&og)!=1){
fprintf(stderr,"Internal Ogg library error.\n");
exit(1);
}
fwrite(og.header,1,og.header_len,outfile);
fwrite(og.body,1,og.body_len,outfile);
/* remaining vorbis header packets */
ogg_stream_packetin(&vo,&header_comm);
ogg_stream_packetin(&vo,&header_code);
}
/* Flush the rest of our headers. This ensures
the actual data in each stream will start
on a new page, as per spec. */
while(1){
int result = ogg_stream_flush(&to,&og);
if(result<0){
/* can't get here */
fprintf(stderr,"Internal Ogg library error.\n");
exit(1);
}
if(result==0)break;
fwrite(og.header,1,og.header_len,outfile);
fwrite(og.body,1,og.body_len,outfile);
}
if(audio){
while(1){
int result=ogg_stream_flush(&vo,&og);
if(result<0){
/* can't get here */
fprintf(stderr,"Internal Ogg library error.\n");
exit(1);
}
if(result==0)break;
fwrite(og.header,1,og.header_len,outfile);
fwrite(og.body,1,og.body_len,outfile);
}
}
/* setup complete. Raw processing loop */
fprintf(stderr,"Compressing....\n");
while(1){
/* is there an audio page flushed? If not, fetch one if possible */
audioflag=fetch_and_process_audio(audio,&audiopage,&vo,&vd,&vb,audioflag);
/* is there a video page flushed? If not, fetch one if possible */
videoflag=fetch_and_process_video(video,&videopage,&to,&ttc,videoflag);
/* no pages of either? Must be end of stream. */
if(!audioflag && !videoflag)break;
/* which is earlier; the end of the audio page or the end of the
video page? Flush the earlier to stream */
{
int audio_or_video=-1;
double audiotime=
audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
double videotime=
videoflag?transcode_granule_time(&ttc,ogg_page_granulepos(&videopage)):-1;
if(!audioflag){
audio_or_video=1;
} else if(!videoflag) {
audio_or_video=0;
} else {
if(audiotime<videotime)
audio_or_video=0;
else
audio_or_video=1;
}
if(audio_or_video==1){
/* flush a video page */
video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
videoflag=0;
timebase=videotime;
}else{
/* flush an audio page */
audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,outfile);
audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,outfile);
audioflag=0;
timebase=audiotime;
}
{
int hundredths=timebase*100-(long)timebase*100;
int seconds=(long)timebase%60;
int minutes=((long)timebase/60)%60;
int hours=(long)timebase/3600;
if(audio_or_video)
vkbps=rint(video_bytesout*8./timebase*.001);
else
akbps=rint(audio_bytesout*8./timebase*.001);
fprintf(stderr,
"\n %d:%02d:%02d.%02d audio: %dkbps video: %dkbps ",
hours,minutes,seconds,hundredths,akbps,vkbps);
}
}
}
/* clear out state */
if(audio){
ogg_stream_clear(&vo);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
}
if(video){
ogg_stream_clear(&to);
theora_clear(&td);
}
if(outfile && outfile!=stdout)fclose(outfile);
fprintf(stderr,"\r \ndone.\n\n");
return(0);
}