Blame operations/workshop/external/ff-save.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,2004,2007 Øyvind Kolås <pippin@gimp.org>
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
Packit bc1512
#include <stdlib.h>
Packit bc1512
Packit bc1512
#include <glib/gi18n-lib.h>
Packit bc1512
Packit bc1512
#include <libswscale/swscale.h>
Packit bc1512
Packit bc1512
Packit bc1512
#ifdef GEGL_CHANT_PROPERTIES
Packit bc1512
Packit bc1512
gegl_chant_string (path, _("File"), "/tmp/fnord.mp4", _("Target path and filename, use '-' for stdout."))
Packit bc1512
gegl_chant_double (bitrate, _("Bitrate"), 0.0, 100000000.0, 800000.0, _("target bitrate"))
Packit bc1512
gegl_chant_double (fps, _("FPS"), 0.0, 100.0, 25, _("frames per second"))
Packit bc1512
Packit bc1512
#else
Packit bc1512
Packit bc1512
#define GEGL_CHANT_TYPE_SINK
Packit bc1512
#define GEGL_CHANT_C_FILE       "ff-save.c"
Packit bc1512
Packit bc1512
#include "gegl-chant.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    frame;
Packit bc1512
  gdouble    frames;
Packit bc1512
  gdouble    width;
Packit bc1512
  gdouble    height;
Packit bc1512
  GeglBuffer *input;
Packit bc1512
Packit bc1512
  AVOutputFormat *fmt;
Packit bc1512
  AVFormatContext *oc;
Packit bc1512
  AVStream *video_st;
Packit bc1512
Packit bc1512
  AVFrame  *picture, *tmp_picture;
Packit bc1512
  uint8_t  *video_outbuf;
Packit bc1512
  int       frame_count, video_outbuf_size;
Packit bc1512
Packit bc1512
    /** the rest is for audio handling within oxide, note that the interface
Packit bc1512
     * used passes all used functions in the oxide api through the reg_sym api
Packit bc1512
     * of gggl, this means that the ops should be usable by other applications
Packit bc1512
     * using gggl directly,. without needing to link with the oxide library
Packit bc1512
     */
Packit bc1512
Packit bc1512
  AVStream *audio_st;
Packit bc1512
Packit bc1512
  void     *oxide_audio_instance;
Packit bc1512
  /*< non NULL audio_query,. means audio present */
Packit bc1512
Packit bc1512
  int32_t (*oxide_audio_query) (void *audio_instance,
Packit bc1512
                                uint32_t * sample_rate,
Packit bc1512
                                uint32_t * bits,
Packit bc1512
                                uint32_t * channels,
Packit bc1512
                                uint32_t * fragment_samples,
Packit bc1512
                                uint32_t * fragment_size);
Packit bc1512
Packit bc1512
  /* get audio samples for the current video frame, this should provide all
Packit bc1512
   * audiosamples associated with the frame, frame centering on audio stream is
Packit bc1512
   * undefined (FIXME:<<)
Packit bc1512
   */
Packit bc1512
Packit bc1512
  int32_t (*oxide_audio_get_fragment) (void *audio_instance, uint8_t * buf);
Packit bc1512
Packit bc1512
  uint32_t  sample_rate;
Packit bc1512
  uint32_t  bits;
Packit bc1512
  uint32_t  channels;
Packit bc1512
  uint32_t  fragment_samples;
Packit bc1512
  uint32_t  fragment_size;
Packit bc1512
Packit bc1512
  uint8_t  *fragment;
Packit bc1512
Packit bc1512
  int       buffer_size;
Packit bc1512
  int       buffer_read_pos;
Packit bc1512
  int       buffer_write_pos;
Packit bc1512
  uint8_t  *buffer;   /* optimal buffer size should be calculated,. preferably
Packit bc1512
                         run time,. no magic numbers needed for the filewrite
Packit bc1512
                         case, perhaps a tiny margin for ntsc since it has a
Packit bc1512
                         strange framerate */
Packit bc1512
Packit bc1512
  int       audio_outbuf_size;
Packit bc1512
  int       audio_input_frame_size;
Packit bc1512
  int16_t  *samples;
Packit bc1512
  uint8_t  *audio_outbuf;
Packit bc1512
} Priv;
Packit bc1512
Packit bc1512
#define DISABLE_AUDIO
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
  if (!inited)
Packit bc1512
    {
Packit bc1512
      av_register_all ();
Packit bc1512
      avcodec_register_all ();
Packit bc1512
      inited = 1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
#ifndef DISABLE_AUDIO
Packit bc1512
  p->oxide_audio_instance = gggl_op_sym (op, "oxide_audio_instance");
Packit bc1512
  p->oxide_audio_query = gggl_op_sym (op, "oxide_audio_query()");
Packit bc1512
  p->oxide_audio_get_fragment =
Packit bc1512
    gggl_op_sym (op, "oxide_audio_get_fragment()");
Packit bc1512
Packit bc1512
  if (p->oxide_audio_instance && p->oxide_audio_query)
Packit bc1512
    {
Packit bc1512
      p->oxide_audio_query (p->oxide_audio_instance,
Packit bc1512
                            &p->sample_rate,
Packit bc1512
                            &p->bits,
Packit bc1512
                            &p->channels,
Packit bc1512
                            &p->fragment_samples, &p->fragment_size);
Packit bc1512
Packit bc1512
      /* FIXME: for now, the buffer is set to a size double that of a oxide
Packit bc1512
       * provided fragment,. should be enough no matter how things are handled,
Packit bc1512
       * but it should also be more than needed,. find out exact amount needed later
Packit bc1512
       */
Packit bc1512
Packit bc1512
      if (!p->buffer)
Packit bc1512
        {
Packit bc1512
          int size =
Packit bc1512
            (p->sample_rate / p->fps) * p->channels * (p->bits / 8) * 2;
Packit bc1512
          buffer_open (op, size);
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (!p->fragment)
Packit bc1512
        p->fragment = gggl_op_calloc (op, 1, p->fragment_size);
Packit bc1512
    }
Packit bc1512
#endif
Packit bc1512
}
Packit bc1512
Packit bc1512
static void close_video       (Priv            *p,
Packit bc1512
                               AVFormatContext *oc,
Packit bc1512
                               AVStream        *st);
Packit bc1512
void        close_audio       (Priv            *p,
Packit bc1512
                               AVFormatContext *oc,
Packit bc1512
                               AVStream        *st);
Packit bc1512
static int  tfile             (GeglChantO      *self);
Packit bc1512
static void write_video_frame (GeglChantO      *self,
Packit bc1512
                               AVFormatContext *oc,
Packit bc1512
                               AVStream        *st);
Packit bc1512
static void write_audio_frame (GeglChantO      *self,
Packit bc1512
                               AVFormatContext *oc,
Packit bc1512
                               AVStream        *st);
Packit bc1512
Packit bc1512
Packit bc1512
Packit bc1512
Packit bc1512
#define STREAM_FRAME_RATE 25    /* 25 images/s */
Packit bc1512
Packit bc1512
static int
Packit bc1512
buffer_used (Priv * p)
Packit bc1512
{
Packit bc1512
  int       ret;
Packit bc1512
  if (!p || !p->buffer_size || !p->buffer)
Packit bc1512
    return -1;
Packit bc1512
Packit bc1512
  if (p->buffer_write_pos == p->buffer_read_pos)
Packit bc1512
    return 0;
Packit bc1512
  if (p->buffer_write_pos > p->buffer_read_pos)
Packit bc1512
    {
Packit bc1512
      ret = p->buffer_write_pos - p->buffer_read_pos;
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      ret = p->buffer_size - (p->buffer_read_pos - p->buffer_write_pos);
Packit bc1512
    }
Packit bc1512
  return ret;
Packit bc1512
}
Packit bc1512
Packit bc1512
static int
Packit bc1512
buffer_unused (Priv * p)
Packit bc1512
{
Packit bc1512
  int       ret;
Packit bc1512
  ret = p->buffer_size - buffer_used (p) - 1;   /* 1 byte for indicating full */
Packit bc1512
  return ret;
Packit bc1512
}
Packit bc1512
Packit bc1512
#ifndef DISABLE_AUDIO
Packit bc1512
static void
Packit bc1512
buffer_flush (Priv * p)
Packit bc1512
{
Packit bc1512
  if (!p->buffer)
Packit bc1512
    return;
Packit bc1512
  p->buffer_write_pos = 0;
Packit bc1512
  p->buffer_read_pos = 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
buffer_open (GeglChantOperation *op, int size)
Packit bc1512
{
Packit bc1512
  Priv     *p = (Priv*)op->priv;
Packit bc1512
Packit bc1512
  if (p->buffer)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "double init of buffer, eek!\n");
Packit bc1512
    }
Packit bc1512
Packit bc1512
  p->buffer_size = size;
Packit bc1512
  p->buffer = g_malloc (size);
Packit bc1512
  buffer_flush (p);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
buffer_close (GeglChantOperation *op)
Packit bc1512
{
Packit bc1512
  Priv     *p = (Priv*)op->priv;
Packit bc1512
Packit bc1512
  if (!p->buffer)
Packit bc1512
    return;
Packit bc1512
  g_free (p->buffer);
Packit bc1512
  p->buffer = NULL;
Packit bc1512
}
Packit bc1512
Packit bc1512
#endif
Packit bc1512
Packit bc1512
static int
Packit bc1512
buffer_write (Priv * p, uint8_t * source, int count)
Packit bc1512
{
Packit bc1512
  int       first_segment_size = 0;
Packit bc1512
  int       second_segment_size = 0;
Packit bc1512
Packit bc1512
  /* check if we have room for the data in the buffer */
Packit bc1512
  if (!p->buffer || buffer_unused (p) < count + 1)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "ff_save audio buffer full!! shouldn't happen\n");
Packit bc1512
      return -1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* calculate size of segments to write */
Packit bc1512
  first_segment_size = p->buffer_size - p->buffer_write_pos;
Packit bc1512
Packit bc1512
  if (p->buffer_read_pos > p->buffer_write_pos)
Packit bc1512
    first_segment_size = p->buffer_read_pos - p->buffer_write_pos;
Packit bc1512
Packit bc1512
  if (first_segment_size >= count)
Packit bc1512
    {
Packit bc1512
      first_segment_size = count;
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      second_segment_size = count - first_segment_size;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  memcpy (p->buffer + p->buffer_write_pos, source, first_segment_size);
Packit bc1512
  p->buffer_write_pos += first_segment_size;
Packit bc1512
Packit bc1512
  if (p->buffer_write_pos == p->buffer_size)
Packit bc1512
    p->buffer_write_pos = 0;
Packit bc1512
Packit bc1512
  if (second_segment_size)
Packit bc1512
    {
Packit bc1512
      memcpy (p->buffer + p->buffer_write_pos, source + first_segment_size,
Packit bc1512
              second_segment_size);
Packit bc1512
      p->buffer_write_pos = second_segment_size;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return first_segment_size + second_segment_size;
Packit bc1512
}
Packit bc1512
Packit bc1512
#if 0
Packit bc1512
. = unused byte
Packit bc1512
# = used byte
Packit bc1512
  all sizes are specified in contiguous bytes of memory
Packit bc1512
  __write_pos / __read_pos
Packit bc1512
  //
Packit bc1512
  |................................... | empty buffer
Packit bc1512
  \ _________________________________ /
Packit bc1512
  size_ / __read_pos __write_pos / /|........
Packit bc1512
#################..........|
Packit bc1512
  \ ______________ / used ()_ / __write_pos __read_pos / /|
Packit bc1512
############..................#####|
Packit bc1512
  \______________ / unused ()_ / ___write_pos / __read_pos / /|
Packit bc1512
#################.#################|    full buffer
Packit bc1512
#endif
Packit bc1512
     static int
Packit bc1512
     buffer_read (Priv * p, uint8_t * dest, int count)
Packit bc1512
{
Packit bc1512
  int       first_segment_size = 0;
Packit bc1512
  int       second_segment_size = 0;
Packit bc1512
Packit bc1512
  /* check if we have enough data to fulfil request */
Packit bc1512
  if (!p->buffer || buffer_used (p) < count)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "ff_save audio buffer doesn't have enough data\n");
Packit bc1512
      return -1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* calculate size of segments to write */
Packit bc1512
  first_segment_size = p->buffer_size - p->buffer_read_pos;
Packit bc1512
Packit bc1512
  if (p->buffer_write_pos > p->buffer_read_pos)
Packit bc1512
    first_segment_size = p->buffer_write_pos - p->buffer_read_pos;
Packit bc1512
Packit bc1512
  if (first_segment_size >= count)
Packit bc1512
    {
Packit bc1512
      first_segment_size = count;
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      second_segment_size = count - first_segment_size;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  memcpy (dest, p->buffer + p->buffer_read_pos, first_segment_size);
Packit bc1512
  p->buffer_read_pos += first_segment_size;
Packit bc1512
Packit bc1512
  if (p->buffer_read_pos == p->buffer_size)
Packit bc1512
    p->buffer_read_pos = 0;
Packit bc1512
Packit bc1512
  if (second_segment_size)
Packit bc1512
    {
Packit bc1512
      memcpy (dest + first_segment_size, p->buffer + p->buffer_read_pos,
Packit bc1512
              second_segment_size);
Packit bc1512
      p->buffer_read_pos = second_segment_size;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return first_segment_size + second_segment_size;
Packit bc1512
}
Packit bc1512
Packit bc1512
#ifndef DISABLE_AUDIO
Packit bc1512
/* add an audio output stream */
Packit bc1512
static AVStream *
Packit bc1512
add_audio_stream (GeglChantOperation *op, AVFormatContext * oc, int codec_id)
Packit bc1512
{
Packit bc1512
  Priv     *p = (Priv*)op->priv;
Packit bc1512
  AVCodecContext *c;
Packit bc1512
  AVStream *st;
Packit bc1512
Packit bc1512
  p = NULL;
Packit bc1512
  st = av_new_stream (oc, 1);
Packit bc1512
  if (!st)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "Could not alloc stream\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
  c->codec_id = codec_id;
Packit bc1512
  c->codec_type = CODEC_TYPE_AUDIO;
Packit bc1512
Packit bc1512
  c->bit_rate = 64000;
Packit bc1512
  c->sample_rate = 44100;
Packit bc1512
  c->channels = 2;
Packit bc1512
  return st;
Packit bc1512
}
Packit bc1512
#endif
Packit bc1512
Packit bc1512
static void
Packit bc1512
open_audio (Priv * p, AVFormatContext * oc, AVStream * st)
Packit bc1512
{
Packit bc1512
  AVCodecContext *c;
Packit bc1512
  AVCodec  *codec;
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
Packit bc1512
  /* find the audio encoder */
Packit bc1512
  codec = avcodec_find_encoder (c->codec_id);
Packit bc1512
  if (!codec)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "codec not found\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* open it */
Packit bc1512
  if (avcodec_open2 (c, codec, NULL) < 0)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "could not open codec\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  p->audio_outbuf_size = 10000;
Packit bc1512
  p->audio_outbuf = malloc (p->audio_outbuf_size);
Packit bc1512
Packit bc1512
  /* ugly hack for PCM codecs (will be removed ASAP with new PCM
Packit bc1512
     support to compute the input frame size in samples */
Packit bc1512
  if (c->frame_size <= 1)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "eeko\n");
Packit bc1512
      p->audio_input_frame_size = p->audio_outbuf_size / c->channels;
Packit bc1512
      switch (st->codec->codec_id)
Packit bc1512
        {
Packit bc1512
        case CODEC_ID_PCM_S16LE:
Packit bc1512
        case CODEC_ID_PCM_S16BE:
Packit bc1512
        case CODEC_ID_PCM_U16LE:
Packit bc1512
        case CODEC_ID_PCM_U16BE:
Packit bc1512
          p->audio_input_frame_size >>= 1;
Packit bc1512
          break;
Packit bc1512
        default:
Packit bc1512
          break;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      p->audio_input_frame_size = c->frame_size;
Packit bc1512
    }
Packit bc1512
  /*audio_input_frame_size = 44100/25;*/
Packit bc1512
  p->samples = malloc (p->audio_input_frame_size * 2 * c->channels);
Packit bc1512
}
Packit bc1512
Packit bc1512
void
Packit bc1512
write_audio_frame (GeglChantO *op, AVFormatContext * oc, AVStream * st)
Packit bc1512
{
Packit bc1512
  Priv *p = (Priv*)op->chant_data;
Packit bc1512
Packit bc1512
  AVCodecContext *c;
Packit bc1512
  AVPacket  pkt;
Packit bc1512
  av_init_packet (&pkt);
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
Packit bc1512
  /*fprintf (stderr, "going to grab %i\n", p->fragment_size);*/
Packit bc1512
  if (p->oxide_audio_get_fragment (p->oxide_audio_instance,
Packit bc1512
                                   p->fragment) == (signed) p->fragment_size)
Packit bc1512
    {
Packit bc1512
      buffer_write (p, p->fragment, p->fragment_size);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  while (buffer_used (p) >= p->audio_input_frame_size * 2 * c->channels)
Packit bc1512
    {
Packit bc1512
      buffer_read (p, (uint8_t *) p->samples,
Packit bc1512
                   p->audio_input_frame_size * 2 * c->channels);
Packit bc1512
Packit bc1512
      pkt.size = avcodec_encode_audio (c, p->audio_outbuf,
Packit bc1512
                                       p->audio_outbuf_size, p->samples);
Packit bc1512
Packit bc1512
      pkt.pts = c->coded_frame->pts;
Packit bc1512
      pkt.flags |= AV_PKT_FLAG_KEY;
Packit bc1512
      pkt.stream_index = st->index;
Packit bc1512
      pkt.data = p->audio_outbuf;
Packit bc1512
Packit bc1512
      if (av_write_frame (oc, &pkt) != 0)
Packit bc1512
        {
Packit bc1512
          fprintf (stderr, "Error while writing audio frame\n");
Packit bc1512
          exit (1);
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
/*p->audio_get_frame (samples, audio_input_frame_size, c->channels);*/
Packit bc1512
Packit bc1512
void
Packit bc1512
close_audio (Priv * p, AVFormatContext * oc, AVStream * st)
Packit bc1512
{
Packit bc1512
  avcodec_close (st->codec);
Packit bc1512
Packit bc1512
  av_free (p->samples);
Packit bc1512
  av_free (p->audio_outbuf);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* add a video output stream */
Packit bc1512
static AVStream *
Packit bc1512
add_video_stream (GeglChantO *op, AVFormatContext * oc, int codec_id)
Packit bc1512
{
Packit bc1512
  Priv *p = (Priv*)op->chant_data;
Packit bc1512
Packit bc1512
  AVCodecContext *c;
Packit bc1512
  AVStream *st;
Packit bc1512
Packit bc1512
  st = av_new_stream (oc, 0);
Packit bc1512
  if (!st)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "Could not alloc stream %p %p %i\n", op, oc, codec_id);
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
  c->codec_id = codec_id;
Packit bc1512
  c->codec_type = AVMEDIA_TYPE_VIDEO;
Packit bc1512
Packit bc1512
  /* put sample propeters */
Packit bc1512
  c->bit_rate = op->bitrate;
Packit bc1512
  /* resolution must be a multiple of two */
Packit bc1512
  c->width = p->width;
Packit bc1512
  c->height = p->height;
Packit bc1512
  /* frames per second */
Packit bc1512
  /*c->frame_rate = op->fps;
Packit bc1512
  c->frame_rate_base = 1;*/
Packit bc1512
Packit bc1512
#if LIBAVCODEC_BUILD >= 4754
Packit bc1512
  c->time_base=(AVRational){1, op->fps};
Packit bc1512
    #else
Packit bc1512
        c->frame_rate=op->fps;
Packit bc1512
        c->frame_rate_base=1;
Packit bc1512
    #endif
Packit bc1512
     c->pix_fmt = PIX_FMT_YUV420P;
Packit bc1512
Packit bc1512
Packit bc1512
  c->gop_size = 12;             /* emit one intra frame every twelve frames at most */
Packit bc1512
  if (c->codec_id == CODEC_ID_MPEG2VIDEO)
Packit bc1512
    {
Packit bc1512
      /* just for testing, we also add B frames */
Packit bc1512
      /*c->max_b_frames = 2;*/
Packit bc1512
    }
Packit bc1512
/*    if (!strcmp (oc->oformat->name, "mp4") ||
Packit bc1512
          !strcmp (oc->oformat->name, "3gp"))
Packit bc1512
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;
Packit bc1512
    */
Packit bc1512
  return st;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static AVFrame *
Packit bc1512
alloc_picture (int pix_fmt, int width, int height)
Packit bc1512
{
Packit bc1512
  AVFrame  *picture;
Packit bc1512
  uint8_t  *picture_buf;
Packit bc1512
  int       size;
Packit bc1512
Packit bc1512
  picture = avcodec_alloc_frame ();
Packit bc1512
  if (!picture)
Packit bc1512
    return NULL;
Packit bc1512
  size = avpicture_get_size (pix_fmt, width, height);
Packit bc1512
  picture_buf = malloc (size);
Packit bc1512
  if (!picture_buf)
Packit bc1512
    {
Packit bc1512
      av_free (picture);
Packit bc1512
      return NULL;
Packit bc1512
    }
Packit bc1512
  avpicture_fill ((AVPicture *) picture, picture_buf, pix_fmt, width, height);
Packit bc1512
  return picture;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
open_video (Priv * p, AVFormatContext * oc, AVStream * st)
Packit bc1512
{
Packit bc1512
  AVCodec  *codec;
Packit bc1512
  AVCodecContext *c;
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
Packit bc1512
  /* find the video encoder */
Packit bc1512
  codec = avcodec_find_encoder (c->codec_id);
Packit bc1512
  if (!codec)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "codec not found\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* open the codec */
Packit bc1512
  if (avcodec_open (c, codec) < 0)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "could not open codec\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  p->video_outbuf = NULL;
Packit bc1512
  if (!(oc->oformat->flags & AVFMT_RAWPICTURE))
Packit bc1512
    {
Packit bc1512
      /* allocate output buffer */
Packit bc1512
      /* XXX: API change will be done */
Packit bc1512
      p->video_outbuf_size = 200000;
Packit bc1512
      p->video_outbuf = malloc (p->video_outbuf_size);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* allocate the encoded raw picture */
Packit bc1512
  p->picture = alloc_picture (c->pix_fmt, c->width, c->height);
Packit bc1512
  if (!p->picture)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "Could not allocate picture\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* if the output format is not YUV420P, then a temporary YUV420P
Packit bc1512
     picture is needed too. It is then converted to the required
Packit bc1512
     output format */
Packit bc1512
  p->tmp_picture = NULL;
Packit bc1512
  if (c->pix_fmt != PIX_FMT_RGB24)
Packit bc1512
    {
Packit bc1512
      p->tmp_picture = alloc_picture (PIX_FMT_RGB24, c->width, c->height);
Packit bc1512
      if (!p->tmp_picture)
Packit bc1512
        {
Packit bc1512
          fprintf (stderr, "Could not allocate temporary picture\n");
Packit bc1512
          exit (1);
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
close_video (Priv * p, AVFormatContext * oc, AVStream * st)
Packit bc1512
{
Packit bc1512
  avcodec_close (st->codec);
Packit bc1512
  av_free (p->picture->data[0]);
Packit bc1512
  av_free (p->picture);
Packit bc1512
  if (p->tmp_picture)
Packit bc1512
    {
Packit bc1512
      av_free (p->tmp_picture->data[0]);
Packit bc1512
      av_free (p->tmp_picture);
Packit bc1512
    }
Packit bc1512
  av_free (p->video_outbuf);
Packit bc1512
}
Packit bc1512
Packit bc1512
#include "string.h"
Packit bc1512
Packit bc1512
/* prepare a dummy image */
Packit bc1512
static void
Packit bc1512
fill_yuv_image (GeglChantO *op,
Packit bc1512
                AVFrame *pict, int frame_index, int width, int height)
Packit bc1512
{
Packit bc1512
  Priv     *p = (Priv*)op->chant_data;
Packit bc1512
  /*memcpy (pict->data[0],
Packit bc1512
Packit bc1512
   op->input_pad[0]->data,
Packit bc1512
Packit bc1512
          op->input_pad[0]->width * op->input_pad[0]->height * 3);*/
Packit bc1512
  GeglRectangle rect={0,0,width,height};
Packit bc1512
  gegl_buffer_get (p->input, &rect, 1.0, babl_format ("R'G'B' u8"), pict->data[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
write_video_frame (GeglChantO *op,
Packit bc1512
                   AVFormatContext *oc, AVStream *st)
Packit bc1512
{
Packit bc1512
  Priv     *p = (Priv*)op->chant_data;
Packit bc1512
  int       out_size, ret;
Packit bc1512
  AVCodecContext *c;
Packit bc1512
  AVFrame  *picture_ptr;
Packit bc1512
Packit bc1512
  c = st->codec;
Packit bc1512
Packit bc1512
  if (c->pix_fmt != PIX_FMT_RGB24)
Packit bc1512
    {
Packit bc1512
      struct SwsContext *img_convert_ctx;
Packit bc1512
Packit bc1512
      /* as we only generate a RGB24 picture, we must convert it
Packit bc1512
         to the codec pixel format if needed */
Packit bc1512
      fill_yuv_image (op, p->tmp_picture, p->frame_count, c->width,
Packit bc1512
                      c->height);
Packit bc1512
Packit bc1512
      img_convert_ctx = sws_getContext(c->width, c->height, c->pix_fmt,
Packit bc1512
                                       c->width, c->height, PIX_FMT_RGB24,
Packit bc1512
                                       SWS_BICUBIC, NULL, NULL, NULL);
Packit bc1512
Packit bc1512
      if (img_convert_ctx == NULL)
Packit bc1512
        {
Packit bc1512
          fprintf(stderr, "ff_save: Cannot initialize conversion context.");
Packit bc1512
        }
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          sws_scale(img_convert_ctx,
Packit bc1512
                    p->tmp_picture->data,
Packit bc1512
                    p->tmp_picture->linesize,
Packit bc1512
                    0,
Packit bc1512
                    c->height,
Packit bc1512
                    p->picture->data,
Packit bc1512
                    p->picture->linesize);
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      fill_yuv_image (op, p->picture, p->frame_count, c->width, c->height);
Packit bc1512
    }
Packit bc1512
  picture_ptr = p->picture;
Packit bc1512
Packit bc1512
  if (oc->oformat->flags & AVFMT_RAWPICTURE)
Packit bc1512
    {
Packit bc1512
      /* raw video case. The API will change slightly in the near
Packit bc1512
         future for that */
Packit bc1512
      AVPacket  pkt;
Packit bc1512
      av_init_packet (&pkt);
Packit bc1512
Packit bc1512
      pkt.flags |= AV_PKT_FLAG_KEY;
Packit bc1512
      pkt.stream_index = st->index;
Packit bc1512
      pkt.data = (uint8_t *) picture_ptr;
Packit bc1512
      pkt.size = sizeof (AVPicture);
Packit bc1512
Packit bc1512
      ret = av_write_frame (oc, &pkt);
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      /* encode the image */
Packit bc1512
      out_size =
Packit bc1512
        avcodec_encode_video (c,
Packit bc1512
                              p->video_outbuf,
Packit bc1512
                              p->video_outbuf_size, picture_ptr);
Packit bc1512
Packit bc1512
      /* if zero size, it means the image was buffered */
Packit bc1512
      if (out_size != 0)
Packit bc1512
        {
Packit bc1512
          AVPacket  pkt;
Packit bc1512
          av_init_packet (&pkt);
Packit bc1512
Packit bc1512
          pkt.pts = c->coded_frame->pts;
Packit bc1512
          if (c->coded_frame->key_frame)
Packit bc1512
            pkt.flags |= AV_PKT_FLAG_KEY;
Packit bc1512
          pkt.stream_index = st->index;
Packit bc1512
          pkt.data = p->video_outbuf;
Packit bc1512
          pkt.size = out_size;
Packit bc1512
Packit bc1512
          /* write the compressed frame in the media file */
Packit bc1512
          ret = av_write_frame (oc, &pkt);
Packit bc1512
        }
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          ret = 0;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
  if (ret != 0)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "Error while writing video frame\n");
Packit bc1512
      exit (1);
Packit bc1512
    }
Packit bc1512
  p->frame_count++;
Packit bc1512
}
Packit bc1512
Packit bc1512
static int
Packit bc1512
tfile (GeglChantO *self)
Packit bc1512
{
Packit bc1512
  Priv *p = (Priv*)self->chant_data;
Packit bc1512
Packit bc1512
  p->fmt = av_guess_format (NULL, self->path, NULL);
Packit bc1512
  if (!p->fmt)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr,
Packit bc1512
               "ff_save couldn't deduce outputformat from file extension: using MPEG.\n%s",
Packit bc1512
               "");
Packit bc1512
      p->fmt = av_guess_format ("mpeg", NULL, NULL);
Packit bc1512
    }
Packit bc1512
  p->oc = avformat_alloc_context ();/*g_malloc (sizeof (AVFormatContext));*/
Packit bc1512
  if (!p->oc)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "memory error\n%s", "");
Packit bc1512
      return -1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  p->oc->oformat = p->fmt;
Packit bc1512
Packit bc1512
  snprintf (p->oc->filename, sizeof (p->oc->filename), "%s", self->path);
Packit bc1512
Packit bc1512
  p->video_st = NULL;
Packit bc1512
  p->audio_st = NULL;
Packit bc1512
Packit bc1512
  if (p->fmt->video_codec != CODEC_ID_NONE)
Packit bc1512
    {
Packit bc1512
      p->video_st = add_video_stream (self, p->oc, p->fmt->video_codec);
Packit bc1512
    }
Packit bc1512
  if (p->oxide_audio_query && p->fmt->audio_codec != CODEC_ID_NONE)
Packit bc1512
    {
Packit bc1512
     /*XXX: FOO p->audio_st = add_audio_stream (op, p->oc, p->fmt->audio_codec);*/
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (av_set_parameters (p->oc, NULL) < 0)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "Invalid output format propeters\n%s", "");
Packit bc1512
      return -1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  dump_format (p->oc, 0, self->path, 1);
Packit bc1512
Packit bc1512
  if (p->video_st)
Packit bc1512
    open_video (p, p->oc, p->video_st);
Packit bc1512
  if (p->audio_st)
Packit bc1512
    open_audio (p, p->oc, p->audio_st);
Packit bc1512
Packit bc1512
  if (url_fopen (&p->oc->pb, self->path, URL_WRONLY) < 0)
Packit bc1512
    {
Packit bc1512
      fprintf (stderr, "couldn't open '%s'\n", self->path);
Packit bc1512
      return -1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  av_write_header (p->oc);
Packit bc1512
Packit bc1512
  return 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
#if 0
Packit bc1512
static int
Packit bc1512
filechanged (GeglChantOperation *op, const char *att)
Packit bc1512
{
Packit bc1512
  init (op);
Packit bc1512
  return 0;
Packit bc1512
}
Packit bc1512
#endif
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
process (GeglOperation       *operation,
Packit bc1512
         GeglBuffer          *input,
Packit bc1512
         const GeglRectangle *result,
Packit bc1512
         gint                 level)
Packit bc1512
{
Packit bc1512
  static gint inited = 0;
Packit bc1512
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  Priv       *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  g_assert (input);
Packit bc1512
Packit bc1512
  if (p == NULL)
Packit bc1512
    init (o);
Packit bc1512
  p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
  p->width = result->width;
Packit bc1512
  p->height = result->height;
Packit bc1512
  p->input = input;
Packit bc1512
Packit bc1512
  if (!inited)
Packit bc1512
    {
Packit bc1512
      tfile (o);
Packit bc1512
      inited = 1;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  write_video_frame (o, p->oc, p->video_st);
Packit bc1512
  if (p->audio_st)
Packit bc1512
    write_audio_frame (o, p->oc, p->audio_st);
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
  if (o->chant_data)
Packit bc1512
    {
Packit bc1512
      Priv *p = (Priv*)o->chant_data;
Packit bc1512
Packit bc1512
    if (p->oc)
Packit bc1512
      {
Packit bc1512
        gint i;
Packit bc1512
        if (p->video_st)
Packit bc1512
          close_video (p, p->oc, p->video_st);
Packit bc1512
        if (p->audio_st)
Packit bc1512
          close_audio (p, p->oc, p->audio_st);
Packit bc1512
Packit bc1512
        av_write_trailer (p->oc);
Packit bc1512
Packit bc1512
        for (i = 0; i < p->oc->nb_streams; i++)
Packit bc1512
          {
Packit bc1512
            av_freep (&p->oc->streams[i]);
Packit bc1512
          }
Packit bc1512
Packit bc1512
        url_fclose (&p->oc->pb);
Packit bc1512
        free (p->oc);
Packit bc1512
      }
Packit bc1512
      g_free (o->chant_data);
Packit bc1512
      o->chant_data = NULL;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (object)))->finalize (object);
Packit bc1512
}
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
  GeglOperationSinkClass *sink_class;
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (klass)->finalize = finalize;
Packit bc1512
Packit bc1512
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit bc1512
  sink_class      = GEGL_OPERATION_SINK_CLASS (klass);
Packit bc1512
Packit bc1512
  sink_class->process = process;
Packit bc1512
  sink_class->needs_full = TRUE;
Packit bc1512
Packit bc1512
  gegl_operation_class_set_keys (operation_class,
Packit bc1512
    "name"        , "gegl:ff-save",
Packit bc1512
    "categories"  , "output:video",
Packit bc1512
    "description" , _("FFmpeg video output sink"),
Packit bc1512
    NULL);
Packit bc1512
}
Packit bc1512
Packit bc1512
#endif