On Mon, 2007-10-22 at 19:54 +0100, Glynn Clements wrote: > William Kyngesburye wrote: > > > I finally got back to FFMPEG on OSX. It went fairly smooth this > > time. I added build instructions to the GRASS OSX readme. My future > > OSX builds will now have ffmpeg support, yay! > > > > Question: what is the minimum needed in the ffmpeg libraries for > > GRASS? I'm thinking of disabling codecs and formats that GRASS can't > > use to reduce the library size (a basic universal build without debug > > symbols added ~6.5MB to libgrass_ogsf!). Or can GRASS use any codec > > available in ffmpeg? > > It only uses MPEG-1. There's a compile-time option to use XviD, but > that gives you a "raw" XviD stream (no AVI etc container), which isn't > much use. >
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
--- gsd_img_mpeg.c 2007-10-22 19:15:10.000000000 -0300 +++ gsd_img_mpeg.c_new 2007-10-22 08:52:26.000000000 -0300 @@ -6,92 +6,280 @@ /* 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 + futur 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); - - avcodec_init(); - avcodec_register_all(); - -#ifdef USE_XVID - codec = avcodec_find_encoder(CODEC_ID_XVID); - bitrate=2000000; -#else - codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO); -#endif - - if (!codec) { - fprintf(stderr, "codec not found\n"); - return(-1); + fprintf(stderr, "Opening MPEG stream <%s> ...\n", filename); + + /* 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); + + 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 (av_set_parameters(oc, NULL) < 0) { + fprintf(stderr, "Invalid output format parameters\n"); + return (-1); + } + + dump_format(oc, 0, filename, 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); + + /* 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); } + } - c= avcodec_alloc_context(); - picture= avcodec_alloc_frame(); + /* write the stream header, if any */ + av_write_header(oc); - /* 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; - - /* 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); - } - - if (NULL == (fmpg = fopen(name, "wb"))) { - fprintf(stderr, "Cannot open file for output.\n"); - return(-1); - } - 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 +289,6 @@ } - /********************************************* * get RGB pixbuf and convert to YUV 4:2:0 * image and write to mpeg stream @@ -141,16 +328,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 +345,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
_______________________________________________ grass-dev mailing list grass-dev@grass.itc.it http://grass.itc.it/mailman/listinfo/grass-dev