|
@@ -1,509 +0,0 @@
|
|
|
-/*!
|
|
|
- \file gsd_img_mpeg.c
|
|
|
-
|
|
|
- \brief OGSF library - FFMPEG stuff
|
|
|
-
|
|
|
- GRASS OpenGL gsurf OGSF Library
|
|
|
-
|
|
|
- (C) 1999-2008, 2012 by the GRASS Development Team
|
|
|
-
|
|
|
- This program is free software under the GNU General Public License
|
|
|
- (>=v2). Read the file COPYING that comes with GRASS for details.
|
|
|
-
|
|
|
- \author Bill Brown USACERL, GMSL/University of Illinois
|
|
|
- \author Doxygenized (May 2008) and update for FFMPEG >= 0.7
|
|
|
- (November 2012) by Martin Landa <landa.martin gmail.com>
|
|
|
- */
|
|
|
-
|
|
|
-#include <stdlib.h>
|
|
|
-#include <string.h>
|
|
|
-
|
|
|
-#include <grass/gis.h>
|
|
|
-#include <grass/glocale.h>
|
|
|
-#include <grass/ogsf.h>
|
|
|
-
|
|
|
-/* FFMPEG stuff */
|
|
|
-#ifdef HAVE_FFMPEG
|
|
|
-#include <avformat.h>
|
|
|
-#include <avio.h>
|
|
|
-#if LIBAVUTIL_VERSION_MAJOR < 51
|
|
|
-#include <avutil.h>
|
|
|
-#else
|
|
|
-/* libavutil 51.22.1's avutil.h doesn't include libavutil/mathematics.h */
|
|
|
-#include <mathematics.h>
|
|
|
-#endif
|
|
|
-
|
|
|
-/* 5 seconds stream duration */
|
|
|
-#define STREAM_DURATION 5.0
|
|
|
-#define STREAM_FRAME_RATE 25 /* 25 images/s */
|
|
|
-#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE))
|
|
|
-#define STREAM_PIX_FMT PIX_FMT_YUV420P /* default pix_fmt */
|
|
|
-
|
|
|
-AVFrame *picture, *tmp_picture;
|
|
|
-uint8_t *video_outbuf;
|
|
|
-int frame_count, video_outbuf_size;
|
|
|
-
|
|
|
-AVOutputFormat *fmt;
|
|
|
-AVFormatContext *oc;
|
|
|
-AVStream *video_st;
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Add a video output stream
|
|
|
-
|
|
|
- \param oc
|
|
|
- \param codec_id
|
|
|
- \param w
|
|
|
- \param h
|
|
|
-
|
|
|
- \return
|
|
|
- */
|
|
|
-static AVStream *add_video_stream(AVFormatContext * oc, int codec_id, int w,
|
|
|
- int h)
|
|
|
-{
|
|
|
- AVCodecContext *c;
|
|
|
- AVStream *st;
|
|
|
-
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 112, 0)
|
|
|
- st = av_new_stream(oc, 0);
|
|
|
-#else
|
|
|
- st = avformat_new_stream(oc, NULL);
|
|
|
-#endif
|
|
|
- if (!st) {
|
|
|
- G_warning(_("Unable to allocate stream"));
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- c = st->codec;
|
|
|
- c->codec_id = codec_id;
|
|
|
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 123, 0)
|
|
|
- c->codec_type = CODEC_TYPE_VIDEO;
|
|
|
-#else
|
|
|
- c->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
|
-#endif
|
|
|
-
|
|
|
- /* put sample parameters */
|
|
|
- c->bit_rate = 400000;
|
|
|
- /* resolution must be a multiple of two */
|
|
|
- c->width = w;
|
|
|
- c->height = h;
|
|
|
- /* time base: this is the fundamental unit of time (in seconds) in terms
|
|
|
- of which frame timestamps are represented. for fixed-fps content,
|
|
|
- timebase should be 1/framerate and timestamp increments should be
|
|
|
- identically 1. */
|
|
|
- c->time_base.den = STREAM_FRAME_RATE;
|
|
|
- c->time_base.num = 1;
|
|
|
- c->gop_size = 12; /* emit one intra frame every twelve frames at most */
|
|
|
- c->pix_fmt = STREAM_PIX_FMT;
|
|
|
- if (c->codec_id == CODEC_ID_MPEG2VIDEO) {
|
|
|
- /* just for testing, we also add B frames */
|
|
|
- c->max_b_frames = 2;
|
|
|
- }
|
|
|
- if (c->codec_id == CODEC_ID_MPEG1VIDEO) {
|
|
|
- /* Needed to avoid using macroblocks in which some coeffs overflow.
|
|
|
- This does not happen with normal video, it just happens here as
|
|
|
- the motion of the chroma plane does not match the luma plane. */
|
|
|
- c->mb_decision = 2;
|
|
|
- }
|
|
|
- /* some formats want stream headers to be separate */
|
|
|
- if (!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov")
|
|
|
- || !strcmp(oc->oformat->name, "3gp"))
|
|
|
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
|
|
-
|
|
|
- c->flags |= CODEC_FLAG_QSCALE;
|
|
|
-
|
|
|
- /* Quality, as it has been removed from AVCodecContext and put in AVVideoFrame. */
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- c->global_quality = st->quality = FF_QP2LAMBDA * 10;
|
|
|
-#else
|
|
|
- c->global_quality = FF_QP2LAMBDA * 10;
|
|
|
-#endif
|
|
|
-
|
|
|
- return st;
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Allocate picture
|
|
|
-
|
|
|
- \param pix_fmt
|
|
|
- \param width picture width
|
|
|
- \param height picture height
|
|
|
-
|
|
|
- \return pointer to AVFrame struct
|
|
|
- \return NULL on failure
|
|
|
- */
|
|
|
-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;
|
|
|
-
|
|
|
- size = avpicture_get_size(pix_fmt, width, height);
|
|
|
- picture_buf = av_malloc(size);
|
|
|
-
|
|
|
- if (!picture_buf) {
|
|
|
- av_free(picture);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- avpicture_fill((AVPicture *) picture, picture_buf,
|
|
|
- pix_fmt, width, height);
|
|
|
-
|
|
|
- return picture;
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Open video
|
|
|
-
|
|
|
- \param oc
|
|
|
- \param st
|
|
|
- */
|
|
|
-static void open_video(AVFormatContext * oc, AVStream * st)
|
|
|
-{
|
|
|
- AVCodec *codec;
|
|
|
- AVCodecContext *c;
|
|
|
-
|
|
|
- c = st->codec;
|
|
|
-
|
|
|
- /* find the video encoder */
|
|
|
- codec = avcodec_find_encoder(c->codec_id);
|
|
|
- if (!codec) {
|
|
|
- G_warning(_("Video codec not found"));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* open the codec */
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- if (avcodec_open(c, codec) < 0) {
|
|
|
-#else
|
|
|
- if (avcodec_open2(c, codec, NULL) < 0) {
|
|
|
-#endif
|
|
|
- G_warning(_("Unable to open codec"));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- video_outbuf = NULL;
|
|
|
- if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
|
|
|
- /* allocate output buffer */
|
|
|
- /* XXX: API change will be done */
|
|
|
- /* buffers passed into lav* can be allocated any way you prefer,
|
|
|
- as long as they're aligned enough for the architecture, and
|
|
|
- they're freed appropriately (such as using av_free for buffers
|
|
|
- allocated with av_malloc) */
|
|
|
- video_outbuf_size = 200000;
|
|
|
- video_outbuf = av_malloc(video_outbuf_size);
|
|
|
- }
|
|
|
-
|
|
|
- /* allocate the encoded raw picture */
|
|
|
- picture = alloc_picture(c->pix_fmt, c->width, c->height);
|
|
|
- if (!picture) {
|
|
|
- G_warning(_("Unable to allocate picture"));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* if the output format is not YUV420P, then a temporary YUV420P
|
|
|
- picture is needed too. It is then converted to the required
|
|
|
- output format */
|
|
|
- tmp_picture = NULL;
|
|
|
- if (c->pix_fmt != PIX_FMT_YUV420P) {
|
|
|
- tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);
|
|
|
- if (!tmp_picture) {
|
|
|
- G_warning(_("Unable to allocate temporary picture"));
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Write video frame
|
|
|
-
|
|
|
- \param oc
|
|
|
- \param st
|
|
|
- */
|
|
|
-static void write_video_frame(AVFormatContext * oc, AVStream * st)
|
|
|
-{
|
|
|
- int out_size, ret;
|
|
|
- AVCodecContext *c;
|
|
|
-
|
|
|
- c = st->codec;
|
|
|
-
|
|
|
- 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);
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 32, 0)
|
|
|
- pkt.flags |= PKT_FLAG_KEY;
|
|
|
-#else
|
|
|
- pkt.flags |= AV_PKT_FLAG_KEY;
|
|
|
-#endif
|
|
|
- pkt.stream_index = st->index;
|
|
|
- pkt.data = (uint8_t *) picture;
|
|
|
- pkt.size = sizeof(AVPicture);
|
|
|
-
|
|
|
- ret = av_write_frame(oc, &pkt);
|
|
|
- }
|
|
|
- else {
|
|
|
- /* encode the image */
|
|
|
- out_size =
|
|
|
- avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);
|
|
|
- /* if zero size, it means the image was buffered */
|
|
|
- if (out_size > 0) {
|
|
|
- AVPacket pkt;
|
|
|
-
|
|
|
- av_init_packet(&pkt);
|
|
|
-
|
|
|
- pkt.pts =
|
|
|
- av_rescale_q(c->coded_frame->pts, c->time_base,
|
|
|
- st->time_base);
|
|
|
- if (c->coded_frame->key_frame)
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 32, 0)
|
|
|
- pkt.flags |= PKT_FLAG_KEY;
|
|
|
-#else
|
|
|
- pkt.flags |= AV_PKT_FLAG_KEY;
|
|
|
-#endif
|
|
|
- pkt.stream_index = st->index;
|
|
|
- pkt.data = video_outbuf;
|
|
|
- pkt.size = out_size;
|
|
|
-
|
|
|
- /* write the compressed frame in the media file */
|
|
|
- ret = av_write_frame(oc, &pkt);
|
|
|
- }
|
|
|
- else {
|
|
|
- ret = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- if (ret != 0) {
|
|
|
- G_warning(_("Error while writing video frame"));
|
|
|
- return;
|
|
|
- }
|
|
|
- frame_count++;
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Close video
|
|
|
-
|
|
|
- \param oc [unused]
|
|
|
- \param st
|
|
|
- */
|
|
|
-static void close_video(AVStream * st)
|
|
|
-{
|
|
|
- avcodec_close(st->codec);
|
|
|
- av_free(picture->data[0]);
|
|
|
- av_free(picture);
|
|
|
- if (tmp_picture) {
|
|
|
- av_free(tmp_picture->data[0]);
|
|
|
- av_free(tmp_picture);
|
|
|
- }
|
|
|
- av_free(video_outbuf);
|
|
|
-}
|
|
|
-
|
|
|
-#endif
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Initialize FAME setup mpeg defaults and open file for writing
|
|
|
-
|
|
|
- \param filename file name
|
|
|
-
|
|
|
- \return -1 on failure
|
|
|
- \return 0 on success
|
|
|
- */
|
|
|
-int gsd_init_mpeg(const char *filename)
|
|
|
-{
|
|
|
-#ifdef HAVE_FFMPEG
|
|
|
- GLuint l, r, b, t;
|
|
|
- GLint tmp[4];
|
|
|
-
|
|
|
- glGetIntegerv(GL_VIEWPORT, tmp);
|
|
|
- l = tmp[0];
|
|
|
- r = tmp[0] + tmp[2] - 1;
|
|
|
- b = tmp[1];
|
|
|
- t = tmp[1] + tmp[3] - 1;
|
|
|
-
|
|
|
- G_verbose_message(_("Opening MPEG stream <%s>..."), filename);
|
|
|
-
|
|
|
- /* initialize libavcodec, and register all codecs and formats */
|
|
|
- av_register_all();
|
|
|
-
|
|
|
- /* auto detect the output format from the name. default is mpeg. */
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 32, 0)
|
|
|
- fmt = guess_format(NULL, filename, NULL);
|
|
|
-#else
|
|
|
- fmt = av_guess_format(NULL, filename, NULL);
|
|
|
-#endif
|
|
|
- if (!fmt) {
|
|
|
- G_warning(_("Unable to deduce output format from file extension: using MPEG"));
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 32, 0)
|
|
|
- fmt = guess_format("mpeg", NULL, NULL);
|
|
|
-#else
|
|
|
- fmt = av_guess_format("mpeg", NULL, NULL);
|
|
|
-#endif
|
|
|
- }
|
|
|
- if (!fmt) {
|
|
|
- G_warning(_("Unable to find suitable output format"));
|
|
|
- return (-1);
|
|
|
- }
|
|
|
-
|
|
|
- /* allocate the output media context */
|
|
|
- oc = avformat_alloc_context();
|
|
|
- if (!oc) {
|
|
|
- G_warning(_("Out of memory"));
|
|
|
- return (-1);
|
|
|
- }
|
|
|
- oc->oformat = fmt;
|
|
|
- snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
|
|
|
-
|
|
|
- /* if you want to hardcode the codec (eg #ifdef USE_XVID)
|
|
|
- this may be the place to do it (?????) */
|
|
|
-#ifdef USE_XVID
|
|
|
- fmt->video_codec = CODEC_ID_XVID;
|
|
|
-#endif
|
|
|
-
|
|
|
- video_st = NULL;
|
|
|
- if (fmt->video_codec != CODEC_ID_NONE) {
|
|
|
- video_st =
|
|
|
- add_video_stream(oc, fmt->video_codec, (r - l + 1), (t - b + 1));
|
|
|
- }
|
|
|
-
|
|
|
- /* set the output parameters (must be done even if no parameters). */
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- if (av_set_parameters(oc, NULL) < 0) {
|
|
|
-#else
|
|
|
- if (avformat_write_header(oc, NULL) < 0) {
|
|
|
-#endif
|
|
|
- G_warning(_("Invalid output format parameters"));
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- dump_format(oc, 0, filename, 1);
|
|
|
-#else
|
|
|
- av_dump_format(oc, 0, filename, 1);
|
|
|
-#endif
|
|
|
-
|
|
|
- /* now that all the parameters are set, we can open the audio and
|
|
|
- video codecs and allocate the necessary encode buffers */
|
|
|
- if (video_st)
|
|
|
- open_video(oc, video_st);
|
|
|
-
|
|
|
- /* open the output file, if needed */
|
|
|
- if (!(fmt->flags & AVFMT_NOFILE)) {
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 112, 0)
|
|
|
- if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
|
|
|
-#else
|
|
|
- if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) {
|
|
|
-#endif
|
|
|
- G_warning(_("Unable to open <%s>"), filename);
|
|
|
- return (-1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* write the stream header, if any */
|
|
|
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- av_write_header(oc);
|
|
|
-#else
|
|
|
- avformat_write_header(oc, NULL);
|
|
|
-#endif
|
|
|
-
|
|
|
-#else
|
|
|
- G_warning(_("OGSF library has not been built with MPEG output support"));
|
|
|
- return -1;
|
|
|
-#endif
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Get RGB pixbuf and convert to YUV 4:2:0
|
|
|
-
|
|
|
- Image and write to mpeg stream
|
|
|
-
|
|
|
- \return 0
|
|
|
- */
|
|
|
-int gsd_write_mpegframe(void)
|
|
|
-{
|
|
|
-#ifdef HAVE_FFMPEG
|
|
|
- unsigned int xsize, ysize, x;
|
|
|
- int y, xy, xy_uv;
|
|
|
- int yy, uu, vv;
|
|
|
- unsigned char *pixbuf;
|
|
|
-
|
|
|
- gsd_getimage(&pixbuf, &xsize, &ysize);
|
|
|
- xy = xy_uv = 0;
|
|
|
- for (y = ysize - 1; y >= 0; y--) {
|
|
|
- for (x = 0; x < xsize; x++) {
|
|
|
- unsigned char r = pixbuf[(y * xsize + x) * 4 + 0];
|
|
|
- unsigned char g = pixbuf[(y * xsize + x) * 4 + 1];
|
|
|
- unsigned char b = pixbuf[(y * xsize + x) * 4 + 2];
|
|
|
-
|
|
|
- yy = (0.257 * r) + (0.504 * g) + (0.098 * b) + 16;;
|
|
|
- vv = (0.439 * r) - (0.368 * g) - (0.071 * b) + 128;
|
|
|
- uu = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128;
|
|
|
- fflush(stdout);
|
|
|
- picture->data[0][xy] = yy;
|
|
|
-
|
|
|
- if ((x % 2) && (y % 2)) {
|
|
|
- picture->data[1][xy_uv] = uu;
|
|
|
- picture->data[2][xy_uv] = vv;
|
|
|
- xy_uv++;
|
|
|
- }
|
|
|
-
|
|
|
- xy++;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- G_free(pixbuf);
|
|
|
-
|
|
|
- write_video_frame(oc, video_st);
|
|
|
-
|
|
|
-
|
|
|
-#endif
|
|
|
-
|
|
|
- return (0);
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Close the mpeg, free buffer, and close file
|
|
|
-
|
|
|
- \return 0
|
|
|
- */
|
|
|
-int gsd_close_mpeg(void)
|
|
|
-{
|
|
|
-#ifdef HAVE_FFMPEG
|
|
|
- unsigned int i;
|
|
|
-
|
|
|
- close_video(video_st);
|
|
|
-
|
|
|
- /* write the trailer, if any */
|
|
|
- av_write_trailer(oc);
|
|
|
-
|
|
|
- /* free the streams */
|
|
|
- for (i = 0; i < oc->nb_streams; i++) {
|
|
|
- av_freep(&oc->streams[i]->codec);
|
|
|
- av_freep(&oc->streams[i]);
|
|
|
- }
|
|
|
-
|
|
|
- if (!(fmt->flags & AVFMT_NOFILE)) {
|
|
|
- /* close the output file */
|
|
|
-#if (LIBAVFORMAT_VERSION_INT>>16) < 52
|
|
|
- url_fclose(&oc->pb);
|
|
|
-#elif LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 100, 0)
|
|
|
- url_fclose(oc->pb);
|
|
|
-#else
|
|
|
- avio_close(oc->pb);
|
|
|
-#endif
|
|
|
- }
|
|
|
-
|
|
|
- /* free the stream */
|
|
|
- av_free(oc);
|
|
|
-
|
|
|
-
|
|
|
- G_debug(3, "Closed MPEG stream");
|
|
|
-#endif
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|