123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /*!
- \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;
- }
|