ff-save.c 23.1 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 100
  int       audio_outbuf_size;
  int       audio_input_frame_size;
  int16_t  *samples;
101

102 103 104
  GList    *audio_track;
  long      audio_pos;
  long      audio_read_pos;
105 106
} Priv;

107 108 109 110 111 112
static void
clear_audio_track (GeglProperties *o)
{
  Priv *p = (Priv*)o->user_data;
  while (p->audio_track)
    {
113
      g_object_unref (p->audio_track->data);
114 115 116 117 118 119 120 121 122 123 124 125 126
      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;
  l = p->audio_track;
  if (sample_no < 0)
    return;
  for (; l; l = l->next)
  {
127
    GeglAudioFragment *af = l->data;
128 129 130 131
    gint pos = gegl_audio_fragment_get_pos (af);
    gint channels = gegl_audio_fragment_get_channels (af);
    gint sample_count = gegl_audio_fragment_get_sample_count (af);
    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 154
            gint pos = gegl_audio_fragment_get_pos (af);
            gint sample_count = gegl_audio_fragment_get_sample_count (af);
            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
              goto again;
            }
          }
        }
        return;
      }
  }
165
  fprintf (stderr, "ff-save didn't find audio sample\n");
166 167 168
  *left  = 0;
  *right = 0;
}
169

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

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

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

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

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

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

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

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

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

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

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

234 235
  return st;
}
236
#endif
237 238

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

  c = st->codec;

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

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

273

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

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

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

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 322
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;
}

323
void
324
write_audio_frame (GeglProperties *o, AVFormatContext * oc, AVStream * st)
325
{
326
  Priv *p = (Priv*)o->user_data;
327
  AVCodecContext *c;
328
  AVPacket  pkt = { 0 };
329 330
  av_init_packet (&pkt);

331 332 333
  /* first we add incoming frames audio samples */
  {
    int i;
334 335 336 337 338 339
    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);
    for (i = 0; i < sample_count; i++)
340
      {
341 342
        af->data[0][i] = o->audio->data[0][i];
        af->data[1][i] = o->audio->data[1][i];
343
      }
344 345
    gegl_audio_fragment_set_pos (af, p->audio_pos);
    p->audio_pos += sample_count;
346 347 348 349
    p->audio_track = g_list_append (p->audio_track, af);
  }

  /* then we encode as much as we can in a loop using the codec frame size */
350
  c = st->codec;
351 352 353 354
  
  while (p->audio_pos - p->audio_read_pos > c->frame_size)
  {
    long i;
355 356
    int ret;
    int got_packet = 0;
357 358 359
    AVFrame *frame = alloc_audio_frame (c->sample_fmt, c->channel_layout,
                                        c->sample_rate, c->frame_size);

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
    switch (c->sample_fmt) {
      case AV_SAMPLE_FMT_FLT:
        for (i = 0; i < c->frame_size; i++)
        {
          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:
        for (i = 0; i < c->frame_size; i++)
        {
          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;
379 380 381 382 383 384 385 386 387
      case AV_SAMPLE_FMT_S16:
        for (i = 0; i < c->frame_size; i++)
        {
          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;
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
      case AV_SAMPLE_FMT_S32:
        for (i = 0; i < c->frame_size; i++)
        {
          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:
        for (i = 0; i < c->frame_size; i++)
        {
          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);
        }
405 406 407 408 409 410 411 412 413
        break;
      case AV_SAMPLE_FMT_S16P:
        for (i = 0; i < c->frame_size; i++)
        {
          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);
        }
414 415 416 417 418 419 420 421 422 423 424 425 426
        break;
      default:
        fprintf (stderr, "eeeek unhandled audio format\n");
        break;
    }

    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)
427
    {
428 429
      pkt.stream_index = st->index;
      av_interleaved_write_frame (oc, &pkt);
430 431 432 433 434
    }

    av_frame_free (&frame);
    p->audio_read_pos += c->frame_size;
  }
435

436
  p->audio_input_frame_size = c->frame_size;
437 438 439 440 441 442 443 444 445 446 447
}

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

}

/* add a video output stream */
static AVStream *
448
add_video_stream (GeglProperties *o, AVFormatContext * oc, int codec_id)
449
{
450
  Priv *p = (Priv*)o->user_data;
451 452 453 454

  AVCodecContext *c;
  AVStream *st;

455
  st = avformat_new_stream (oc, NULL);
456 457
  if (!st)
    {
458
      fprintf (stderr, "Could not alloc stream %p %p %i\n", o, oc, codec_id);
459 460 461 462 463
      exit (1);
    }

  c = st->codec;
  c->codec_id = codec_id;
464
  c->codec_type = AVMEDIA_TYPE_VIDEO;
465
  /* put sample propeters */
466
  c->bit_rate = 810000;
467 468 469 470
  /* resolution must be a multiple of two */
  c->width = p->width;
  c->height = p->height;
  /* frames per second */
471
  st->time_base =(AVRational){1, o->frame_rate};
472 473 474
  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 */
475

476
  if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
477
    {
478
      c->max_b_frames = 2;
479
    }
480 481
  if (c->codec_id == AV_CODEC_ID_H264)
   {
482 483 484 485 486 487 488
#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
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
     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;
509 510
#endif
#endif
511 512
   }

513 514 515
   if (oc->oformat->flags & AVFMT_GLOBALHEADER)
     c->flags |= CODEC_FLAG_GLOBAL_HEADER;

516 517 518 519 520 521 522 523 524 525 526 527 528 529
  return st;
}


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

  picture = avcodec_alloc_frame ();
  if (!picture)
    return NULL;
530
  size = avpicture_get_size (pix_fmt, width, height + 1);
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
  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 */
558
  if (avcodec_open2 (c, codec, NULL) < 0)
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    {
      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;
585
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
586
    {
587
      p->tmp_picture = alloc_picture (AV_PIX_FMT_RGB24, c->width, c->height);
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
      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
614
fill_rgb_image (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
615
                AVFrame *pict, int frame_index, int width, int height)
616
{
617
  Priv     *p = (Priv*)o->user_data;
618
  GeglRectangle rect={0,0,width,height};
619
  gegl_buffer_get (p->input, &rect, 1.0, babl_format ("R'G'B' u8"), pict->data[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
620 621 622
}

static void
623
write_video_frame (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
624
                   AVFormatContext *oc, AVStream *st)
625
{
626 627
  Priv           *p = (Priv*)o->user_data;
  int             out_size, ret;
628
  AVCodecContext *c;
629
  AVFrame        *picture_ptr;
630 631 632

  c = st->codec;

633
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
634
    {
635
      struct SwsContext *img_convert_ctx;
636
      fill_rgb_image (o, p->tmp_picture, p->frame_count, c->width,
637
                      c->height);
638

639
      img_convert_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_RGB24,
640
                                       c->width, c->height, c->pix_fmt,
641 642 643 644 645 646 647 648 649
                                       SWS_BICUBIC, NULL, NULL, NULL);

      if (img_convert_ctx == NULL)
        {
          fprintf(stderr, "ff_save: Cannot initialize conversion context.");
        }
      else
        {
          sws_scale(img_convert_ctx,
650
                    (void*)p->tmp_picture->data,
651 652 653 654 655
                    p->tmp_picture->linesize,
                    0,
                    c->height,
                    p->picture->data,
                    p->picture->linesize);
656 657 658
         p->picture->format = c->pix_fmt;
         p->picture->width = c->width;
         p->picture->height = c->height;
659
        }
660 661 662
    }
  else
    {
663
      fill_rgb_image (o, p->picture, p->frame_count, c->width, c->height);
664
    }
665

666
  picture_ptr      = p->picture;
667
  picture_ptr->pts = p->frame_count;
668 669 670 671 672 673 674 675

  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);

676
      pkt.flags |= AV_PKT_FLAG_KEY;
677 678 679
      pkt.stream_index = st->index;
      pkt.data = (uint8_t *) picture_ptr;
      pkt.size = sizeof (AVPicture);
680 681
      pkt.pts = picture_ptr->pts;
      av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

      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)
699
            pkt.flags |= AV_PKT_FLAG_KEY;
700 701 702
          pkt.stream_index = st->index;
          pkt.data = p->video_outbuf;
          pkt.size = out_size;
703 704
          pkt.pts = picture_ptr->pts;
          av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
          /* 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
722
tfile (GeglProperties *o)
723
{
724
  Priv *p = (Priv*)o->user_data;
725

726
  p->fmt = av_guess_format (NULL, o->path, NULL);
727 728 729 730 731
  if (!p->fmt)
    {
      fprintf (stderr,
               "ff_save couldn't deduce outputformat from file extension: using MPEG.\n%s",
               "");
732
      p->fmt = av_guess_format ("mpeg", NULL, NULL);
733
    }
734
  p->oc = avformat_alloc_context ();
735 736 737 738 739 740 741 742
  if (!p->oc)
    {
      fprintf (stderr, "memory error\n%s", "");
      return -1;
    }

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

743
  snprintf (p->oc->filename, sizeof (p->oc->filename), "%s", o->path);
744 745 746 747 748 749

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

  if (p->fmt->video_codec != CODEC_ID_NONE)
    {
750
      p->video_st = add_video_stream (o, p->oc, p->fmt->video_codec);
751
    }
752
  if (p->fmt->audio_codec != CODEC_ID_NONE)
753
    {
754
     p->audio_st = add_audio_stream (o, p->oc, p->fmt->audio_codec);
755 756 757 758 759
    }


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

761
  if (p->audio_st)
762
    open_audio (o, p->oc, p->audio_st);
763

764 765
  av_dump_format (p->oc, 0, o->path, 1);

766
  if (avio_open (&p->oc->pb, o->path, AVIO_FLAG_WRITE) < 0)
767
    {
768
      fprintf (stderr, "couldn't open '%s'\n", o->path);
769 770 771
      return -1;
    }

772
  avformat_write_header (p->oc, NULL);
773 774 775
  return 0;
}

Kevin Cozens's avatar
Kevin Cozens committed
776 777 778
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
779 780
         const GeglRectangle *result,
         gint                 level)
Kevin Cozens's avatar
Kevin Cozens committed
781
{
782
  GeglProperties *o = GEGL_PROPERTIES (operation);
783 784
  Priv           *p = (Priv*)o->user_data;
  static gint     inited = 0;
Kevin Cozens's avatar
Kevin Cozens committed
785 786 787 788 789

  g_assert (input);

  if (p == NULL)
    init (o);
790
  p = (Priv*)o->user_data;
Kevin Cozens's avatar
Kevin Cozens committed
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811

  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)
{
812 813
  GeglProperties *o = GEGL_PROPERTIES (object);
  if (o->user_data)
Kevin Cozens's avatar
Kevin Cozens committed
814
    {
815
      Priv *p = (Priv*)o->user_data;
816

Kevin Cozens's avatar
Kevin Cozens committed
817 818 819 820 821 822 823 824
    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);
825 826
        avio_closep (&p->oc->pb);
        avformat_free_context (p->oc);
Kevin Cozens's avatar
Kevin Cozens committed
827
      }
828

829 830
      g_free (o->user_data);
      o->user_data = NULL;
Kevin Cozens's avatar
Kevin Cozens committed
831 832 833 834 835 836 837
    }

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


static void
838
gegl_op_class_init (GeglOpClass *klass)
Kevin Cozens's avatar
Kevin Cozens committed
839 840 841 842 843 844 845 846 847 848 849 850
{
  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;

851 852 853 854 855
  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
856 857
}

858
#endif