ff-save.c 28.2 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_audio_fragment (audio, _("audio"), 0)
property_string (audio_codec, _("Audio codec"), "auto")
32 33 34
property_int (audio_sample_rate, _("audio sample rate"), -1)
    description (_("-1 means autodetect on first audio fragment"))

35 36 37
property_int (audio_bit_rate, _("audio bitrate in kb/s"), 64)
    description (_("Target encoded video bitrate in kb/s"))

38 39
property_double (frame_rate, _("Frames/second"), 25.0)
    value_range (0.0, 100.0)
40

41
property_string (video_codec, _("Video codec"), "auto")
42 43 44 45 46 47 48 49
property_int (video_bit_rate, _("video bitrate in kb/s"), 128)
    description (_("Target encoded video bitrate in kb/s"))

property_int (global_quality, _("global quality"), 0)
property_int (noise_reduction, _("noise reduction"), 0)
property_int (scenechange_threshold, _("scenechange threshold"), 0)
property_int (video_bit_rate_min, _("video bitrate min"), 0)
property_int (video_bit_rate_max, _("video bitrate max"), 0)
50
property_int (video_bit_rate_tolerance, _("video bitrate tolerance"), -1)
51 52 53

property_int (keyint_min, _("keyint-min"), 0)
property_int (trellis, _("trellis"), 0)
54
property_int (bufsize, _("bufsize"), 0)
55 56 57 58 59 60 61 62 63 64 65
property_int (qmin, _("qmin"), 0)
property_int (qmax, _("qmax"), 0)
property_int (max_qdiff, _("max_qdiff"), 0)
property_int (me_range, _("me_range"), 0)
property_int (max_b_frames, _("max_b_frames"), 0)
property_int (gop_size, _("gop-size"), 0)
property_double (qcompress, _("qcompress"), 0.0)
property_double (qblur, _("qblur"), 0.0)
property_double (i_quant_factor, _("i-quant-factor"), 0.0)
property_double (i_quant_offset, _("i-quant-offset"), 0.0)
property_int (me_subpel_quality, _("me-subpel-quality"), 0)
66

67

68

69 70
#else

71
#define GEGL_OP_SINK
72
#define GEGL_OP_C_SOURCE ff-save.c
73

74
#include "gegl-op.h"
75

76
#include <libavformat/avformat.h>
77 78 79 80
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

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;

98 99 100 101
    /** 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
102 103 104 105 106 107 108 109 110
     */
  AVStream *audio_st;

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

111
  int       bufsize;
112 113
  int       buffer_read_pos;
  int       buffer_write_pos;
114 115
  uint8_t  *buffer; 
                   
116 117
  int       audio_outbuf_size;
  int16_t  *samples;
118

119 120 121
  GList    *audio_track;
  long      audio_pos;
  long      audio_read_pos;
122 123
  
  int       next_apts;
124 125
} Priv;

126 127 128 129 130 131
static void
clear_audio_track (GeglProperties *o)
{
  Priv *p = (Priv*)o->user_data;
  while (p->audio_track)
    {
132
      g_object_unref (p->audio_track->data);
133 134 135 136
      p->audio_track = g_list_remove (p->audio_track, p->audio_track->data);
    }
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
static int
samples_per_frame (int    frame,
                   double frame_rate,
                   int    sample_rate,
                   long  *start)
{
  double osamples;
  double samples = 0;
  int f = 0;

  if (fabs(fmod (sample_rate, frame_rate)) < 0.0001)
  {
    if (start)
      *start = (sample_rate / frame_rate) * frame;
    return sample_rate / frame_rate;
  }

  for (f = 0; f < frame; f++) 
  {
    samples += sample_rate / frame_rate;
  }
  osamples = samples;
  samples += sample_rate / frame_rate;
  if (start)
     (*start) = ceil(osamples);
  return ceil(samples)-ceil(osamples);
}

165 166 167 168 169 170
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;
171
  for (l = p->audio_track; l; l = l->next)
172
  {
173
    GeglAudioFragment *af = l->data;
174 175 176
    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);
177
    if (sample_no > pos + sample_count)
178 179 180 181
    {
      to_remove ++;
    }

182 183
    if (pos <= sample_no &&
        sample_no < pos + sample_count)
184
      {
185
        int i = sample_no - pos;
186
        *left  = af->data[0][i];
187
        if (channels == 1)
188 189 190 191 192 193 194 195 196
          *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)
          {
197
            GeglAudioFragment *af = l->data;
198 199
            int pos = gegl_audio_fragment_get_pos (af);
            int sample_count = gegl_audio_fragment_get_sample_count (af);
200
            if (sample_no > pos + sample_count)
201 202
            {
              p->audio_track = g_list_remove (p->audio_track, af);
203
              g_object_unref (af);
204 205 206 207 208 209 210 211 212 213
              goto again;
            }
          }
        }
        return;
      }
  }
  *left  = 0;
  *right = 0;
}
214

Kevin Cozens's avatar
Kevin Cozens committed
215
static void
216
init (GeglProperties *o)
217
{
Kevin Cozens's avatar
Kevin Cozens committed
218
  static gint inited = 0; /*< this is actually meant to be static, only to be done once */
219
  Priv       *p = (Priv*)o->user_data;
Kevin Cozens's avatar
Kevin Cozens committed
220 221

  if (p == NULL)
222
    {
Kevin Cozens's avatar
Kevin Cozens committed
223
      p = g_new0 (Priv, 1);
224
      o->user_data = (void*) p;
225 226 227 228 229 230 231 232 233
    }

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

234 235 236
  clear_audio_track (o);
  p->audio_pos = 0;
  p->audio_read_pos = 0;
237

238
  o->audio_sample_rate = -1; /* only do this if it hasn't been manually set? */
239 240
}

Kevin Cozens's avatar
Kevin Cozens committed
241 242 243 244 245 246
static void close_video       (Priv            *p,
                               AVFormatContext *oc,
                               AVStream        *st);
void        close_audio       (Priv            *p,
                               AVFormatContext *oc,
                               AVStream        *st);
247 248
static int  tfile             (GeglProperties  *o);
static void write_video_frame (GeglProperties  *o,
Kevin Cozens's avatar
Kevin Cozens committed
249 250
                               AVFormatContext *oc,
                               AVStream        *st);
251
static void write_audio_frame (GeglProperties      *o,
Kevin Cozens's avatar
Kevin Cozens committed
252 253
                               AVFormatContext *oc,
                               AVStream        *st);
254 255 256

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

257
#ifndef DISABLE_AUDIO
258 259
/* add an audio output stream */
static AVStream *
260
add_audio_stream (GeglProperties *o, AVFormatContext * oc, int codec_id)
261 262 263 264
{
  AVCodecContext *c;
  AVStream *st;

265
  st = avformat_new_stream (oc, NULL);
266 267 268 269 270 271 272 273
  if (!st)
    {
      fprintf (stderr, "Could not alloc stream\n");
      exit (1);
    }

  c = st->codec;
  c->codec_id = codec_id;
274
  c->codec_type = AVMEDIA_TYPE_AUDIO;
275

276 277 278
  if (oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;

279 280
  return st;
}
281
#endif
282 283

static void
284
open_audio (GeglProperties *o, AVFormatContext * oc, AVStream * st)
285 286 287
{
  AVCodecContext *c;
  AVCodec  *codec;
288
  int i;
289 290 291 292 293 294 295 296 297 298

  c = st->codec;

  /* find the audio encoder */
  codec = avcodec_find_encoder (c->codec_id);
  if (!codec)
    {
      fprintf (stderr, "codec not found\n");
      exit (1);
    }
299
  c->bit_rate = o->audio_bit_rate * 1000;
300 301
  c->sample_fmt = codec->sample_fmts ? codec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

302
  if (o->audio_sample_rate == -1)
303 304 305
  {
    if (o->audio)
    {
306
      if (gegl_audio_fragment_get_sample_rate (o->audio) == 0)
307
      {
308
        gegl_audio_fragment_set_sample_rate (o->audio, 48000); // XXX: should skip adding audiostream instead
309
      }
310
      o->audio_sample_rate = gegl_audio_fragment_get_sample_rate (o->audio);
311 312
    }
  }
313
  c->sample_rate = o->audio_sample_rate;
314 315 316
  c->channel_layout = AV_CH_LAYOUT_STEREO;
  c->channels = 2;

317

318 319 320 321
  if (codec->supported_samplerates)
  {
    c->sample_rate = codec->supported_samplerates[0];
    for (i = 0; codec->supported_samplerates[i]; i++)
322
    {
323 324
      if (codec->supported_samplerates[i] == o->audio_sample_rate)
         c->sample_rate = o->audio_sample_rate;
325
    }
326
  }
327
  //st->time_base = (AVRational){1, c->sample_rate};
328
  st->time_base = (AVRational){1, o->audio_sample_rate};
329 330

  c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; // ffmpeg AAC is not quite stable yet
331 332

  /* open it */
333
  if (avcodec_open2 (c, codec, NULL) < 0)
334 335 336 337 338 339
    {
      fprintf (stderr, "could not open codec\n");
      exit (1);
    }
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
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;
}

367
void
368
write_audio_frame (GeglProperties *o, AVFormatContext * oc, AVStream * st)
369
{
370
  Priv *p = (Priv*)o->user_data;
371 372 373 374 375 376 377 378
  AVCodecContext *c = st->codec;
  int sample_count = 100000;
  static AVPacket  pkt = { 0 };

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

380
  /* first we add incoming frames audio samples */
381
  if (o->audio)
382 383
  {
    int i;
384 385 386
    GeglAudioFragment *af;
    sample_count = gegl_audio_fragment_get_sample_count (o->audio);
    af = gegl_audio_fragment_new (gegl_audio_fragment_get_sample_rate (o->audio),
387 388 389
                                                     gegl_audio_fragment_get_channels (o->audio),
                                                     gegl_audio_fragment_get_channel_layout (o->audio),
                                                     sample_count);
390
    gegl_audio_fragment_set_sample_count (af, sample_count);
391
    for (i = 0; i < sample_count; i++)
392
      {
393 394
        af->data[0][i] = o->audio->data[0][i];
        af->data[1][i] = o->audio->data[1][i];
395
      }
396 397
    gegl_audio_fragment_set_pos (af, p->audio_pos);
    p->audio_pos += sample_count;
398 399
    p->audio_track = g_list_append (p->audio_track, af);
  }
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
  else
  {
    int i;
    GeglAudioFragment *af;
    sample_count = samples_per_frame (p->frame_count, o->frame_rate, o->audio_sample_rate, NULL);
    af = gegl_audio_fragment_new (sample_count,
                                                     2,
                                                     0,
                                                     sample_count);
    gegl_audio_fragment_set_sample_count (af, sample_count);
    gegl_audio_fragment_set_pos (af, p->audio_pos);
    for (i = 0; i < sample_count; i++)
      {
        af->data[0][i] = 0.0;
        af->data[1][i] = 0.0;
      }
    p->audio_pos += sample_count;
    p->audio_track = g_list_append (p->audio_track, af);
  }
419 420 421
  if (!(c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))
    sample_count = c->frame_size;

422 423
  /* then we encode as much as we can in a loop using the codec frame size */
  
424
  while (p->audio_pos - p->audio_read_pos > sample_count)
425 426
  {
    long i;
427 428
    int ret;
    int got_packet = 0;
429
    AVFrame *frame = alloc_audio_frame (c->sample_fmt, c->channel_layout,
430
                                        c->sample_rate, sample_count);
431

432 433
    switch (c->sample_fmt) {
      case AV_SAMPLE_FMT_FLT:
434
        for (i = 0; i < sample_count; i++)
435 436 437 438 439 440 441 442
        {
          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:
443
        for (i = 0; i < sample_count; i++)
444 445 446 447 448 449 450
        {
          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;
451
      case AV_SAMPLE_FMT_S16:
452
        for (i = 0; i < sample_count; i++)
453 454 455 456 457 458 459
        {
          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;
460
      case AV_SAMPLE_FMT_S32:
461
        for (i = 0; i < sample_count; i++)
462 463 464 465 466 467 468 469
        {
          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:
470
        for (i = 0; i < sample_count; i++)
471 472 473 474 475 476
        {
          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);
        }
477 478
        break;
      case AV_SAMPLE_FMT_S16P:
479
        for (i = 0; i < sample_count; i++)
480 481 482 483 484 485
        {
          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);
        }
486 487 488 489 490
        break;
      default:
        fprintf (stderr, "eeeek unhandled audio format\n");
        break;
    }
491
    frame->pts = p->next_apts;
492
    p->next_apts += sample_count;
493 494 495

    av_frame_make_writable (frame);
    ret = avcodec_encode_audio2 (c, &pkt, frame, &got_packet);
496 497

    av_packet_rescale_ts (&pkt, st->codec->time_base, st->time_base);
498 499 500 501 502
    if (ret < 0) {
      fprintf (stderr, "Error encoding audio frame: %s\n", av_err2str (ret));
    }

    if (got_packet)
503
    {
504 505
      pkt.stream_index = st->index;
      av_interleaved_write_frame (oc, &pkt);
506
      av_free_packet (&pkt);
507 508 509
    }

    av_frame_free (&frame);
510
    p->audio_read_pos += sample_count;
511
  }
512 513 514 515 516 517 518 519 520 521 522
}

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

}

/* add a video output stream */
static AVStream *
523
add_video_stream (GeglProperties *o, AVFormatContext * oc, int codec_id)
524
{
525
  Priv *p = (Priv*)o->user_data;
526 527 528 529

  AVCodecContext *c;
  AVStream *st;

530
  st = avformat_new_stream (oc, NULL);
531 532
  if (!st)
    {
533
      fprintf (stderr, "Could not alloc stream %p %p %i\n", o, oc, codec_id);
534 535 536 537 538
      exit (1);
    }

  c = st->codec;
  c->codec_id = codec_id;
539
  c->codec_type = AVMEDIA_TYPE_VIDEO;
540
  /* put sample propeters */
541 542 543
  c->bit_rate = o->video_bit_rate * 1000;
  c->rc_min_rate = o->video_bit_rate_min * 1000;
  c->rc_max_rate = o->video_bit_rate_max * 1000;
544 545
  if (o->video_bit_rate_tolerance >= 0)
    c->bit_rate_tolerance = o->video_bit_rate_tolerance * 1000;
546 547 548 549
  /* resolution must be a multiple of two */
  c->width = p->width;
  c->height = p->height;
  /* frames per second */
550
  st->time_base =(AVRational){1000, o->frame_rate * 1000};
551
  c->time_base = st->time_base;
552

553
  c->pix_fmt = AV_PIX_FMT_YUV420P;
554
  
555

556
  if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
557
    {
558
      c->max_b_frames = 2;
559
    }
560

561 562
  if (c->codec_id == AV_CODEC_ID_H264)
   {
563 564 565 566
     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
567 568
   }

569 570
  if (o->bufsize)
    c->rc_buffer_size = o->bufsize * 1000;
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
  if (o->global_quality)
     c->global_quality = o->global_quality;
  if (o->qcompress != 0.0)
     c->qcompress = o->qcompress;
  if (o->qblur != 0.0)
     c->qblur = o->qblur;
  if (o->max_qdiff != 0)
     c->max_qdiff = o->max_qdiff;
  if (o->me_subpel_quality != 0)
     c->me_subpel_quality = o->me_subpel_quality;
  if (o->i_quant_factor != 0.0)
     c->i_quant_factor = o->i_quant_factor;
  if (o->i_quant_offset != 0.0)
     c->i_quant_offset = o->i_quant_offset;
  if (o->max_b_frames)
    c->max_b_frames = o->max_b_frames;
  if (o->me_range)
    c->me_range = o->me_range;
  if (o->noise_reduction)
    c->noise_reduction = o->noise_reduction;
  if (o->scenechange_threshold)
    c->scenechange_threshold = o->scenechange_threshold;
  if (o->trellis)
    c->trellis = o->trellis;
  if (o->qmin)
    c->qmin = o->qmin;
  if (o->qmax)
    c->qmax = o->qmax;
  if (o->gop_size)
    c->gop_size = o->gop_size;
  if (o->keyint_min)
    c->keyint_min = o->keyint_min;

604 605 606
   if (oc->oformat->flags & AVFMT_GLOBALHEADER)
     c->flags |= CODEC_FLAG_GLOBAL_HEADER;

607 608 609 610 611 612 613 614 615 616 617
  return st;
}


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

618
  picture = av_frame_alloc ();
619 620
  if (!picture)
    return NULL;
621
  size = avpicture_get_size (pix_fmt, width + 1, height + 1);
622 623 624 625 626 627 628 629 630 631 632
  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
633
open_video (GeglProperties *o, AVFormatContext * oc, AVStream * st)
634
{
635
  Priv           *p = (Priv*)o->user_data;
636 637
  AVCodec  *codec;
  AVCodecContext *c;
638
  AVDictionary *codec_options = {0};
639 640 641 642 643 644 645 646 647 648 649

  c = st->codec;

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

650 651 652 653 654 655 656 657 658 659
  if (codec->pix_fmts){
    int i = 0;
    c->pix_fmt = codec->pix_fmts[0];
    while (codec->pix_fmts[i] !=-1)
    {
      if (codec->pix_fmts[i] ==  AV_PIX_FMT_RGB24)
         c->pix_fmt = AV_PIX_FMT_RGB24;
      i++;
    }
  }
660 661 662 663
#if 0
  if (o->video_preset[0])
    av_dict_set (&codec_options, "preset", o->video_preset, 0);
#endif
664

665
  /* open the codec */
666
  if (avcodec_open2 (c, codec, &codec_options) < 0)
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    {
      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;
693
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
694
    {
695
      p->tmp_picture = alloc_picture (AV_PIX_FMT_RGB24, c->width, c->height);
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
      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
722
fill_rgb_image (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
723
                AVFrame *pict, int frame_index, int width, int height)
724
{
725
  Priv     *p = (Priv*)o->user_data;
726
  GeglRectangle rect={0,0,width,height};
727
  gegl_buffer_get (p->input, &rect, 1.0, babl_format ("R'G'B' u8"), pict->data[0], GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
728 729 730
}

static void
731
write_video_frame (GeglProperties *o,
Kevin Cozens's avatar
Kevin Cozens committed
732
                   AVFormatContext *oc, AVStream *st)
733
{
734 735
  Priv           *p = (Priv*)o->user_data;
  int             out_size, ret;
736
  AVCodecContext *c;
737
  AVFrame        *picture_ptr;
738 739 740

  c = st->codec;

741
  if (c->pix_fmt != AV_PIX_FMT_RGB24)
742
    {
743
      struct SwsContext *img_convert_ctx;
744
      fill_rgb_image (o, p->tmp_picture, p->frame_count, c->width,
745
                      c->height);
746

747
      img_convert_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_RGB24,
748
                                       c->width, c->height, c->pix_fmt,
749 750 751 752 753 754 755 756 757
                                       SWS_BICUBIC, NULL, NULL, NULL);

      if (img_convert_ctx == NULL)
        {
          fprintf(stderr, "ff_save: Cannot initialize conversion context.");
        }
      else
        {
          sws_scale(img_convert_ctx,
758
                    (void*)p->tmp_picture->data,
759 760 761 762 763
                    p->tmp_picture->linesize,
                    0,
                    c->height,
                    p->picture->data,
                    p->picture->linesize);
764 765 766
         p->picture->format = c->pix_fmt;
         p->picture->width = c->width;
         p->picture->height = c->height;
767
         sws_freeContext (img_convert_ctx);
768
        }
769 770 771
    }
  else
    {
772
      fill_rgb_image (o, p->picture, p->frame_count, c->width, c->height);
773
    }
774

775
  picture_ptr      = p->picture;
776
  picture_ptr->pts = p->frame_count;
777 778 779 780 781 782 783 784

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

785
      pkt.flags |= AV_PKT_FLAG_KEY;
786 787 788
      pkt.stream_index = st->index;
      pkt.data = (uint8_t *) picture_ptr;
      pkt.size = sizeof (AVPicture);
789
      pkt.pts = picture_ptr->pts;
790
      pkt.dts = picture_ptr->pts;
791
      av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808

      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)
809
            pkt.flags |= AV_PKT_FLAG_KEY;
810 811 812
          pkt.stream_index = st->index;
          pkt.data = p->video_outbuf;
          pkt.size = out_size;
813
          pkt.pts = picture_ptr->pts;
814
          pkt.dts = picture_ptr->pts;
815
          av_packet_rescale_ts (&pkt, c->time_base, st->time_base);
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
          /* 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
833
tfile (GeglProperties *o)
834
{
835
  Priv *p = (Priv*)o->user_data;
836

837
  p->fmt = av_guess_format (NULL, o->path, NULL);
838 839 840 841 842
  if (!p->fmt)
    {
      fprintf (stderr,
               "ff_save couldn't deduce outputformat from file extension: using MPEG.\n%s",
               "");
843
      p->fmt = av_guess_format ("mpeg", NULL, NULL);
844
    }
845
  p->oc = avformat_alloc_context ();
846 847 848 849 850 851 852 853
  if (!p->oc)
    {
      fprintf (stderr, "memory error\n%s", "");
      return -1;
    }

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

854
  snprintf (p->oc->filename, sizeof (p->oc->filename), "%s", o->path);
855 856 857

  p->video_st = NULL;
  p->audio_st = NULL;
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
  
  if (strcmp (o->video_codec, "auto"))
  {
    AVCodec *codec = avcodec_find_encoder_by_name (o->video_codec);
    p->fmt->video_codec = AV_CODEC_ID_NONE;
    if (codec)
      p->fmt->video_codec = codec->id;
    else
      {
        fprintf (stderr, "didn't find video encoder \"%s\"\navailable codecs: ", o->video_codec);
        while ((codec = av_codec_next (codec)))
          if (av_codec_is_encoder (codec) &&
              avcodec_get_type (codec->id) == AVMEDIA_TYPE_VIDEO)
          fprintf (stderr, "%s ", codec->name);
        fprintf (stderr, "\n");
      }
  }
  if (strcmp (o->audio_codec, "auto"))
  {
    AVCodec *codec = avcodec_find_encoder_by_name (o->audio_codec);
    p->fmt->audio_codec = AV_CODEC_ID_NONE;
    if (codec)
      p->fmt->audio_codec = codec->id;
    else
      {
        fprintf (stderr, "didn't find audio encoder \"%s\"\navailable codecs: ", o->audio_codec);
        while ((codec = av_codec_next (codec)))
          if (av_codec_is_encoder (codec) &&
              avcodec_get_type (codec->id) == AVMEDIA_TYPE_AUDIO)
                fprintf (stderr, "%s ", codec->name);
        fprintf (stderr, "\n");
      }
  }
891

892
  if (p->fmt->video_codec != AV_CODEC_ID_NONE)
893
    {
894
      p->video_st = add_video_stream (o, p->oc, p->fmt->video_codec);
895
    }
896
  if (p->fmt->audio_codec != AV_CODEC_ID_NONE)
897
    {
898
     p->audio_st = add_audio_stream (o, p->oc, p->fmt->audio_codec);
899 900 901 902
    }


  if (p->video_st)
903
    open_video (o, p->oc, p->video_st);
904

905
  if (p->audio_st)
906
    open_audio (o, p->oc, p->audio_st);
907

908 909
  av_dump_format (p->oc, 0, o->path, 1);

910
  if (avio_open (&p->oc->pb, o->path, AVIO_FLAG_WRITE) < 0)
911
    {
912
      fprintf (stderr, "couldn't open '%s'\n", o->path);
913 914 915
      return -1;
    }

916
  avformat_write_header (p->oc, NULL);
917 918 919
  return 0;
}

Kevin Cozens's avatar
Kevin Cozens committed
920 921 922
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
923 924
         const GeglRectangle *result,
         gint                 level)
Kevin Cozens's avatar
Kevin Cozens committed
925
{
926
  GeglProperties *o = GEGL_PROPERTIES (operation);
927
  Priv *p = (Priv*)o->user_data;
928
  static gint     inited = 0;
Kevin Cozens's avatar
Kevin Cozens committed
929 930 931 932 933

  g_assert (input);

  if (p == NULL)
    init (o);
934
  p = (Priv*)o->user_data;
Kevin Cozens's avatar
Kevin Cozens committed
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952

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

953 954 955 956 957
static void flush_audio (GeglProperties *o)
{
  Priv *p = (Priv*)o->user_data;

  int got_packet;
958 959
  if (!p->audio_st)
    return;
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
  do
  {
    AVPacket  pkt = { 0 };
    int ret;
    got_packet = 0;
    av_init_packet (&pkt);
    ret = avcodec_encode_audio2 (p->audio_st->codec, &pkt, NULL, &got_packet);
    if (ret < 0)
      break;
    if (got_packet)
      {
        pkt.stream_index = p->audio_st->index;
        av_packet_rescale_ts (&pkt, p->audio_st->codec->time_base, p->audio_st->time_base);
        av_interleaved_write_frame (p->oc, &pkt);
        av_free_packet (&pkt);
      }
  } while (got_packet);
}

static void flush_video (GeglProperties *o)
{
  Priv *p = (Priv*)o->user_data;
  int got_packet = 0;
983
  long ts = p->frame_count;
984 985 986 987 988 989 990 991 992 993 994 995
  do {
    AVPacket  pkt = { 0 };
    int ret;
    got_packet = 0;
    av_init_packet (&pkt);
    ret = avcodec_encode_video2 (p->video_st->codec, &pkt, NULL, &got_packet);
    if (ret < 0)
      return;
      
     if (got_packet)
     {
       pkt.stream_index = p->video_st->index;
996 997
       pkt.pts = ts;
       pkt.dts = ts++;
998 999 1000 1001 1002 1003 1004
       av_packet_rescale_ts (&pkt, p->video_st->codec->time_base, p->video_st->time_base);
       av_interleaved_write_frame (p->oc, &pkt);
       av_free_packet (&pkt);
     }
  } while (got_packet);
}

Kevin Cozens's avatar
Kevin Cozens committed
1005 1006 1007
static void
finalize (GObject *object)
{
1008 1009
  GeglProperties *o = GEGL_PROPERTIES (object);
  if (o->user_data)
Kevin Cozens's avatar
Kevin Cozens committed
1010
    {
1011
      Priv *p = (Priv*)o->user_data;
1012 1013
      flush_audio (o);
      flush_video (o);
1014

1015
      av_write_trailer (p->oc);
1016

1017 1018 1019 1020
      if (p->video_st)
        close_video (p, p->oc, p->video_st);
      if (p->audio_st)
        close_audio (p, p->oc, p->audio_st);
Kevin Cozens's avatar
Kevin Cozens committed
1021

1022 1023
      avio_closep (&p->oc->pb);
      avformat_free_context (p->oc);
1024

1025 1026
      g_free (o->user_data);
      o->user_data = NULL;
Kevin Cozens's avatar
Kevin Cozens committed
1027 1028 1029 1030 1031 1032 1033
    }

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


static void
1034
gegl_op_class_init (GeglOpClass *klass)
Kevin Cozens's avatar
Kevin Cozens committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
{
  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;

1047 1048 1049 1050 1051
  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
1052 1053
}

1054
#endif