2 * Simple movie writer for vnc; based on Libavformat API example from FFMPEG
4 * Copyright (c) 2003 Fabrice Bellard, 2004 Johannes E. Schindelin
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 #define M_PI 3.1415926535897931
35 #include <rfb/rfbclient.h>
37 #define STREAM_FRAME_RATE 25 /* 25 images/s */
39 /**************************************************************/
42 AVFrame *picture, *tmp_picture;
43 uint8_t *video_outbuf;
44 int frame_count, video_outbuf_size;
46 /* add a video output stream */
47 AVStream *add_video_stream(AVFormatContext *oc, int codec_id, int w, int h)
52 st = av_new_stream(oc, 0);
54 fprintf(stderr, "Could not alloc stream\n");
58 #if LIBAVFORMAT_BUILD<4629
63 c->codec_id = codec_id;
64 c->codec_type = CODEC_TYPE_VIDEO;
66 /* put sample parameters */
68 /* resolution must be a multiple of two */
71 /* frames per second */
72 #if LIBAVCODEC_BUILD<4754
73 c->frame_rate = STREAM_FRAME_RATE;
74 c->frame_rate_base = 1;
76 c->time_base.den = STREAM_FRAME_RATE;
78 c->pix_fmt = PIX_FMT_YUV420P;
80 c->gop_size = 12; /* emit one intra frame every twelve frames at most */
81 if (c->codec_id == CODEC_ID_MPEG2VIDEO) {
82 /* just for testing, we also add B frames */
85 if (c->codec_id == CODEC_ID_MPEG1VIDEO){
86 /* needed to avoid using macroblocks in which some coeffs overflow
87 this doesnt happen with normal video, it just happens here as the
88 motion of the chroma plane doesnt match the luma plane */
91 /* some formats want stream headers to be seperate */
92 if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))
93 c->flags |= CODEC_FLAG_GLOBAL_HEADER;
98 AVFrame *alloc_picture(int pix_fmt, int width, int height)
101 uint8_t *picture_buf;
104 picture = avcodec_alloc_frame();
107 size = avpicture_get_size(pix_fmt, width, height);
108 picture_buf = malloc(size);
113 avpicture_fill((AVPicture *)picture, picture_buf,
114 pix_fmt, width, height);
118 void open_video(AVFormatContext *oc, AVStream *st)
123 #if LIBAVFORMAT_BUILD<4629
129 /* find the video encoder */
130 codec = avcodec_find_encoder(c->codec_id);
132 fprintf(stderr, "codec not found\n");
137 if (avcodec_open(c, codec) < 0) {
138 fprintf(stderr, "could not open codec\n");
143 if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
144 /* allocate output buffer */
145 /* XXX: API change will be done */
146 video_outbuf_size = 200000;
147 video_outbuf = malloc(video_outbuf_size);
150 /* allocate the encoded raw picture */
151 picture = alloc_picture(c->pix_fmt, c->width, c->height);
153 fprintf(stderr, "Could not allocate picture\n");
157 /* if the output format is not RGB565, then a temporary RGB565
158 picture is needed too. It is then converted to the required
161 if (c->pix_fmt != PIX_FMT_RGB565) {
162 tmp_picture = alloc_picture(PIX_FMT_RGB565, c->width, c->height);
164 fprintf(stderr, "Could not allocate temporary picture\n");
170 void write_video_frame(AVFormatContext *oc, AVStream *st)
174 AVFrame *picture_ptr;
176 #if LIBAVFORMAT_BUILD<4629
182 if (c->pix_fmt != PIX_FMT_RGB565) {
183 /* as we only generate a RGB565 picture, we must convert it
184 to the codec pixel format if needed */
185 img_convert((AVPicture *)picture, c->pix_fmt,
186 (AVPicture *)tmp_picture, PIX_FMT_RGB565,
187 c->width, c->height);
189 picture_ptr = picture;
192 if (oc->oformat->flags & AVFMT_RAWPICTURE) {
193 /* raw video case. The API will change slightly in the near
196 av_init_packet(&pkt);
198 pkt.flags |= PKT_FLAG_KEY;
199 pkt.stream_index= st->index;
200 pkt.data= (uint8_t *)picture_ptr;
201 pkt.size= sizeof(AVPicture);
203 ret = av_write_frame(oc, &pkt);
205 /* encode the image */
206 out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture_ptr);
207 /* if zero size, it means the image was buffered */
210 av_init_packet(&pkt);
212 pkt.pts= c->coded_frame->pts;
213 if(c->coded_frame->key_frame)
214 pkt.flags |= PKT_FLAG_KEY;
215 pkt.stream_index= st->index;
216 pkt.data= video_outbuf;
219 /* write the compressed frame in the media file */
220 ret = av_write_frame(oc, &pkt);
226 fprintf(stderr, "Error while writing video frame\n");
232 void close_video(AVFormatContext *oc, AVStream *st)
234 avcodec_close(st->codec);
235 av_free(picture->data[0]);
238 av_free(tmp_picture->data[0]);
239 av_free(tmp_picture);
241 av_free(video_outbuf);
244 static const char *filename;
245 static AVOutputFormat *fmt;
246 static AVFormatContext *oc;
247 static AVStream *video_st;
248 static double video_pts;
250 static int movie_open(int w, int h) {
251 if (fmt->video_codec != CODEC_ID_NONE) {
252 video_st = add_video_stream(oc, fmt->video_codec, w, h);
256 /* set the output parameters (must be done even if no
258 if (av_set_parameters(oc, NULL) < 0) {
259 fprintf(stderr, "Invalid output format parameters\n");
263 dump_format(oc, 0, filename, 1);
265 /* now that all the parameters are set, we can open the audio and
266 video codecs and allocate the necessary encode buffers */
268 open_video(oc, video_st);
270 /* open the output file, if needed */
271 if (!(fmt->flags & AVFMT_NOFILE)) {
272 if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
273 fprintf(stderr, "Could not open '%s'\n", filename);
278 /* write the stream header, if any */
284 static int movie_close() {
287 /* close each codec */
288 close_video(oc, video_st);
290 /* write the trailer, if any */
291 av_write_trailer(oc);
293 /* free the streams */
294 for(i = 0; i < oc->nb_streams; i++) {
295 av_freep(&oc->streams[i]);
298 if (!(fmt->flags & AVFMT_NOFILE)) {
299 /* close the output file */
303 /* free the stream */
308 static rfbBool quit=FALSE;
309 static void signal_handler(int signal) {
310 fprintf(stderr,"Cleaning up.\n");
314 /**************************************************************/
315 /* VNC callback functions */
316 static rfbBool resize(rfbClient* client) {
317 static rfbBool first=TRUE;
320 perror("I don't know yet how to change resolutions!\n");
322 movie_open(client->width, client->height);
323 signal(SIGINT,signal_handler);
325 client->frameBuffer=tmp_picture->data[0];
327 client->frameBuffer=picture->data[0];
331 static void update(rfbClient* client,int x,int y,int w,int h) {
334 /**************************************************************/
335 /* media file output */
337 int main(int argc, char **argv)
343 /* get a vnc client structure (don't connect yet). */
344 client = rfbGetClient(5,3,2);
345 client->format.redShift=11; client->format.redMax=31;
346 client->format.greenShift=5; client->format.greenMax=63;
347 client->format.blueShift=0; client->format.blueMax=31;
349 /* initialize libavcodec, and register all codecs and formats */
352 if(!strncmp(argv[argc-1],":",1) ||
353 !strncmp(argv[argc-1],"127.0.0.1",9) ||
354 !strncmp(argv[argc-1],"localhost",9))
355 client->appData.encodingsString="raw";
358 for(i=1;i<argc;i++) {
360 if(argc>i+1 && !strcmp("-o",argv[i])) {
363 } else if(argc>i+1 && !strcmp("-t",argv[i])) {
364 stop=time(0)+atoi(argv[i+1]);
369 memmove(argv+i,argv+j,(argc-i)*sizeof(char*));
375 /* auto detect the output format from the name. default is
377 fmt = filename?guess_format(NULL, filename, NULL):0;
379 printf("Could not deduce output format from file extension: using MPEG.\n");
380 fmt = guess_format("mpeg", NULL, NULL);
383 fprintf(stderr, "Could not find suitable output format\n");
387 /* allocate the output media context */
388 oc = av_alloc_format_context();
390 fprintf(stderr, "Memory error\n");
394 snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
396 /* add the audio and video streams using the default format codecs
397 and initialize the codecs */
400 /* open VNC connection */
401 client->MallocFrameBuffer=resize;
402 client->GotFrameBufferUpdate=update;
403 if(!rfbInitClient(client,&argc,argv)) {
404 printf("usage: %s [-o output_file] [-t seconds] server:port\n"
405 "Shoot a movie from a VNC server.\n", argv[0]);
408 if(client->serverPort==-1)
409 client->vncRec->doNotSleep = TRUE; /* vncrec playback */
414 int i=WaitForMessage(client,1000000/STREAM_FRAME_RATE);
420 if(!HandleRFBServerMessage(client))
423 /* compute current audio and video time */
424 video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
426 /* write interleaved audio and video frames */
427 write_video_frame(oc, video_st);
429 if(stop!=0 && stop<time(0))