[xiph-commits] r12969 - in trunk/theora-tools: . png2theora

j at svn.xiph.org j at svn.xiph.org
Tue May 22 02:23:36 PDT 2007


Author: j
Date: 2007-05-22 02:23:36 -0700 (Tue, 22 May 2007)
New Revision: 12969

Added:
   trunk/theora-tools/png2theora/
   trunk/theora-tools/png2theora/Makefile.am
   trunk/theora-tools/png2theora/png2theora.c
Modified:
   trunk/theora-tools/Makefile.am
   trunk/theora-tools/configure.ac
Log:
- add png2theora to theora-tools, better place than examples



Modified: trunk/theora-tools/Makefile.am
===================================================================
--- trunk/theora-tools/Makefile.am	2007-05-22 00:27:22 UTC (rev 12968)
+++ trunk/theora-tools/Makefile.am	2007-05-22 09:23:36 UTC (rev 12969)
@@ -2,7 +2,7 @@
 
 AUTOMAKE_OPTIONS = foreign dist-zip
 
-SUBDIRS = debian theora123 theora_splayer theoraenc theora_transcoder theora_dumpvideo theoracomment
+SUBDIRS = debian theora123 theora_splayer theoraenc theora_transcoder theora_dumpvideo theoracomment png2theora
 
 EXTRA_DIST = COPYING autogen.sh win32
 

Modified: trunk/theora-tools/configure.ac
===================================================================
--- trunk/theora-tools/configure.ac	2007-05-22 00:27:22 UTC (rev 12968)
+++ trunk/theora-tools/configure.ac	2007-05-22 09:23:36 UTC (rev 12969)
@@ -70,10 +70,13 @@
 dnl Check for library functions
 dnl --------------------------------------------------
 
-dnl substitue the included getopt if the system doesn't support long options
-AC_CHECK_FUNC(getopt_long, [GETOPT_OBJS=''], [GETOPT_OBJS='getopt.$(OBJEXT) getopt1.$(OBJEXT)'])
+dnl substitute the included getopt if the system doesn't support long options
+AC_CHECK_FUNC(getopt_long,
+              [GETOPT_OBJS=''],
+              [GETOPT_OBJS='getopt.$(OBJEXT) getopt1.$(OBJEXT)'])
 AC_SUBST(GETOPT_OBJS)
 
+AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes)
 PKG_CHECK_MODULES(XIPH,ogg >= 1.1 vorbis theora)
 CFLAGS="$CFLGS $XIPH_CFLAGS"
 LIBS="$LIBS $XIPH_LIBS"
@@ -106,6 +109,15 @@
 fi
 AC_SUBST(PORTAUDIO_LIBS)
 
+dnl check for libpng
+HAVE_PNG=no
+if test "x$HAVE_PKG_CONFIG" = "xyes"
+then
+  PKG_CHECK_MODULES(PNG, libpng, HAVE_PNG=yes, HAVE_PNG=no)
+fi
+AC_SUBST(PNG_CFLAGS)
+AC_SUBST(PNG_LIBS)
+
 if test x$HAVE_SDL = xyes -a x$HAVE_OSS = xyes; then
   THEORA123="theora123"
   AC_SUBST(THEORA123)
@@ -114,6 +126,10 @@
   SPLAYER="theora_splayer"
   AC_SUBST(SPLAYER)
 fi
+if test x$HAVE_PNG = xyes; then
+  PNG2THEORA="png2theora"
+  AC_SUBST(PNG2THEORA)
+fi
 
 dnl --------------------------------------------------
 dnl Do substitutions
@@ -133,6 +149,7 @@
   theora_transcoder/Makefile
   theora_dumpvideo/Makefile
   theoracomment/Makefile
+  png2theora/Makefile
   debian/Makefile
 ])
 AC_OUTPUT

Copied: trunk/theora-tools/png2theora/Makefile.am (from rev 12968, trunk/theora-tools/theoraenc/Makefile.am)
===================================================================
--- trunk/theora-tools/png2theora/Makefile.am	                        (rev 0)
+++ trunk/theora-tools/png2theora/Makefile.am	2007-05-22 09:23:36 UTC (rev 12969)
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = $(PNG2THEORA)
+
+# possible contents of BUILDABLE_PLAYERS:
+EXTRA_PROGRAMS = png2theora
+
+png2theora_SOURCES = png2theora.c
+png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
+png2theora_LDADD = $(GETOPT_OBJS) $(LDADD) $(PNG_LIBS)
+png2theora_DEPENDENCIES = $(GETOPT_OBJS)

Copied: trunk/theora-tools/png2theora/png2theora.c (from rev 12968, trunk/theora/examples/png2theora.c)
===================================================================
--- trunk/theora-tools/png2theora/png2theora.c	                        (rev 0)
+++ trunk/theora-tools/png2theora/png2theora.c	2007-05-22 09:23:36 UTC (rev 12969)
@@ -0,0 +1,531 @@
+/********************************************************************
+ *                                                                  *
+ * 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-2007                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example encoder application; makes an Ogg Theora
+            file from a sequence of png images
+  last mod: $Id$
+             based on code from Vegard Nossum
+  
+ ********************************************************************/
+
+#define _FILE_OFFSET_BITS 64
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <png.h>
+#include <ogg/ogg.h>
+#include "theora/theora.h"
+
+
+#define PROGRAM_NAME  "png2theora"
+#define PROGRAM_VERSION  "1.0"
+
+static const char *option_output = NULL;
+static int video_fps_numerator = 24;
+static int video_fps_denominator = 1;
+static int video_aspect_numerator = 0;
+static int video_aspect_denominator = 0;
+static int video_rate = 0;
+static int video_quality = 63;
+
+static int theora_initialized = 0;
+
+static FILE *ogg_fp = NULL;
+static ogg_stream_state ogg_os;
+
+static theora_state theora_td;
+static theora_info theora_ti;
+
+static char *input_filter;
+
+const char *optstring = "o:h:v:V:s:S:f:F:";
+struct option options [] = {
+ {"output",required_argument,NULL,'o'},
+ {"help",optional_argument,NULL,'h'},
+ {"video-rate-target",required_argument,NULL,'V'},
+ {"video-quality",required_argument,NULL,'v'},
+ {"aspect-numerator",optional_argument,NULL,'s'},
+ {"aspect-denominator",optional_argument,NULL,'S'},
+ {"framerate-numerator",optional_argument,NULL,'f'},
+ {"framerate-denominator",optional_argument,NULL,'F'},
+ {NULL,0,NULL,0}
+};
+
+static void usage(void){
+  fprintf(stderr,
+          "%s %s\n"
+          "Usage: %s [options] input\n\n"
+          "Output is parsed by scanf and represents a list of files, i.e.\n"
+          "  file-%%06d.png to look for files file000001.png to file9999999.png \n\n"
+          "Options: \n\n"
+          "  -o --output <filename.ogg>     file name for encoded output;\n"
+          "                                 If this option is not given, the\n"
+          "                                 compressed data is sent to stdout.\n\n"
+          "  -V --video-rate-target <n>     bitrate target for Theora video\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"
+          "   -s --aspect-numerator <n>     Aspect ratio numerator, default is 0\n"
+          "   -S --aspect-denominator <n>   Aspect ratio denominator, default is 0\n"
+          "   -f --framerate-numerator <n>  Frame rate numerator\n"
+          "   -F --framerate-denominator <n>Frame rate denominator\n"
+          "                                 The frame rate nominator divided by this\n"
+          "                                 determinates the frame rate in units per tick\n"
+          ,PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_NAME
+  );
+  exit(0);
+}
+
+
+static int
+theora_open(const char *pathname)
+{
+  ogg_packet op;
+  ogg_page og;
+  theora_comment tc;
+
+  ogg_fp = fopen(pathname, "wb");
+  if(!ogg_fp) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't open output file");
+    return 1;
+  }
+
+  if(ogg_stream_init(&ogg_os, rand())) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't create ogg stream state");
+    return 1;
+  }
+
+  if(theora_encode_init(&theora_td, &theora_ti)) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't initialize theora encoding");
+    return 1;
+  }
+
+  theora_encode_header(&theora_td, &op);
+  ogg_stream_packetin(&ogg_os, &op);
+  if(ogg_stream_pageout(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }
+
+  theora_comment_init(&tc);
+  theora_encode_comment(&tc, &op);
+  ogg_stream_packetin(&ogg_os, &op);
+  if(ogg_stream_pageout(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }
+
+  theora_encode_tables(&theora_td, &op);
+  ogg_stream_packetin(&ogg_os, &op);
+  if(ogg_stream_pageout(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }
+
+  if(ogg_stream_flush(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }
+
+  return 0;
+}
+
+static int
+theora_write_frame(unsigned long w, unsigned long h, unsigned char *yuv)
+{
+  yuv_buffer yuv_buf;
+  ogg_packet op;
+  ogg_page og;
+
+  unsigned long yuv_w;
+  unsigned long yuv_h;
+
+  unsigned char *yuv_y;
+  unsigned char *yuv_u;
+  unsigned char *yuv_v;
+
+  unsigned int x;
+  unsigned int y;
+  
+  /* Must hold: yuv_w >= w */
+  yuv_w = (w + 15) & ~15;
+
+  /* Must hold: yuv_h >= h */
+  yuv_h = (h + 15) & ~15;
+
+  yuv_y = malloc(yuv_w * yuv_h);
+  yuv_u = malloc(yuv_w * yuv_h / 4);
+  yuv_v = malloc(yuv_w * yuv_h / 4);
+
+  yuv_buf.y_width = yuv_w;
+  yuv_buf.y_height = yuv_h;
+  yuv_buf.y_stride = yuv_w;
+  yuv_buf.uv_width = yuv_w >> 1;
+  yuv_buf.uv_height = yuv_h >> 1;
+  yuv_buf.uv_stride = yuv_w >> 1;
+  yuv_buf.y = yuv_y;
+  yuv_buf.u = yuv_u;
+  yuv_buf.v = yuv_v;
+
+  for(y = 0; y < yuv_h; y++) {
+    for(x = 0; x < yuv_w; x++) {
+      yuv_y[x + y * yuv_w] = 0;
+    }
+  }
+
+  for(y = 0; y < yuv_h; y += 2) {
+    for(x = 0; x < yuv_w; x += 2) {
+      yuv_u[(x >> 1) + (y >> 1) * (yuv_w >> 1)] = 0;
+      yuv_v[(x >> 1) + (y >> 1) * (yuv_w >> 1)] = 0;
+    }
+  }
+
+  for(y = 0; y < h; y++) {
+    for(x = 0; x < w; x++) {
+      yuv_y[x + y * yuv_w] = yuv[3 * (x + y * w) + 0];
+    }
+  }
+
+  for(y = 0; y < h; y += 2) {
+    for(x = 0; x < w; x += 2) {
+      yuv_u[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
+        yuv[3 * (x + y * w) + 1];
+      yuv_v[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
+        yuv[3 * (x + y * w) + 2];
+    }
+  }
+
+  if(theora_encode_YUVin(&theora_td, &yuv_buf)) {
+    fprintf(stderr, "%s: error: could not encode frame\n",
+      option_output);
+    return 1;
+  }
+
+  if(!theora_encode_packetout(&theora_td, 0, &op)) {
+    fprintf(stderr, "%s: error: could not read packets\n",
+      option_output);
+    return 1;
+  }
+
+  ogg_stream_packetin(&ogg_os, &op);
+  if(ogg_stream_pageout(&ogg_os, &og)) {
+    fwrite(og.header, og.header_len, 1, ogg_fp);
+    fwrite(og.body, og.body_len, 1, ogg_fp);
+  }
+
+  free(yuv_y);
+  free(yuv_u);
+  free(yuv_v);
+
+  return 0;
+}
+
+static void
+theora_close(void)
+{
+  ogg_packet op;
+  ogg_page og;
+
+  if (theora_initialized) {
+    theora_encode_packetout(&theora_td, 1, &op);
+    if(ogg_stream_pageout(&ogg_os, &og)) {
+      fwrite(og.header, og.header_len, 1, ogg_fp);
+      fwrite(og.body, og.body_len, 1, ogg_fp);
+    }
+  
+    theora_info_clear(&theora_ti);
+    theora_clear(&theora_td);
+  
+    fflush(ogg_fp);
+    fclose(ogg_fp);
+  }
+  
+  ogg_stream_clear(&ogg_os);
+}
+
+static unsigned char
+clamp(double d)
+{
+  if(d < 0)
+    return 0;
+
+  if(d > 255)
+    return 255;
+
+  return d;
+}
+
+static void
+rgb_to_yuv(png_bytep *png,
+           unsigned char *yuv,
+           unsigned int w, unsigned int h)
+{
+  unsigned int x;
+  unsigned int y;
+
+  for(y = 0; y < h; y++) {
+    for(x = 0; x < w; x++) {
+      png_byte r;
+      png_byte g;
+      png_byte b;
+
+      r = png[y][3 * x + 0];
+      g = png[y][3 * x + 1];
+      b = png[y][3 * x + 2];
+
+      /* XXX: Cringe. */
+      yuv[3 * (x + w * y) + 0] = clamp(
+        0.299 * r
+        + 0.587 * g
+        + 0.114 * b);
+      yuv[3 * (x + w * y) + 1] = clamp((0.436 * 255
+        - 0.14713 * r
+        - 0.28886 * g
+        + 0.436 * b) / 0.872);
+      yuv[3 * (x + w * y) + 2] = clamp((0.615 * 255
+        + 0.615 * r
+        - 0.51499 * g
+        - 0.10001 * b) / 1.230);
+    }
+  }
+}
+
+static int
+png_read(const char *pathname, unsigned int *w, unsigned int *h, unsigned char **yuv)
+{
+  FILE *fp;
+  unsigned char header[8];
+  png_structp png_ptr;
+  png_infop info_ptr;
+  png_infop end_ptr;
+  png_bytep *row_pointers;
+
+  fp = fopen(pathname, "rb");
+  if(!fp) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, strerror(errno));
+    return 1;
+  }
+
+  fprintf(stderr, "%s\n", pathname);
+
+  fread(header, 1, 8, fp);
+  if(png_sig_cmp(header, 0, 8)) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "not a PNG");
+    return 1;
+  }
+
+  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+    NULL, NULL, NULL);
+  if(!png_ptr) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't create png read structure");
+    return 1;
+  }
+
+  info_ptr = png_create_info_struct(png_ptr);
+  if(!info_ptr) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't create png info structure");
+    /* XXX: cleanup */
+    return 1;
+  }
+
+  end_ptr = png_create_info_struct(png_ptr);
+  if(!end_ptr) {
+    fprintf(stderr, "%s: error: %s\n",
+      pathname, "couldn't create png info structure");
+    /* XXX: cleanup */
+    return 1;
+  }
+
+  png_init_io(png_ptr, fp);
+  png_set_sig_bytes(png_ptr, 8);
+
+  png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16, NULL);
+
+  row_pointers = png_get_rows(png_ptr, info_ptr);
+
+  *w = png_get_image_width(png_ptr, info_ptr);
+  *h = png_get_image_height(png_ptr, info_ptr);
+  *yuv = malloc(*w * *h * 3);
+  rgb_to_yuv(row_pointers, *yuv, *w, *h);
+
+  png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
+
+  fclose(fp);
+  return 0;
+}
+
+static int include_files (const struct dirent *de)
+{
+  char name[1024];
+  int number = -1;
+  sscanf(de->d_name, input_filter, &number);
+  sprintf(name, input_filter, number);
+  return !strcmp(name, de->d_name);
+}
+
+int
+main(int argc, char *argv[])
+{
+  int c,long_option_index;
+  int i, n;
+  char *input_mask;
+  char *input_directory;
+  char *scratch;
+  struct dirent **png_files;
+  
+  while(1) {
+
+    c=getopt_long(argc,argv,optstring,options,&long_option_index);
+    if(c == EOF)
+      break;
+
+    switch(c) {
+      case 'h':
+        usage();
+        break;
+      case 'o':
+        option_output = optarg;
+        break;;
+      case 'v':
+        video_quality=rint(atof(optarg)*6.3);
+        if(video_quality<0 || video_quality>63){
+          fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+          exit(1);
+        }
+        video_rate=0;
+        break;
+      case 'V':
+        video_rate=rint(atof(optarg)*1000);
+        if(video_rate<45000 || video_rate>2000000){
+          fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n");
+          exit(1);
+        }
+        video_quality=0;
+       break;
+     case 's':
+       video_aspect_numerator=rint(atof(optarg));
+       break;
+     case 'S':
+       video_aspect_denominator=rint(atof(optarg));
+       break;
+     case 'f':
+       video_fps_numerator=rint(atof(optarg));
+       break;
+     case 'F':
+       video_fps_denominator=rint(atof(optarg));   
+     default:
+        usage();
+        break;
+      }
+  }
+
+  if(argc < 3) {
+    usage();
+  }
+
+  input_mask = argv[optind];
+  /* dirname and basename must operate on scratch strings */
+  scratch = strdup(input_mask);
+  input_directory = strdup(dirname(scratch));
+  free(scratch);
+  scratch = strdup(input_mask);
+  input_filter = strdup(basename(scratch));
+  free(scratch);
+
+#ifdef DEBUG
+  fprintf(stderr, "scanning %s with filter '%s'\n",
+	input_directory, input_filter);
+#endif
+  n = scandir (input_directory, &png_files, include_files, alphasort);
+  for(i=0;i< n;i++) {
+    unsigned int w;
+    unsigned int h;
+    unsigned char *yuv;
+    char input_png[1024];
+
+    sprintf(input_png, "%s/%s", input_directory, png_files[i]->d_name);
+    
+    if(png_read(input_png, &w, &h, &yuv)) {
+      fprintf(stderr, "could not read %s\n", input_png);
+      theora_close();
+      exit(1);
+    }
+    
+    if(!theora_initialized) {
+      theora_info_init(&theora_ti);
+
+      theora_ti.width = ((w + 15) >>4)<<4;
+      theora_ti.height = ((h + 15)>>4)<<4;
+      theora_ti.frame_width = w;
+      theora_ti.frame_height = h;
+      theora_ti.offset_x = 0;
+      theora_ti.offset_y = 0;
+      theora_ti.fps_numerator = video_fps_numerator;
+      theora_ti.fps_denominator = video_fps_denominator;
+      theora_ti.aspect_numerator = video_aspect_numerator;
+      theora_ti.aspect_denominator = video_aspect_denominator;
+      theora_ti.colorspace = OC_CS_UNSPECIFIED;
+      theora_ti.pixelformat = OC_PF_420;
+      theora_ti.target_bitrate = video_rate;
+      theora_ti.quality = video_quality;
+
+      theora_ti.dropframes_p = 0;
+      theora_ti.quick_p = 1;
+      theora_ti.keyframe_auto_p = 1;
+      theora_ti.keyframe_frequency = 64;
+      theora_ti.keyframe_frequency_force = 64;
+      theora_ti.keyframe_data_target_bitrate = video_rate * 1.5;
+      theora_ti.keyframe_mindistance = 8;
+      theora_ti.noise_sensitivity = 1;
+
+      if(theora_open(option_output)) {
+        /* XXX: cleanup */
+        return 1;
+      }
+
+      theora_initialized = 1;
+    }
+
+    if(theora_write_frame(w, h, yuv)) {
+      theora_close();
+      free(input_directory);
+      free(input_filter);
+      exit(1);
+    }
+
+    free(yuv);
+  }
+
+  theora_close();
+  return 0;
+}



More information about the commits mailing list