ff-save.c 23.3 KB
Newer Older
1 2 3 4 5
/* This file is an image processing operation for GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
6
 * version 3 of the License, or (at your option) any later version.
7 8 9 10 11 12 13
 *
 * GEGL 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
14
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
15
 *
16
 * Copyright 2003,2004,2007, 2015 Øyvind Kolås <pippin@gimp.org>
17
 */
18 19

#include "config.h"
Martin Nordholts's avatar
Martin Nordholts committed
20 21 22

#include <stdlib.h>

23 24
#include <glib/gi18n-lib.h>

25
#ifdef GEGL_PROPERTIES
26

27
property_string (path, _("File"), "/tmp/fnord.ogv")
28
    description (_("Target path and filename, use '-' for stdout."))
29 30 31

property_double (frame_rate, _("Frames/second"), 25.0)
    value_range (0.0, 100.0)
32
#if 0
33 34 35 36 37
property_string (audio_codec, _("Audio codec"), "auto")
property_int (audio_bit_rate, _("Audio bitrate"), 810000)
property_string (video_codec,   _("Video codec"),   "auto")
property_int (video_bit_rate, _("video bitrate"), 810000)
    value_range (0.0, 500000000.0)
38 39 40 41 42 43 44 45
property_double (video_bit_rate_tolerance, _("video bitrate"), 1000.0)
property_int    (video_global_quality, _("global quality"), 255)
property_int    (compression_level,    _("compression level"), 255)
property_int    (noise_reduction,      _("noise reduction strength"), 0)
property_int    (gop_size,             _("the number of frames in a group of pictures, 0 for keyframe only"), 16)
property_int    (key_int_min,          _("the minimum number of frames in a group of pictures, 0 for keyframe only"), 1)
property_int    (max_b_frames,         _("maximum number of consequetive b frames"), 3)
#endif
46

47

48
property_audio_fragment (audio, _("audio"), 0)
49

50 51
#else

52
#define GEGL_OP_SINK
53
#define GEGL_OP_C_SOURCE ff-save.c
54

55
#include "gegl-op.h"
56

57
#include <libavformat/avformat.h>
58 59 60 61
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

typedef struct
{
  gdouble    frame;
  gdouble    frames;
  gdouble    width;
  gdouble    height;
  GeglBuffer *input;

  AVOutputFormat *fmt;
  AVFormatContext *oc;
  AVStream *video_st;

  AVFrame  *picture, *tmp_picture;
  uint8_t  *video_outbuf;
  int       frame_count, video_outbuf_size;
78
  int       audio_sample_rate;
79

80 81 82 83
    /** the rest is for audio handling within oxide, note that the interface
     * used passes all used functions in the oxide api through the reg_sym api
     * of gggl, this means that the ops should be usable by other applications
     * using gggl directly,. without needing to link with the oxide library
84 85 86 87 88 89 90 91 92 93 94 95
     */
  AVStream *audio_st;

  uint32_t  sample_rate;
  uint32_t  bits;
  uint32_t  channels;
  uint32_t  fragment_samples;
  uint32_t  fragment_size;

  int       buffer_size;
  int       buffer_read_pos;
  int       buffer_write_pos;
96 97
  uint8_t  *buffer; 
                   
98 99
  int       audio_outbuf_size;
  int16_t  *samples;
100

101 102 103
  GList    *audio_track;
  long      audio_pos;
  long      audio_read_pos;
104 105
  
  int       next_apts;
106 107
} Priv;

108 109 110 111 112 113
static void
clear_audio_track (GeglProperties *o)
{
  Priv *p = (Priv*)o->user_data;
  while (p->audio_track)
    {
114
      g_object_unref (p->audio_track->data);
115 116 117 118 119 120 121 122 123 124
      p->audio_track = g_list_remove (p->audio_track, p->audio_track->data);
    }
}

static void get_sample_data (Priv *p, long sample_no, float *left, float *right)
{
  int to_remove = 0;
  GList *l;
  if (sample_no < 0)
    return;
125
  for (l = p->audio_track; l; l = l->next)
126
  {
127
    GeglAudioFragment *af = l->data;
128 129 130
    int channels = gegl_audio_fragment_get_channels (af);
    int pos = gegl_audio_fragment_get_pos (af);
    int sample_count = gegl_audio_fragment_get_sample_count (af);
131
    if (sample_no > pos + sample_count)
132 133 134 135
    {
      to_remove ++;
    }

136 137
    if (pos <= sample_no &&
        sample_no < pos + sample_count)
138
      {
139
        int i = sample_no - pos;
140
        *left  = af->data[0][i];
141
        if (channels == 1)
142 143 144 145 146 147 148 149 150
          *right = af->data[0][i];
        else
          *right = af->data[1][i];

	if (to_remove)  /* consuming audiotrack */
        {
          again:
          for (l = p->audio_track; l; l = l->next)
          {
151
            GeglAudioFragment *af = l->data;
152 153
            int pos = gegl_audio_fragment_get_pos (af);
            int sample_count = gegl_audio_fragment_get_sample_count (af);
154
            if (sample_no > pos + sample_count)
155 156
            {
              p->audio_track = g_list_remove (p->audio_track, af);
157
              g_object_unref (af);
158 159 160 161 162 163 164 165 166 167
              goto again;
            }
          }
        }
        return;
      }
  }
  *left  = 0;
  *right = 0;
}
168

Kevin Cozens's avatar
Kevin Cozens committed
169
static void
170
init (GeglProperties *o)
171
{
Kevin Cozens's avatar
Kevin Cozens committed
172
  static gint inited = 0; /*< this is actually meant to be static, only to be done once */
173
  Priv       *p = (Priv*)o->user_data;
Kevin Cozens's avatar
Kevin Cozens committed
174 175

  if (p == NULL)
176
    {
Kevin Cozens's avatar
Kevin Cozens committed
177
      p = g_new0 (Priv, 1);
178
      o->user_data = (void*) p;
179 180 181 182 183 184 185 186 187
    }

  if (!inited)
    {
      av_register_all ();
      avcodec_register_all ();
      inited = 1;
    }

188 189 190
  clear_audio_track (o);
  p->audio_pos = 0;
  p->audio_read_pos = 0;
191 192

  p->audio_sample_rate = -1; /* only do this if it hasn't been manually set? */
193 194
}

Kevin Cozens's avatar
Kevin Cozens committed
195 196 197 198 199 200
static void close_video       (Priv            *p,
                               AVFormatContext *oc,
                               AVStream        *st);
void        close_audio       (Priv            *p,
                               AVFormatContext *oc,
                               AVStream        *st);
201 202
static int  tfile             (GeglProperties  *o);
static void write_video_frame (GeglProperties  *o,
Kevin Cozens's avatar
Kevin Cozens committed
203 204
                               AVFormatContext *oc,
                               AVStream        *st);
205
static void write_audio_frame (GeglProperties      *o,
Kevin Cozens's avatar
Kevin Cozens committed
206 207
                               AVFormatContext *oc,
                               AVStream        *st);
208 209 210

#define STREAM_FRAME_RATE 25    /* 25 images/s */

211
#ifndef DISABLE_AUDIO
212 213
/* add an audio output stream */
static AVStream *
214
add_audio_stream (GeglProperties *o, AVFormatContext * oc, int codec_id)
215 216 217 218
{
  AVCodecContext *c;
  AVStream *st;

219
  st = avformat_new_stream (oc, NULL);
220 221 222 223 224 225 226 227
  if (!st)
    {
      fprintf (stderr, "Could not alloc stream\n");
      exit (1);
    }

  c = st->codec;
  c->codec_id = codec_id;
228
  c->codec_type = AVMEDIA_TYPE_AUDIO;
229

230 231 232
  if (oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;

233 234
  return st;
}
235
#endif
236 237

static void
238
open_audio (GeglProperties *o, AVFormatContext * oc, AVStream * st)
239
{
240
  Priv *p = (Priv*)o->user_data;
241 242
  AVCodecContext *c;
  AVCodec  *codec;
243
  int i;
244 245 246 247 248 249 250 251 252 253

  c = st->codec;

  /* find the audio encoder */
  codec = avcodec_find_encoder (c->codec_id);
  if (!codec)
    {
      fprintf (stderr, "codec not found\n");
      exit (1);
    }
254 255 256
  c->bit_rate = 64000;
  c->sample_fmt = codec->sample_fmts ? codec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

257 258 259 260
  if (p->audio_sample_rate == -1)
  {
    if (o->audio)
    {
261
      if (gegl_audio_fragment_get_sample_rate (o->audio) == 0)
262
      {
263
        gegl_audio_fragment_set_sample_rate (o->audio, 48000); // XXX: should skip adding audiostream instead
264
      }
265
      p->audio_sample_rate = gegl_audio_fragment_get_sample_rate (o->audio);
266 267 268
    }
  }
  c->sample_rate = p->audio_sample_rate;
269 270 271
  c->channel_layout = AV_CH_LAYOUT_STEREO;
  c->channels = 2;

272

273 274 275 276
  if (codec->supported_samplerates)
  {
    c->sample_rate = codec->supported_samplerates[0];
    for (i = 0; codec->supported_samplerates[i]; i++)
277 278 279 280
    {
      if (codec->supported_samplerates[i] == p->audio_sample_rate)
         c->sample_rate = p->audio_sample_rate;
    }
281
  }
282 283
  //st->time_base = (AVRational){1, c->sample_rate};
  st->time_base = (AVRational){1, p->audio_sample_rate};
284 285

  c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; // ffmpeg AAC is not quite stable yet
286 287

  /* open it */
288
  if (avcodec_open2 (c, codec, NULL) < 0)
289 290 291 292 293 294
    {
      fprintf (stderr, "could not open codec\n");
      exit (1);
    }
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
static AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt,
                                  uint64_t channel_layout,
                                  int sample_rate, int nb_samples)
{
  AVFrame *frame = av_frame_alloc();
  int ret;

  if (!frame) {
      fprintf(stderr, "Error allocating an audio frame\n");
      exit(1);
  }

  frame->format         = sample_fmt;
  frame->channel_layout = channel_layout;
  frame->sample_rate    = sample_rate;
  frame->nb_samples     = nb_samples;

  if (nb_samples) {
      ret = av_frame_get_buffer(frame, 0);
      if (ret < 0) {
          fprintf(stderr, "Error allocating an audio buffer\n");
          exit(1);
      }
  }
  return frame;
}

322
void
323
write_audio_frame (GeglProperties *o, AVFormatContext * oc, AVStream * st)
324
{
325
  Priv *p = (Priv*)o->user_data;
326 327 328 329 330 331 332 333
  AVCodecContext *c = st->codec;
  int sample_count = 100000;
  static AVPacket  pkt = { 0 };

  if (pkt.size == 0)
  {
    av_init_packet (&pkt);
  }
334

335 336 337
  /* first we add incoming frames audio samples */
  {
    int i;
338 339 340 341 342
    int sample_count = gegl_audio_fragment_get_sample_count (o->audio);
    GeglAudioFragment *af = gegl_audio_fragment_new (gegl_audio_fragment_get_sample_rate (o->audio),
                                                     gegl_audio_fragment_get_channels (o->audio),
                                                     gegl_audio_fragment_get_channel_layout (o->audio),
                                                     sample_count);
343
    gegl_audio_fragment_set_sample_count (af, sample_count);
344
    for (i = 0; i < sample_count; i++)
345
      {
346 347
        af->data[0][i] = o->audio->data[0][i];
        af->data[1][i] = o->audio->data[1][i];
348
      }
349 350
    gegl_audio_fragment_set_pos (af, p->audio_pos);
    p->audio_pos += sample_count;
351 352 353
    p->audio_track = g_list_append (p->audio_track, af);
  }

354 355 356
  if (!(c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))
    sample_count = c->frame_size;

357
  /* then we encode as much as we can in a loop using the codec frame size */
358

359
  
360
  while (p->audio_pos - p->audio_read_pos > sample_count)
361 362
  {
    long i;
363 364
    int ret;
    int got_packet = 0;
365
    AVFrame *frame = alloc_audio_frame (c->sample_fmt, c->channel_layout,
366
                                        c->sample_rate, sample_count);
367

368 369
    switch (c->sample_fmt) {
      case AV_SAMPLE_FMT_FLT:
370
        for (i = 0; i < sample_count; i++)
371 372 373 374 375 376 377 378
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((float*)frame->data[0])[c->channels*i+0] = left;
          ((float*)frame->data[0])[c->channels*i+1] = right;
        }
        break;
      case AV_SAMPLE_FMT_FLTP:
379
        for (i = 0; i < sample_count; i++)
380 381 382 383 384 385 386
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((float*)frame->data[0])[i] = left;
          ((float*)frame->data[1])[i] = right;
        }
        break;
387
      case AV_SAMPLE_FMT_S16:
388
        for (i = 0; i < sample_count; i++)
389 390 391 392 393 394 395
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((int16_t*)frame->data[0])[c->channels*i+0] = left * (1<<15);
          ((int16_t*)frame->data[0])[c->channels*i+1] = right * (1<<15);
        }
        break;
396
      case AV_SAMPLE_FMT_S32:
397
        for (i = 0; i < sample_count; i++)
398 399 400 401 402 403 404 405
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((int32_t*)frame->data[0])[c->channels*i+0] = left * (1<<31);
          ((int32_t*)frame->data[0])[c->channels*i+1] = right * (1<<31);
        }
        break;
      case AV_SAMPLE_FMT_S32P:
406
        for (i = 0; i < sample_count; i++)
407 408 409 410 411 412
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((int32_t*)frame->data[0])[i] = left * (1<<31);
          ((int32_t*)frame->data[1])[i] = right * (1<<31);
        }
413 414
        break;
      case AV_SAMPLE_FMT_S16P:
415
        for (i = 0; i < sample_count; i++)
416 417 418 419 420 421
        {
          float left = 0, right = 0;
          get_sample_data (p, i + p->audio_read_pos, &left, &right);
          ((int16_t*)frame->data[0])[i] = left * (1<<15);
          ((int16_t*)frame->data[1])[i] = right * (1<<15);
        }
422 423 424 425 426
        break;
      default:
        fprintf (stderr, "eeeek unhandled audio format\n");
        break;
    }
427 428
    frame->pts = av_rescale_q (p->next_apts, (AVRational){1, c->sample_rate}, c->time_base);
    p->next_apts += sample_count;
429 430 431 432 433 434 435 436

    av_frame_make_writable (frame);
    ret = avcodec_encode_audio2 (c, &pkt, frame, &got_packet);
    if (ret < 0) {
      fprintf (stderr, "Error encoding audio frame: %s\n", av_err2str (ret));
    }

    if (got_packet)
437
    {
438 439
      pkt.stream_index = st->index;
      av_interleaved_write_frame (oc, &pkt);
440
      av_free_packet (&pkt);
441 442 443
    }

    av_frame_free (&frame);
444
    p->audio_read_pos += sample_count;
445
  }
446 447 448 449 450 451 452 453 454 455 456
}

void
close_audio (Priv * p, AVFormatContext * oc, AVStream * st)
{
  avcodec_close (st->codec);

}

/* add a video output stream */
static AVStream *
457
add_video_stream (GeglProperties *o, AVFormatContext * oc, int codec_id)
458
{
459
  Priv *p = (Priv*)o->user_data;
460 461 462 463

  AVCodecContext *c;
  AVStream *st;

464
  st = avformat_new_stream (oc, NULL);
465 466
  if (!st)
    {
467
      fprintf (stderr, "Could not alloc stream %p %p %i\n", o, oc, codec_id);
468 469 470 471 472
      exit (1);
    }

  c = st->codec;
  c->codec_id = codec_id;
473
  c->codec_type = AVMEDIA_TYPE_VIDEO;
474
  /* put sample propeters */
475
  c->bit_rate = 810000;
476 477 478 479
  /* resolution must be a multiple of two */
  c->width = p->width;
  c->height = p->height;
  /* frames per second */
480
  st->time_base =(AVRational){1, o->frame_rate};
481 482 483
  c->time_base = st->time_base;
  c->pix_fmt = AV_PIX_FMT_YUV420P;
  c->gop_size = 12;             /* emit one intra frame every twelve frames at most */
484

485
  if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
486
    {
487
      c->max_b_frames = 2;
488
    }
489 490
  if (c->codec_id == AV_CODEC_ID_H264)
   {
491 492 493 494 495 496 497
#if 1
     c->qcompress = 0.6;  // qcomp=0.6
     c->me_range = 16;    // me_range=16
     c->gop_size = 250;   // g=250

     c->max_b_frames = 3; // bf=3
#if 0
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
     c->coder_type = 1;  // coder = 1
     c->flags|=CODEC_FLAG_LOOP_FILTER;   // flags=+loop
     c->me_cmp|= 1;  // cmp=+chroma, where CHROMA = 1
     //c->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8
     c->me_subpel_quality = 7;   // subq=7
     c->keyint_min = 25; // keyint_min=25
     c->scenechange_threshold = 40;  // sc_threshold=40
     c->i_quant_factor = 0.71; // i_qfactor=0.71
     c->b_frame_strategy = 1;  // b_strategy=1
     c->qmin = 10;   // qmin=10
     c->qmax = 51;   // qmax=51
     c->max_qdiff = 4;   // qdiff=4
     c->refs = 3;    // refs=3
     //c->directpred = 1;  // directpred=1
     c->trellis = 1; // trellis=1
     //c->flags2|=AV_CODEC_FLAG2_BPYRAMID|AV_CODEC_FLAG2_MIXED_REFS|AV_CODEC_FLAG2_WPRED+CODEC_FLAG2_8X8DCT+CODEC_FLAG2_FASTPSKIP;  // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip
     //c->weighted_p_pred = 2; // wpredp=2

// libx264-main.ffpreset preset
     //c->flags2|=CODEC_FLAG2_8X8DCT;c->flags2^=CODEC_FLAG2_8X8DCT;
518 519
#endif
#endif
520 521
   }

522 523 524
   if (oc->oformat->flags & AVFMT_GLOBALHEADER)
     c->flags |= CODEC_FLAG_GLOBAL_HEADER;

525 526 527 528 529 530 531 532 533 534 535
  return st;
}


static AVFrame *
alloc_picture (int pix_fmt, int width, int height)
{
  AVFrame  *picture;
  uint8_t  *picture_buf;
  int       size;

536
  picture = av_frame_alloc ();
537 538
  if (!picture)
    return NULL;
539
  size = avpicture_get_size (pix_fmt, width, height + 1);
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
  picture_buf = malloc (size);
  if (!picture_buf)
    {
      av_free (picture);
      return NULL;
    }
  avpicture_fill ((AVPicture *) picture, picture_buf, pix_fmt, width, height);
  return picture;
}

static void
open_video (Priv * p, AVFormatContext * oc, AVStream * st)
{
  AVCodec  *codec;
  AVCodecContext *c;

  c = st->codec;

  /* find the video encoder */
  codec = avcodec_find_encoder (c->codec_id);
  if (!codec)
    {
      fprintf (stderr, "codec not found\n");
      exit (1);
    }

  /* open the codec */
567
  if (avcodec_open2 (c, codec, NULL) < 0)
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    {
      fprintf (stderr, "could not open codec\n");
      exit (1);
    }

  p->video_outbuf = NULL;
  if (!(oc->oformat->flags & AVFMT_RAWPICTURE))
    {
      /* allocate output buffer */
      /* XXX: API change will be done */
      p->video_outbuf_size = 200000;
      p->video_outbuf = malloc (p->video_outbuf_size);
    }

  /* allocate the encoded raw picture */
  p->picture = alloc_picture (c->pix_fmt, c->width, c->height);
  if (!p->picture)
    {
      fprintf (stderr, "Could not allocate picture\n");
      exit (1);
    }

  /* if the output format is not YUV420P, then a temporary YUV420P
     picture is needed too. It is then converted to the required
     output format */
  p->tmp_picture = NULL;
594
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
595
    {
596
      p->tmp_picture = alloc_picture (AV_PIX_FMT_RGB24, c->width, c->height);
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
      if (!p->tmp_picture)
        {
          fprintf (stderr, "Could not allocate temporary picture\n");
          exit (1);
        }
    }
}

static void
close_video (Priv * p, AVFormatContext * oc, AVStream * st)
{
  avcodec_close (st->codec);
  av_free (p->picture->data[0]);
  av_free (p->picture);
  if (p->tmp_picture)
    {
      av_free (p->tmp_picture->data[0]);
      av_free (p->tmp_picture);
    }
  av_free (p->video_outbuf);
}

#include "string.h"

/* prepare a dummy image */
static void
623
fill_rgb_image (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
624
                AVFrame *pict, int frame_index, int width, int height)
625
{
626
  Priv     *p = (Priv*)o->user_data;
627
  GeglRectangle rect={0,0,width,height};
628
  gegl_buffer_get (p->input, &rect, 1.0, babl_format ("R'G'B' u8"), pict->data[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
629 630 631
}

static void
632
write_video_frame (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
633
                   AVFormatContext *oc, AVStream *st)
634
{
635 636
  Priv           *p = (Priv*)o->user_data;
  int             out_size, ret;
637
  AVCodecContext *c;
638
  AVFrame        *picture_ptr;
639 640 641

  c = st->codec;

642
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
643
    {
644
      struct SwsContext *img_convert_ctx;
645
      fill_rgb_image (o, p->tmp_picture, p->frame_count, c->width,
646
                      c->height);
647

648
      img_convert_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_RGB24,
649
                                       c->width, c->height, c->pix_fmt,
650 651 652 653 654 655 656 657 658
                                       SWS_BICUBIC, NULL, NULL, NULL);

      if (img_convert_ctx == NULL)
        {
          fprintf(stderr, "ff_save: Cannot initialize conversion context.");
        }
      else
        {
          sws_scale(img_convert_ctx,
659
                    (void*)p->tmp_picture->data,
660 661 662 663 664
                    p->tmp_picture->linesize,
                    0,
                    c->height,
                    p->picture->data,
                    p->picture->linesize);
665 666 667
         p->picture->format = c->pix_fmt;
         p->picture->width = c->width;
         p->picture->height = c->height;
668
        }
669 670 671
    }
  else
    {
672
      fill_rgb_image (o, p->picture, p->frame_count, c->width, c->height);
673
    }
674

675
  picture_ptr      = p->picture;
676
  picture_ptr->pts = p->frame_count;
677 678 679 680 681 682 683 684

  if (oc->oformat->flags & AVFMT_RAWPICTURE)
    {
      /* raw video case. The API will change slightly in the near
         future for that */
      AVPacket  pkt;
      av_init_packet (&pkt);

685
      pkt.flags |= AV_PKT_FLAG_KEY;
686 687 688
      pkt.stream_index = st->index;
      pkt.data = (uint8_t *) picture_ptr;
      pkt.size = sizeof (AVPicture);
689 690
      pkt.pts = picture_ptr->pts;
      av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707

      ret = av_write_frame (oc, &pkt);
    }
  else
    {
      /* encode the image */
      out_size =
        avcodec_encode_video (c,
                              p->video_outbuf,
                              p->video_outbuf_size, picture_ptr);

      /* if zero size, it means the image was buffered */
      if (out_size != 0)
        {
          AVPacket  pkt;
          av_init_packet (&pkt);
          if (c->coded_frame->key_frame)
708
            pkt.flags |= AV_PKT_FLAG_KEY;
709 710 711
          pkt.stream_index = st->index;
          pkt.data = p->video_outbuf;
          pkt.size = out_size;
712 713
          pkt.pts = picture_ptr->pts;
          av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
          /* write the compressed frame in the media file */
          ret = av_write_frame (oc, &pkt);
        }
      else
        {
          ret = 0;
        }
    }
  if (ret != 0)
    {
      fprintf (stderr, "Error while writing video frame\n");
      exit (1);
    }
  p->frame_count++;
}

static int
731
tfile (GeglProperties *o)
732
{
733
  Priv *p = (Priv*)o->user_data;
734

735
  p->fmt = av_guess_format (NULL, o->path, NULL);
736 737 738 739 740
  if (!p->fmt)
    {
      fprintf (stderr,
               "ff_save couldn't deduce outputformat from file extension: using MPEG.\n%s",
               "");
741
      p->fmt = av_guess_format ("mpeg", NULL, NULL);
742
    }
743
  p->oc = avformat_alloc_context ();
744 745 746 747 748 749 750 751
  if (!p->oc)
    {
      fprintf (stderr, "memory error\n%s", "");
      return -1;
    }

  p->oc->oformat = p->fmt;

752
  snprintf (p->oc->filename, sizeof (p->oc->filename), "%s", o->path);
753 754 755 756

  p->video_st = NULL;
  p->audio_st = NULL;

757
  if (p->fmt->video_codec != AV_CODEC_ID_NONE)
758
    {
759
      p->video_st = add_video_stream (o, p->oc, p->fmt->video_codec);
760
    }
761
  if (p->fmt->audio_codec != AV_CODEC_ID_NONE)
762
    {
763
     p->audio_st = add_audio_stream (o, p->oc, p->fmt->audio_codec);
764 765 766 767 768
    }


  if (p->video_st)
    open_video (p, p->oc, p->video_st);
769

770
  if (p->audio_st)
771
    open_audio (o, p->oc, p->audio_st);
772

773 774
  av_dump_format (p->oc, 0, o->path, 1);

775
  if (avio_open (&p->oc->pb, o->path, AVIO_FLAG_WRITE) < 0)
776
    {
777
      fprintf (stderr, "couldn't open '%s'\n", o->path);
778 779 780
      return -1;
    }

781
  avformat_write_header (p->oc, NULL);
782 783 784
  return 0;
}

Kevin Cozens's avatar
Kevin Cozens committed
785 786 787
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
788 789
         const GeglRectangle *result,
         gint                 level)
Kevin Cozens's avatar
Kevin Cozens committed
790
{
791
  GeglProperties *o = GEGL_PROPERTIES (operation);
792 793
  Priv           *p = (Priv*)o->user_data;
  static gint     inited = 0;
Kevin Cozens's avatar
Kevin Cozens committed
794 795 796 797 798

  g_assert (input);

  if (p == NULL)
    init (o);
799
  p = (Priv*)o->user_data;
Kevin Cozens's avatar
Kevin Cozens committed
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820

  p->width = result->width;
  p->height = result->height;
  p->input = input;

  if (!inited)
    {
      tfile (o);
      inited = 1;
    }

  write_video_frame (o, p->oc, p->video_st);
  if (p->audio_st)
    write_audio_frame (o, p->oc, p->audio_st);

  return  TRUE;
}

static void
finalize (GObject *object)
{
821 822
  GeglProperties *o = GEGL_PROPERTIES (object);
  if (o->user_data)
Kevin Cozens's avatar
Kevin Cozens committed
823
    {
824
      Priv *p = (Priv*)o->user_data;
825

Kevin Cozens's avatar
Kevin Cozens committed
826 827 828 829 830 831 832 833
    if (p->oc)
      {
        if (p->video_st)
          close_video (p, p->oc, p->video_st);
        if (p->audio_st)
          close_audio (p, p->oc, p->audio_st);

        av_write_trailer (p->oc);
834 835
        avio_closep (&p->oc->pb);
        avformat_free_context (p->oc);
Kevin Cozens's avatar
Kevin Cozens committed
836
      }
837

838 839
      g_free (o->user_data);
      o->user_data = NULL;
Kevin Cozens's avatar
Kevin Cozens committed
840 841 842 843 844 845 846
    }

  G_OBJECT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (object)))->finalize (object);
}


static void
847
gegl_op_class_init (GeglOpClass *klass)
Kevin Cozens's avatar
Kevin Cozens committed
848 849 850 851 852 853 854 855 856 857 858 859
{
  GeglOperationClass     *operation_class;
  GeglOperationSinkClass *sink_class;

  G_OBJECT_CLASS (klass)->finalize = finalize;

  operation_class = GEGL_OPERATION_CLASS (klass);
  sink_class      = GEGL_OPERATION_SINK_CLASS (klass);

  sink_class->process = process;
  sink_class->needs_full = TRUE;

860 861 862 863 864
  gegl_operation_class_set_keys (operation_class,
    "name"        , "gegl:ff-save",
    "categories"  , "output:video",
    "description" , _("FFmpeg video output sink"),
    NULL);
Kevin Cozens's avatar
Kevin Cozens committed
865 866
}

867
#endif