Logo Search packages:      
Sourcecode: capseo version File versions  Download package

cpsrecode.cpp

/////////////////////////////////////////////////////////////////////////////
//
//  CAPSEO - Capseo Video Codec Library
//  $Id$
//  (cpsrecode re-encodes capseo encoded files into y4m format for further reuse)
//
//  Authors:
//      Copyright (c) 2007 by Christian Parpart <trapni@gentoo.org>
//
//  This code is based on seom's seom-filter utility
//      (http://neopsis.com/projects/seom/)
//
//  This file as well as its whole library is licensed under
//  the terms of GPL. See the file COPYING.
//
/////////////////////////////////////////////////////////////////////////////
#include <capseo.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#if THEORA
# include <ogg/ogg.h>
# include <theora/theora.h>
#endif

class IEncoder {//{{{
public:
      virtual ~IEncoder() {}

      virtual void initialize() = 0;
      virtual void writeFrame(capseo_frame_t *) = 0;
      virtual void finalize() = 0;
};//}}}

int fps = 25;                             //!< average fps to re-encode with
int inputFd = -1;                   //!< file descriptor, where to read the capseo video from
int outputFd = -1;                        //!< file descriptor, where to write the re-encoded video to
capseo_stream_t *stream = 0;  //!< capseo input stream handle
capseo_info_t info;                       //!< capseo out parameters
IEncoder *encoder = 0;              //!< the encoder to use

int die(const char *fmt, ...) {//{{{
      va_list va;

      fprintf(stderr, "ERROR: ");
      va_start(va, fmt);
      vfprintf(stderr, fmt, va);
      va_end(va);
      fprintf(stderr, "\nI die...\n");
      exit(1);
      return 1; // never reached.
}//}}}

void printHelp() {//{{{
      printf(
            "capseo recode, version %s\n"
            "\t-r:  frames per second\n"
            "\t-i:  input filename (or - for stdin)\n"
#if THEORA
            "\t-c:  specify output codec to use (y4m, theora)\n"
#else
            "\t-c:  specify output codec to use (y4m only)\n"
#endif
            "\t-o:  output filename (or - for stdout)\n"
            "\t-h:  print help text\n",
            VERSION
      );
}//}}}

template<typename T>
inline T diff(const T& t1, const T& t2) {
      return t1 < t2 ? t2 - t1 : t1 - t2;
}

class TY4MEncoder : public IEncoder {//{{{
public:
      virtual void initialize() {
            char header[128];
            int n = snprintf(header, sizeof(header), "YUV4MPEG2 W%d H%d F%d:1 Ip\n", info.width, info.height, fps);
            write(outputFd, header, n);
      }

      virtual void writeFrame(capseo_frame_t *frame) {
            static const char header[] = "FRAME\n";
            write(outputFd, header, sizeof(header) - 1);

            uint8_t *buffer = frame->buffer;

            // write each freame row up-side-down
            for (int y = info.height - 1; y >= 0; --y)
                  write(outputFd, buffer + y * info.width, info.width);

            buffer += info.width * info.height;

            for (int i = 0; i < 2; ++i) {
                  for (int y = (info.height / 2) - 1; y >= 0; --y)
                        write(outputFd, buffer + y * (info.width / 2), (info.width / 2));

                  buffer += info.width * info.height / 4;
            }
      }

      virtual void finalize() {
      }
};//}}}

#if THEORA
class TOggTheoraEncoder : public IEncoder {//{{{
private:
      ogg_stream_state ogg;
      theora_state theora;
      yuv_buffer yuv;
      unsigned char *buffer;

public:
      virtual void initialize() {
            buffer = new unsigned char[info.width * info.height * 4];
            yuv.y_width = ((info.width + 15) >> 4) << 4;
            yuv.y_height = ((info.height + 15) >> 4) << 4;
            yuv.y_stride = yuv.y_width;

            yuv.uv_width = yuv.y_width / 2;
            yuv.uv_stride = yuv.uv_width;
            yuv.uv_height = yuv.y_height / 2;

            theoraInit();
            theoraComments();
            theoraTables();
      }

      unsigned char *flipV(capseo_frame_t *frame) {
            // TODO
            return frame->buffer;
      }

      virtual void writeFrame(capseo_frame_t *frame) {
            yuv.y = flipV(frame);
            yuv.u = yuv.y + info.width * info.height;
            yuv.v = yuv.u + info.width * info.height / 4;

            int rv = theora_encode_YUVin(&theora, &yuv);
            checkError(rv, "theora_encode_YUVin");

            ogg_packet packet;
            rv = theora_encode_packetout(&theora, false, &packet);
            checkError(rv, "theora_encode_packetout");

            ogg_stream_packetin(&ogg, &packet);
            flushOnce();
      }

      virtual void finalize() {
            flushAll();

            theora_clear(&theora);
            ogg_stream_clear(&ogg);

            delete[] buffer;
      }

private:
      void theoraInit() {
            theora_info ti;
            theora_info_init(&ti);

            ti.width = info.width;
            ti.height = info.height;
            ti.frame_width = info.width;
            ti.frame_height = info.height;
            ti.offset_x = 0;
            ti.offset_y = 0;

            ti.fps_numerator = fps;
            ti.fps_denominator = 1;

            ti.aspect_numerator = 1;
            ti.aspect_denominator = 1;

            ti.dropframes_p = 1;
            ti.quick_p = 0;
            ti.keyframe_auto_p = 1;
            ti.keyframe_frequency = 64;
            ti.keyframe_frequency_force = 64;
            ti.keyframe_data_target_bitrate = (ogg_uint32_t)(ti.target_bitrate * 1.5);
            ti.keyframe_auto_threshold = 80;
            ti.keyframe_mindistance = 8;
            ti.quality = 63; // 0..63
            ti.noise_sensitivity = 1;

            theora_encode_init(&theora, &ti);
            theora_info_clear(&ti);

            ogg_packet packet;
            int rv = theora_encode_header(&theora, &packet);
            checkError(rv, "theora_encode_header");

            ogg_stream_packetin(&ogg, &packet);

            flushOnce();
      }

      void theoraComments() {
            theora_comment tc;
            theora_comment_init(&tc);

            static struct {
                  char *key;
                  char *value;
            } comments[] = {
                  { "ENCODER", "cpsrecode" },
                  { "SOURCE", "captury" },
                  { 0, 0 }
            };

            for (int i = 0; comments[i].key; ++i)
                  theora_comment_add_tag(&tc, comments[i].key, comments[i].value);

            ogg_packet packet;
            int rv = theora_encode_comment(&tc, &packet);
            checkError(rv, "theora_encode_comment");

            ogg_stream_packetin(&ogg, &packet);
      }

      void theoraTables() {
            ogg_packet packet;
            int rv = theora_encode_tables(&theora, &packet);
            checkError(rv, "theora_encode_tables");

            ogg_stream_packetin(&ogg, &packet);
            flushAll();
      }

      void checkError(int rv, const char *fname) {
            if (rv < 0) {
                  fprintf(stderr, "theora error(%s): %d\n", fname ? fname : "unknown", rv);
                  exit(1);
            }
      }

      void flushOnce() {
            ogg_page page;

            if (ogg_stream_pageout(&ogg, &page)) {
                  write(outputFd, page.header, page.header_len);
                  write(outputFd, page.body, page.body_len);
            }
      }

      void flushAll() {
            ogg_page page;

            while (ogg_stream_pageout(&ogg, &page)) {
                  write(outputFd, page.header, page.header_len);
                  write(outputFd, page.body, page.body_len);
            }
      }
};//}}}
#endif

void parseCmdLineArgs(int argc, char *argv[]) {//{{{
      int nargs = 1;
      for (int c; (c = getopt(argc, argv, "r:i:c:o:h")) != -1; ++nargs) {
            switch (c) {
                  case 'r':
                        fps = atoi(optarg);
                        break;
                  case 'i':
                        if (strcmp(optarg, "-") == 0)
                              inputFd = STDIN_FILENO;
#if defined(O_LARGEFILE)
                        else if ((inputFd = open(optarg, O_RDONLY | O_LARGEFILE)) == -1)
#else
                        else if ((inputFd = open(optarg, O_RDONLY)) == -1)
#endif
                              die("Error opening input file(%s): %s", optarg, strerror(errno));

                        break;
                  case 'o':
                        if (strcmp(optarg, "-") == 0)
                              outputFd = STDOUT_FILENO;
                        else if ((outputFd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1)
                              die("Error opening output file(%s): %s", optarg, strerror(errno));

                        break;
                  case 'c':
#if THEORA
                        if (strcmp(optarg, "theora") == 0) {
                              encoder = new TOggTheoraEncoder();
                              break;
                        }
#endif
                        if (strcmp(optarg, "y4m") == 0) {
                              encoder = new TY4MEncoder();
                              break;
                        }
                        die("Unsupported output codec: %s\n", optarg);
                  case 'h':
                        printHelp();
                        exit(0);
                  default:
                        break;
            }
      }

      if (!encoder)
            encoder = new TY4MEncoder(); // default to y4m

      if (inputFd == -1)
            die("No input file specified");

      if (outputFd == -1)
            die("No output file specified");

      info.format = CAPSEO_FORMAT_YUV420;
      if (int error = CapseoStreamCreateFd(CAPSEO_MODE_DECODE, &info, inputFd, &stream))
            die("Could not create input stream (error %d)", error);
}//}}}

int main(int argc, char *argv[]) {
      bzero(&info, sizeof(capseo_info_t));

      parseCmdLineArgs(argc, argv);

      encoder->initialize();

      capseo_frame_t *frames[2];

      CapseoStreamDecodeFrame(stream, &frames[0], true);
      CapseoStreamDecodeFrame(stream, &frames[1], true);

      uint64_t timeStep = 1000000 / fps;
      uint64_t timeNext = frames[0]->id;

      for (;;) {
            while (diff(frames[0]->id, timeNext) > diff(frames[1]->id, timeNext)) {
                  frames[0] = frames[1];

                  if (int error = CapseoStreamDecodeFrame(stream, &frames[1], true)) {
                        if (error == CAPSEO_STREAM_END)
                              goto out;

                        return die("CapseoStreamDecodeFrame: decode error (code %d)", error);
                  }

                  if (!frames[1])
                        goto out;
            }

            encoder->writeFrame(frames[0]);

            timeNext += timeStep;
      }

out:
      // write last frame
      encoder->writeFrame(frames[0]);
      encoder->finalize();
      delete encoder;

      CapseoStreamDestroy(stream);

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index