Blame operations/external/ff-load.c

Packit bc1512
/* This file is an image processing operation for GEGL
Packit bc1512
 *
Packit bc1512
 * GEGL is free software; you can redistribute it and/or
Packit bc1512
 * modify it under the terms of the GNU Lesser General Public
Packit bc1512
 * License as published by the Free Software Foundation; either
Packit bc1512
 * version 3 of the License, or (at your option) any later version.
Packit bc1512
 *
Packit bc1512
 * GEGL is distributed in the hope that it will be useful,
Packit bc1512
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bc1512
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bc1512
 * Lesser General Public License for more details.
Packit bc1512
 *
Packit bc1512
 * You should have received a copy of the GNU Lesser General Public
Packit bc1512
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit bc1512
 *
Packit bc1512
 * Copyright 2003, 2006 Øyvind Kolås <pippin@gimp.org>
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
#include <glib/gi18n-lib.h>
Packit bc1512
Packit bc1512
Packit bc1512
#ifdef GEGL_CHANT_PROPERTIES
Packit bc1512
Packit bc1512
gegl_chant_file_path (path, _("File"), "/home/pippin/input.avi", _("Path of file to load"))
Packit bc1512
gegl_chant_int (frame, _("Frame"), 0, 1000000, 0, _("Frame number"))
Packit bc1512
Packit bc1512
#else
Packit bc1512
Packit bc1512
#define GEGL_CHANT_TYPE_SOURCE
Packit bc1512
#define GEGL_CHANT_C_FILE       "ff-load.c"
Packit bc1512
Packit bc1512
#include "gegl-chant.h"
Packit bc1512
#include <errno.h>
Packit bc1512
Packit bc1512
#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H
Packit bc1512
#include <libavformat/avformat.h>
Packit bc1512
#else
Packit bc1512
#include <avformat.h>
Packit bc1512
#endif
Packit bc1512
Packit bc1512
typedef struct
Packit bc1512
{
Packit bc1512
  gdouble          frames;
Packit bc1512
  gint             width;
Packit bc1512
  gint             height;
Packit bc1512
  gdouble          fps;
Packit bc1512
  gchar           *codec_name;
Packit bc1512
  gchar           *fourcc;
Packit bc1512
Packit bc1512
  int              video_stream;
Packit bc1512
  AVFormatContext *ic;
Packit bc1512
  AVStream        *video_st;
Packit bc1512
  AVCodecContext  *enc;
Packit bc1512
  AVCodec         *codec;
Packit bc1512
  AVPacket         pkt;
Packit bc1512
  AVFrame         *lavc_frame;
Packit bc1512
Packit bc1512
  glong            coded_bytes;
Packit bc1512
  guchar          *coded_buf;
Packit bc1512
Packit bc1512
  gchar           *loadedfilename; /* to remember which file is "cached"     */
Packit bc1512
  glong            prevframe;      /* previously decoded frame in loadedfile */
Packit bc1512
} Priv;
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
print_error (const char *filename, int err)
Packit bc1512
{
Packit bc1512
  switch (err)
Packit bc1512
    {
Packit bc1512
#if LIBAVFORMAT_VERSION_MAJOR >= 53
Packit bc1512
    case AVERROR(EINVAL):
Packit bc1512
#else
Packit bc1512
    case AVERROR_NUMEXPECTED:
Packit bc1512
#endif
Packit bc1512
      g_warning ("%s: Incorrect image filename syntax.\n"
Packit bc1512
                 "Use '%%d' to specify the image number:\n"
Packit bc1512
                 "  for img1.jpg, img2.jpg, ..., use 'img%%d.jpg';\n"
Packit bc1512
                 "  for img001.jpg, img002.jpg, ..., use 'img%%03d.jpg'.\n",
Packit bc1512
                 filename);
Packit bc1512
      break;
Packit bc1512
    case AVERROR_INVALIDDATA:
Packit bc1512
#if LIBAVFORMAT_VERSION_MAJOR >= 53
Packit bc1512
      g_warning ("%s: Error while parsing header or unknown format\n", filename);
Packit bc1512
#else
Packit bc1512
      g_warning ("%s: Error while parsing header\n", filename);
Packit bc1512
      break;
Packit bc1512
    case AVERROR_NOFMT:
Packit bc1512
      g_warning ("%s: Unknown format\n", filename);
Packit bc1512
#endif
Packit bc1512
      break;
Packit bc1512
    default:
Packit bc1512
      g_warning ("%s: Error while opening file\n", filename);
Packit bc1512
      break;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
init (GeglChantO *o)
Packit bc1512
{
Packit bc1512
  static gint inited = 0; /*< this is actually meant to be static, only to be done once */
Packit bc1512
  Priv       *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  if (p==NULL)
Packit bc1512
    {
Packit bc1512
      p = g_new0 (Priv, 1);
Packit bc1512
      o->chant_data = (void*) p;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  p->width = 320;
Packit bc1512
  p->height = 200;
Packit bc1512
Packit bc1512
  if (!inited)
Packit bc1512
    {
Packit bc1512
      av_register_all ();
Packit bc1512
      avcodec_register_all ();
Packit bc1512
      inited = 1;
Packit bc1512
    }
Packit bc1512
  p->loadedfilename = g_strdup ("");
Packit bc1512
  p->fourcc = g_strdup ("");
Packit bc1512
  p->codec_name = g_strdup ("");
Packit bc1512
}
Packit bc1512
Packit bc1512
/* FIXME: probably some more stuff to free here */
Packit bc1512
static void
Packit bc1512
ff_cleanup (GeglChantO *o)
Packit bc1512
{
Packit bc1512
  Priv *p = (Priv*)o->chant_data;
Packit bc1512
  if (p)
Packit bc1512
    {
Packit bc1512
      if (p->codec_name)
Packit bc1512
        g_free (p->codec_name);
Packit bc1512
      if (p->loadedfilename)
Packit bc1512
        g_free (p->loadedfilename);
Packit bc1512
Packit bc1512
      if (p->enc)
Packit bc1512
        avcodec_close (p->enc);
Packit bc1512
      if (p->ic)
Packit bc1512
        av_close_input_file (p->ic);
Packit bc1512
      if (p->lavc_frame)
Packit bc1512
        av_free (p->lavc_frame);
Packit bc1512
Packit bc1512
      p->enc = NULL;
Packit bc1512
      p->ic = NULL;
Packit bc1512
      p->lavc_frame = NULL;
Packit bc1512
      p->codec_name = NULL;
Packit bc1512
      p->loadedfilename = NULL;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static glong
Packit bc1512
prev_keyframe (Priv *priv, glong frame)
Packit bc1512
{
Packit bc1512
  /* no way to detect previous keyframe at the moment for ffmpeg,
Packit bc1512
     so we'll just return 0, the first, and a forced reload happens
Packit bc1512
     if needed
Packit bc1512
   */
Packit bc1512
  return 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
static int
Packit bc1512
decode_frame (GeglOperation *operation,
Packit bc1512
              glong          frame)
Packit bc1512
{
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  Priv       *p = (Priv*)o->chant_data;
Packit bc1512
  glong       prevframe = p->prevframe;
Packit bc1512
  glong       decodeframe;        /*< frame to be requested decoded */
Packit bc1512
Packit bc1512
  if (frame >= p->frames)
Packit bc1512
    {
Packit bc1512
      frame = p->frames - 1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (frame < 0)
Packit bc1512
    {
Packit bc1512
      frame = 0;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (frame == prevframe)
Packit bc1512
    {
Packit bc1512
      return 0;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* figure out which frame we should start decoding at */
Packit bc1512
Packit bc1512
  if (frame == prevframe + 1)
Packit bc1512
    {
Packit bc1512
      decodeframe = prevframe + 1;
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      decodeframe = prev_keyframe (p, frame);
Packit bc1512
      if (prevframe > decodeframe && prevframe < frame)
Packit bc1512
        decodeframe = prevframe + 1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (decodeframe < prevframe)
Packit bc1512
    {
Packit bc1512
      /* seeking backwards, since it ffmpeg doesn't allow us,. we'll reload the file */
Packit bc1512
      g_free (p->loadedfilename);
Packit bc1512
      p->loadedfilename = NULL;
Packit bc1512
      init (o);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  while (decodeframe <= frame)
Packit bc1512
    {
Packit bc1512
      int       got_picture = 0;
Packit bc1512
Packit bc1512
      do
Packit bc1512
        {
Packit bc1512
          int       decoded_bytes;
Packit bc1512
Packit bc1512
          if (p->coded_bytes <= 0)
Packit bc1512
            {
Packit bc1512
              do
Packit bc1512
                {
Packit bc1512
                  if (av_read_packet (p->ic, &p->pkt) < 0)
Packit bc1512
                    {
Packit bc1512
                      fprintf (stderr, "av_read_packet failed for %s\n",
Packit bc1512
                               o->path);
Packit bc1512
                      return -1;
Packit bc1512
                    }
Packit bc1512
                }
Packit bc1512
              while (p->pkt.stream_index != p->video_stream);
Packit bc1512
Packit bc1512
              p->coded_bytes = p->pkt.size;
Packit bc1512
              p->coded_buf = p->pkt.data;
Packit bc1512
            }
Packit bc1512
          decoded_bytes =
Packit bc1512
            avcodec_decode_video2 (p->video_st->codec, p->lavc_frame,
Packit bc1512
                                  &got_picture, &p->pkt);
Packit bc1512
          if (decoded_bytes < 0)
Packit bc1512
            {
Packit bc1512
              fprintf (stderr, "avcodec_decode_video failed for %s\n",
Packit bc1512
                       o->path);
Packit bc1512
              return -1;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          p->coded_buf += decoded_bytes;
Packit bc1512
          p->coded_bytes -= decoded_bytes;
Packit bc1512
        }
Packit bc1512
      while (!got_picture);
Packit bc1512
Packit bc1512
      decodeframe++;
Packit bc1512
    }
Packit bc1512
  p->prevframe = frame;
Packit bc1512
  return 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
prepare (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  Priv       *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  if (p == NULL)
Packit bc1512
    init (o);
Packit bc1512
  p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  g_assert (o->chant_data != NULL);
Packit bc1512
Packit bc1512
  gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A u8"));
Packit bc1512
Packit bc1512
Packit bc1512
  if (!p->loadedfilename ||
Packit bc1512
      strcmp (p->loadedfilename, o->path))
Packit bc1512
    {
Packit bc1512
      gint i;
Packit bc1512
      gint err;
Packit bc1512
Packit bc1512
      ff_cleanup (o);
Packit bc1512
      err = av_open_input_file (&p->ic, o->path, NULL, 0, NULL);
Packit bc1512
      if (err < 0)
Packit bc1512
        {
Packit bc1512
          print_error (o->path, err);
Packit bc1512
        }
Packit bc1512
      err = av_find_stream_info (p->ic);
Packit bc1512
      if (err < 0)
Packit bc1512
        {
Packit bc1512
          g_warning ("ff-load: error finding stream info for %s", o->path);
Packit bc1512
Packit bc1512
          return;
Packit bc1512
        }
Packit bc1512
      for (i = 0; i< p->ic->nb_streams; i++)
Packit bc1512
        {
Packit bc1512
          AVCodecContext *c = p->ic->streams[i]->codec;
Packit bc1512
#if LIBAVFORMAT_VERSION_MAJOR >= 53
Packit bc1512
          if (c->codec_type == AVMEDIA_TYPE_VIDEO)
Packit bc1512
#else
Packit bc1512
          if (c->codec_type == CODEC_TYPE_VIDEO)
Packit bc1512
#endif
Packit bc1512
            {
Packit bc1512
              p->video_st = p->ic->streams[i];
Packit bc1512
              p->video_stream = i;
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      p->enc = p->video_st->codec;
Packit bc1512
      p->codec = avcodec_find_decoder (p->enc->codec_id);
Packit bc1512
Packit bc1512
      /* p->enc->error_resilience = 2; */
Packit bc1512
      p->enc->error_concealment = 3;
Packit bc1512
      p->enc->workaround_bugs = FF_BUG_AUTODETECT;
Packit bc1512
Packit bc1512
      if (p->codec == NULL)
Packit bc1512
        {
Packit bc1512
          g_warning ("codec not found");
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (p->codec->capabilities & CODEC_CAP_TRUNCATED)
Packit bc1512
        p->enc->flags |= CODEC_FLAG_TRUNCATED;
Packit bc1512
Packit bc1512
      if (avcodec_open (p->enc, p->codec) < 0)
Packit bc1512
        {
Packit bc1512
          g_warning ("error opening codec %s", p->enc->codec->name);
Packit bc1512
          return;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      p->width = p->enc->width;
Packit bc1512
      p->height = p->enc->height;
Packit bc1512
      p->frames = 10000000;
Packit bc1512
      p->lavc_frame = avcodec_alloc_frame ();
Packit bc1512
Packit bc1512
      if (p->fourcc)
Packit bc1512
        g_free (p->fourcc);
Packit bc1512
      p->fourcc = g_strdup ("none");
Packit bc1512
          p->fourcc[0] = (p->enc->codec_tag) & 0xff;
Packit bc1512
      p->fourcc[1] = (p->enc->codec_tag >> 8) & 0xff;
Packit bc1512
      p->fourcc[2] = (p->enc->codec_tag >> 16) & 0xff;
Packit bc1512
      p->fourcc[3] = (p->enc->codec_tag >> 24) & 0xff;
Packit bc1512
Packit bc1512
      if (p->codec_name)
Packit bc1512
        g_free (p->codec_name);
Packit bc1512
      if (p->codec->name)
Packit bc1512
        {
Packit bc1512
          p->codec_name = g_strdup (p->codec->name);
Packit bc1512
        }
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          p->codec_name = g_strdup ("");
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (p->loadedfilename)
Packit bc1512
        g_free (p->loadedfilename);
Packit bc1512
      p->loadedfilename = g_strdup (o->path);
Packit bc1512
      p->prevframe = -1;
Packit bc1512
      p->coded_bytes = 0;
Packit bc1512
      p->coded_buf = NULL;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static GeglRectangle
Packit bc1512
get_bounding_box (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GeglRectangle result = {0,0,320,200};
Packit bc1512
  Priv *p = (Priv*)GEGL_CHANT_PROPERTIES (operation)->chant_data;
Packit bc1512
  result.width = p->width;
Packit bc1512
  result.height = p->height;
Packit bc1512
  return result;
Packit bc1512
}
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
process (GeglOperation       *operation,
Packit bc1512
         GeglBuffer          *output,
Packit bc1512
         const GeglRectangle *result,
Packit bc1512
         gint                 level)
Packit bc1512
{
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  Priv       *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  {
Packit bc1512
    if (p->ic && !decode_frame (operation, o->frame))
Packit bc1512
      {
Packit bc1512
        guchar *buf;
Packit bc1512
        gint    pxsize;
Packit bc1512
        gint    x,y;
Packit bc1512
Packit bc1512
Packit bc1512
        g_object_get (output, "px-size", &pxsize, NULL);
Packit bc1512
        buf = g_new (guchar, p->width * p->height * pxsize);
Packit bc1512
Packit bc1512
        for (y=0; y < p->height; y++)
Packit bc1512
          {
Packit bc1512
            guchar       *dst  = buf + y * p->width * 4;
Packit bc1512
            const guchar *ysrc = p->lavc_frame->data[0] + y * p->lavc_frame->linesize[0];
Packit bc1512
            const guchar *usrc = p->lavc_frame->data[1] + y/2 * p->lavc_frame->linesize[1];
Packit bc1512
            const guchar *vsrc = p->lavc_frame->data[2] + y/2 * p->lavc_frame->linesize[2];
Packit bc1512
Packit bc1512
            for (x=0;x < p->width; x++)
Packit bc1512
              {
Packit bc1512
                gint R,G,B;
Packit bc1512
#ifndef byteclamp
Packit bc1512
#define byteclamp(j) do{if(j<0)j=0; else if(j>255)j=255;}while(0)
Packit bc1512
#endif
Packit bc1512
#define YUV82RGB8(Y,U,V,R,G,B)do{\
Packit bc1512
                R= ((Y<<15)                 + 37355*(V-128))>>15;\
Packit bc1512
                G= ((Y<<15) -12911* (U-128) - 19038*(V-128))>>15;\
Packit bc1512
                B= ((Y<<15) +66454* (U-128)                )>>15;\
Packit bc1512
                byteclamp(R);\
Packit bc1512
                byteclamp(G);\
Packit bc1512
                byteclamp(B);\
Packit bc1512
              } while(0)
Packit bc1512
Packit bc1512
              YUV82RGB8 (*ysrc, *usrc, *vsrc, R, G, B);
Packit bc1512
Packit bc1512
              *(unsigned int *) dst = R + G * 256 + B * 256 * 256 + 0xff000000;
Packit bc1512
              dst += 4;
Packit bc1512
              ysrc ++;
Packit bc1512
              if (x % 2)
Packit bc1512
                {
Packit bc1512
                  usrc++;
Packit bc1512
                  vsrc++;
Packit bc1512
                }
Packit bc1512
              }
Packit bc1512
          }
Packit bc1512
        gegl_buffer_set (output, NULL, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE);
Packit bc1512
        g_free (buf);
Packit bc1512
      }
Packit bc1512
  }
Packit bc1512
  return  TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
finalize (GObject *object)
Packit bc1512
{
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (object);
Packit bc1512
Packit bc1512
  if (o->chant_data)
Packit bc1512
    {
Packit bc1512
      Priv *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
      g_free (p->loadedfilename);
Packit bc1512
      g_free (p->fourcc);
Packit bc1512
      g_free (p->codec_name);
Packit bc1512
Packit bc1512
      g_free (o->chant_data);
Packit bc1512
      o->chant_data = NULL;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (gegl_chant_parent_class)->finalize (object);
Packit bc1512
}
Packit bc1512
Packit bc1512
static GeglRectangle
Packit bc1512
get_cached_region (GeglOperation       *operation,
Packit bc1512
                   const GeglRectangle *roi)
Packit bc1512
{
Packit bc1512
  return get_bounding_box (operation);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_chant_class_init (GeglChantClass *klass)
Packit bc1512
{
Packit bc1512
  GeglOperationClass       *operation_class;
Packit bc1512
  GeglOperationSourceClass *source_class;
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (klass)->finalize = finalize;
Packit bc1512
Packit bc1512
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit bc1512
  source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
Packit bc1512
Packit bc1512
  source_class->process = process;
Packit bc1512
  operation_class->get_bounding_box = get_bounding_box;
Packit bc1512
  operation_class->get_cached_region = get_cached_region;
Packit bc1512
  operation_class->prepare = prepare;
Packit bc1512
Packit bc1512
  gegl_operation_class_set_keys (operation_class,
Packit bc1512
    "name"        , "gegl:ff-load",
Packit bc1512
    "categories"  , "input:video",
Packit bc1512
    "description" , _("FFmpeg video frame importer"),
Packit bc1512
    NULL);
Packit bc1512
}
Packit bc1512
Packit bc1512
#endif