[GRASS-CVS] hamish: grass6/lib/ogsf gsd_img_mpeg.c,2.1,2.2

grass at intevation.de grass at intevation.de
Fri Nov 30 23:49:49 EST 2007


Author: hamish

Update of /grassrepository/grass6/lib/ogsf
In directory doto:/tmp/cvs-serv27674/lib/ogsf

Modified Files:
	gsd_img_mpeg.c 
Log Message:
[From Bob Covill] 
Subject: Re: [GRASS-dev] OSX FFMPEG build instructions available
Date: Mon, 22 Oct 2007 20:02:57 -0300

I have attached a patch for gsd_img_mpeg.c (ogsf) which is based on the
later releases of ffmpeg (output_example.c). This uses a function
guess_format to guess the format based upon the file extension given. If
the format is unknown it reverts to mpeg1. With this patch you can save
to any (almost) format supported by ffmpeg. For example you can save to
asf, avi, flv, swf, etc. To use this with nviz the auto name extension
in panel_kanimator.tcl will have to be commented out ...

#       if { [string compare $fnameExt ".mpg"] != 0  &&
#               [string compare $fnameExt ".mpeg"] != 0 } then {
#           append keyanimBaseName ".mpg"
#       }

The patch needs testing, etc. 

Let me know if you have any questions.

--
Bob


Index: gsd_img_mpeg.c
===================================================================
RCS file: /grassrepository/grass6/lib/ogsf/gsd_img_mpeg.c,v
retrieving revision 2.1
retrieving revision 2.2
diff -u -d -r2.1 -r2.2
--- gsd_img_mpeg.c	26 Jul 2007 21:55:26 -0000	2.1
+++ gsd_img_mpeg.c	1 Dec 2007 04:49:47 -0000	2.2
@@ -6,92 +6,286 @@
 
 /* FFMPEG stuff */
 #ifdef HAVE_FFMPEG
-#include <avcodec.h>
+#include <avformat.h>
+#include <swscale.h>
 
-#define DEFAULT_BUFFER_SIZE 1000*1000
+/* 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 */
+
+static int sws_flags = SWS_BICUBIC;
+
+AVFrame *picture, *tmp_picture;
+uint8_t *video_outbuf;
+int frame_count, video_outbuf_size;
+
+AVOutputFormat *fmt;
+AVFormatContext *oc;
+AVStream *video_st;
+
+
+/* add a video output stream */
+static AVStream *add_video_stream(AVFormatContext *oc, int codec_id, int w, int h)
+{
+    AVCodecContext *c;
+    AVStream *st;
+
+    st = av_new_stream(oc, 0);
+    if (!st) {
+        fprintf(stderr, "Could not alloc stream\n");
+	return;
+    }
+
+    c = st->codec;
+    c->codec_id = codec_id;
+    c->codec_type = CODEC_TYPE_VIDEO;
+
+    /* 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;
+    c->global_quality = st->quality = FF_QP2LAMBDA * 10;
+
+    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;
+    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;
+}
+
+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) {
+        fprintf(stderr, "video codec not found\n");
+        return;
+    }
+
+    /* open the codec */
+    if (avcodec_open(c, codec) < 0) {
+        fprintf(stderr, "could not open codec\n");
+        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) {
+        fprintf(stderr, "Could not allocate picture\n");
+        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) {
+            fprintf(stderr, "Could not allocate temporary picture\n");
+            return;
+        }
+    }
+}
+
+static void write_video_frame(AVFormatContext *oc, AVStream *st)
+{
+    int out_size, ret;
+    AVCodecContext *c;
+    static struct SwsContext *img_convert_ctx;
+
+    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);
+
+        pkt.flags |= PKT_FLAG_KEY;
+        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)
+                pkt.flags |= PKT_FLAG_KEY;
+            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) {
+        fprintf(stderr, "Error while writing video frame\n");
+        return;
+    }
+    frame_count++;
+}
+
+static void close_video(AVFormatContext *oc, 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);
+}
 
-AVCodec *codec;
-AVCodecContext *c= NULL;
-int lav_size, out_size, frame_no;
-FILE *fmpg;
-AVFrame *picture;
-unsigned char *outbuf, *picture_buf;
 #endif
-/* end FFMPEG stuff */
 
 /******************************************
  * initialize FAME setup mpeg defaults and
  * open file for writing
 ******************************************/
-int gsd_init_mpeg(char *name)
+int gsd_init_mpeg(char *filename)
 {
 #ifdef HAVE_FFMPEG
 	GLuint l, r, b, t;
 	GLint tmp[4];
-	int bitrate = 400000;
-
+	
         glGetIntegerv(GL_VIEWPORT, tmp);
         l = tmp[0];
         r = tmp[0] + tmp[2] - 1;
         b = tmp[1];
         t = tmp[1] + tmp[3] - 1;
 
-	fprintf(stderr, "Opening MPEG stream <%s> ...\n", name);
+	fprintf(stderr, "Opening MPEG stream <%s> ...\n", filename);
 
-        avcodec_init();
-	avcodec_register_all();
+	/* initialize libavcodec, and register all codecs and formats */
+    	av_register_all();
+
+	/* auto detect the output format from the name. default is mpeg. */
+    fmt = guess_format(NULL, filename, NULL);
+    if (!fmt) {
+        printf("Could not deduce output format from file extension: using MPEG.\n");
+        fmt = guess_format("mpeg", NULL, NULL);
+    }
+    if (!fmt) {
+        fprintf(stderr, "Could not find suitable output format\n");
+        return (-1);
+    }
+
+    /* allocate the output media context */
+    oc = av_alloc_format_context();
+    if (!oc) {
+        fprintf(stderr, "Memory error\n");
+        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
-	codec = avcodec_find_encoder(CODEC_ID_XVID);
-	bitrate=2000000;
-#else
-        codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO);
+    fmt->video_codec = CODEC_ID_XVID;
 #endif
 
-        if (!codec) {
-                fprintf(stderr, "codec not found\n");
-                return(-1);
-        }
+    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) );
+    }
 
-        c= avcodec_alloc_context();
-        picture= avcodec_alloc_frame();
+    /* set the output parameters (must be done even if no parameters). */
+    if (av_set_parameters(oc, NULL) < 0) {
+        fprintf(stderr, "Invalid output format parameters\n");
+        return (-1);
+    }
 
-        /* put sample parameters */
-        c->bit_rate = bitrate;
-        /* resolution must be a multiple of two */
-        c->width = r - l + 1;
-        c->height = t - b + 1;
-        /* frames per second */
-	/* old 
-        c->frame_rate = 25;
-        c->frame_rate_base= 1;
-	*/
-	c->time_base= (AVRational){1,25};
-        c->gop_size = 10; /* emit one intra frame every ten frames */
-        c->max_b_frames=1;
-        c->pix_fmt = PIX_FMT_YUV420P;
+    dump_format(oc, 0, filename, 1);
 
-        /* open it */
-        if (avcodec_open(c, codec) < 0) {
-                fprintf(stderr, "could not open codec\n");
-		 G_debug(1, "avcodec_open() returned %d", avcodec_open(c, codec));
-                return(-1);
-        }
+    /* 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);
 
-        if (NULL == (fmpg = fopen(name, "wb"))) {
-                fprintf(stderr, "Cannot open file for output.\n");
-                return(-1);
+        /* open the output file, if needed */
+    if (!(fmt->flags & AVFMT_NOFILE)) {
+        if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
+            fprintf(stderr, "Could not open '%s'\n", filename);
+            return (-1);
         }
+    }
+
+    /* write the stream header, if any */
+    av_write_header(oc);
+
 
-        outbuf = (unsigned char *) malloc(DEFAULT_BUFFER_SIZE);
-        lav_size = c->width * c->height;
-        picture_buf = malloc((lav_size * 3) / 2); /* size for YUV 420 */
-        picture->data[0] = picture_buf;
-        picture->data[1] = picture->data[0] + lav_size;
-        picture->data[2] = picture->data[1] + lav_size / 4;
-        picture->linesize[0] = c->width;
-        picture->linesize[1] = c->width / 2;
-        picture->linesize[2] = c->width / 2;
 #else
 	fprintf(stderr, "NVIZ has not been built with MPEG output support\n");
 	return(-1);
@@ -101,7 +295,6 @@
 
 }
 
-
 /*********************************************
  * get RGB pixbuf and convert to YUV 4:2:0
  * image and write to mpeg stream
@@ -141,16 +334,15 @@
         }
         free(pixbuf);
 
-        /* encode the image */
-        out_size = avcodec_encode_video(c, outbuf, DEFAULT_BUFFER_SIZE, picture);
-        fwrite(outbuf, 1, out_size, fmpg);
+	write_video_frame(oc, video_st);
+	
+
 #endif
 
         return (0);
 
 }
 
-
 /****************************************
  * close the mpeg, free buffer, and close file
 ****************************************/
@@ -159,28 +351,25 @@
 #ifdef HAVE_FFMPEG
 int i;
 
-        /* get the delayed frames */
-        for(; out_size; i++) {
-                fflush(stdout);
-                out_size = avcodec_encode_video(c, outbuf, DEFAULT_BUFFER_SIZE, NULL);
-                fprintf(stderr, "write frame %3d (size=%5d)\n", i, out_size);
-                fwrite(outbuf, 1, out_size, fmpg);
-        }
+	close_video(oc, video_st);
 
-        /* add sequence end code to have a real mpeg file */
-        outbuf[0] = 0x00;
-        outbuf[1] = 0x00;
-        outbuf[2] = 0x01;
-        outbuf[3] = 0xb7;
-        fwrite(outbuf, 1, 4, fmpg);
-        
-        fclose(fmpg);
-        free(picture_buf);
-        free(outbuf);
+	/* 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 */
+        url_fclose(&oc->pb);
+    }
+
+    /* free the stream */
+    av_free(oc);
 
-        avcodec_close(c);
-        av_free(c);
-        av_free(picture);
 
 	fprintf(stderr, "Closed MPEG stream.\n");
 #endif




More information about the grass-commit mailing list