Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/world_entities/recorder.cc @ 6731

Last change on this file since 6731 was 6731, checked in by bensch, 18 years ago

merged avi_play back to the trunk. command https://svn.orxonox.net/orxonox/branches/avi_play . -r6602:HEAD

File size: 9.1 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2006 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11### File Specific:
12   main-programmer: David Hasenfratz
13   co-programmer:
14*/
15
16#include "recorder.h"
17
18#include "load_param.h"
19#include "factory.h"
20#include "resource_manager.h"
21#include "state.h"
22
23using namespace std;
24
25CREATE_FACTORY(Recorder, CL_RECORDER);
26
27
28Recorder::Recorder (const TiXmlElement* root)
29{
30  this->setClassID(CL_RECORDER, "Recorder");
31
32  // initialize libavcodec, and register all codecs and formats
33  av_register_all();
34
35  // Default values
36  stream_duration = 10;
37  stream_frame_rate = 20;
38
39  this->loadParams(root);
40
41  this->toList(OM_COMMON);
42}
43
44
45Recorder::~Recorder ()
46{
47
48}
49
50
51void Recorder::loadParams(const TiXmlElement* root)
52{
53  WorldEntity::loadParams(root);
54
55  LoadParam(root, "duration", this, Recorder, setStreamDuration);
56  LoadParam(root, "fps", this, Recorder, setFPS);
57  LoadParam(root, "name", this, Recorder, initVideo);
58}
59
60
61void Recorder::setStreamDuration(float duration)
62{
63  this->stream_duration = duration;
64}
65
66
67void Recorder::setFPS(float fps)
68{
69  this->stream_frame_rate = fps;
70}
71
72
73void Recorder::initVideo(const char* filename)
74{
75  frame_count = 0;
76  time = 0;
77  stream_nb_frames = (int)(stream_duration * stream_frame_rate);
78
79  // auto detect the output format from the name, default is mpeg
80  output_format = guess_format(NULL, filename, NULL);
81  if (!output_format)
82  {
83      PRINTF(0)("Could not deduce output format from file extension: using MPEG.\n");
84      output_format = guess_format("mpeg", NULL, NULL);
85  }
86  if (!output_format)
87    PRINTF(1)("Could not find suitable output format\n");
88
89  // allocate the output media context
90  format_context = av_alloc_format_context();
91  if (!format_context)
92    PRINTF(1)("Memory error\n");
93
94  format_context->oformat = output_format;
95  snprintf(format_context->filename, sizeof(format_context->filename), "%s", filename);
96
97  // add video stream using the default format codec and initialize the codec
98  if (output_format->video_codec != CODEC_ID_NONE)
99      this->addVideoStream();
100
101  // set the output parameters (must be done even if no parameters)
102  if (av_set_parameters(format_context, NULL) < 0)
103    PRINTF(1)("Invalid output format parameters\n");
104
105  // print some information
106  dump_format(format_context, 0, filename, 1);
107
108  // now that all the parameters are set, we can open the
109  // video codec and allocate the necessary encode buffer
110  if(video_stream)
111    this->openVideo();
112 
113  // open the output file, if needed
114  if(!(output_format->flags & AVFMT_NOFILE))
115  {
116    if(url_fopen(&format_context->pb, filename, URL_WRONLY) < 0)
117      PRINTF(1)("Could not open %s\n", filename);
118  }
119
120  // write the stream header, if any
121  av_write_header(format_context); 
122}
123
124void Recorder::closeVideo()
125{
126  avcodec_close(video_stream->codec);
127  av_free(picture->data[0]);
128  av_free(picture);
129  av_free(buffer);
130
131  // write the trailer, if any
132  av_write_trailer(format_context);
133 
134  // free the streams
135  for(int i = 0; i < format_context->nb_streams; i++)
136    av_freep(&format_context->streams[i]);
137
138  // close the output file
139  if (!(output_format->flags & AVFMT_NOFILE))
140    url_fclose(&format_context->pb);
141
142  // free the stream
143  av_free(format_context);
144}
145
146
147void Recorder::openVideo()
148{
149  codec_context = video_stream->codec;
150
151  // find the video encoder
152  codec = avcodec_find_encoder(codec_context->codec_id);
153  if(!codec)
154    PRINTF(1)("codec not found\n");
155
156  // open the codec
157  if(avcodec_open(codec_context, codec) < 0)
158    PRINTF(1)("could not open codec\n");
159
160  buffer = NULL;
161  if(!(format_context->oformat->flags & AVFMT_RAWPICTURE))
162  {
163      // allocate output buffer
164      // XXX: API change will be done
165      buffer_size = 200000;
166      buffer = new uint8_t[buffer_size];
167  }
168
169  // allocate the encoded raw picture
170  this->allocPicture();
171  if(!picture)
172    PRINTF(0)("Could not allocate picture\n");
173}
174
175
176void Recorder::allocPicture()
177{ 
178  picture = avcodec_alloc_frame();
179  if(!picture)
180  {
181    picture = NULL;
182    return;
183  }
184  size = avpicture_get_size(codec_context->pix_fmt, width, height);
185  picture_buf = new uint8_t[size];
186  if(!picture_buf)
187  {
188      av_free(picture);
189      return;
190  }
191  avpicture_fill((AVPicture *)picture, picture_buf, 
192                  codec_context->pix_fmt, width, height);
193
194
195
196  RGB_frame = avcodec_alloc_frame();
197
198  // Determine required buffer size and allocate buffer
199  size = avpicture_get_size(PIX_FMT_RGB24, width, height);
200  picture_buf = new uint8_t[size];
201
202  // Assign appropriate parts of buffer to image planes in RGB_frame
203  avpicture_fill((AVPicture *)RGB_frame, picture_buf, PIX_FMT_RGB24, width, height);
204}
205
206
207// add a video output stream
208void Recorder::addVideoStream()
209{
210  video_stream = av_new_stream(format_context, 0);
211  if (!video_stream)
212    PRINTF(1)("Could not alloc stream\n");
213 
214  codec_context = video_stream->codec;
215  codec_context->codec_id = output_format->video_codec;
216  codec_context->codec_type = CODEC_TYPE_VIDEO;
217
218  // put sample parameters
219  codec_context->bit_rate = 400000;
220  // resolution must be a multiple of two
221  codec_context->width = State::getResX(); 
222  codec_context->height = State::getResY();
223
224  this->width = codec_context->width;
225  this->height = codec_context->height;
226
227  // time base: this is the fundamental unit of time (in seconds) in terms
228  // of which frame timestamps are represented. for fixed-fps content,
229  // timebase should be 1/framerate and timestamp increments should be
230  // identically 1
231  codec_context->time_base.den = (int)stream_frame_rate; 
232  codec_context->time_base.num = 1;
233  codec_context->gop_size = 12;  // emit one intra frame every twelve frames at most
234  codec_context->pix_fmt = PIX_FMT_YUV420P;
235
236  if (codec_context->codec_id == CODEC_ID_MPEG1VIDEO)
237      // needed to avoid using macroblocks in which some coeffs overflow
238      // this doesnt happen with normal video, it just happens here as the
239      // motion of the chroma plane doesnt match the luma plane
240      codec_context->mb_decision=2;
241
242  // some formats want stream headers to be seperate
243  if(!strcmp(format_context->oformat->name, "mp4") ||
244     !strcmp(format_context->oformat->name, "mov") ||
245     !strcmp(format_context->oformat->name, "3gp"))
246    format_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
247}
248
249
250void Recorder::tick(float dt)
251{
252  time += dt;
253  if(time < 1/stream_frame_rate)
254    return;
255  else
256    time = 0;
257
258  // compute current video time
259  if (video_stream)
260      video_pts = (double)video_stream->pts.val * video_stream->time_base.num / video_stream->time_base.den;
261  else
262      video_pts = 0.0;
263
264  if (!video_stream || video_pts >= stream_duration)
265  {
266    this->toList(OM_DEAD);
267    this->closeVideo();
268  }
269  else
270    // write video frame
271    this->writeVideoFrame();
272}
273
274void Recorder::writeVideoFrame()
275{
276  int out_size, err;
277 
278  codec_context = video_stream->codec;
279 
280  if(frame_count >= stream_nb_frames)
281  {
282    /* no more frame to compress. The codec has a latency of a few
283        frames if using B frames, so we get the last frames by
284        passing the same picture again */
285  }
286  else
287    this->fillYuvImage();
288
289 
290  if(format_context->oformat->flags & AVFMT_RAWPICTURE)
291  {
292    // raw video case. The API will change slightly in the near
293    // futur for that
294    av_init_packet(&packet);
295   
296    packet.flags |= PKT_FLAG_KEY;
297    packet.stream_index= video_stream->index;
298    packet.data= (uint8_t *)picture;
299    packet.size= sizeof(AVPicture);
300   
301    err = av_write_frame(format_context, &packet);
302  }
303  else
304  {
305    // encode the image
306    out_size = avcodec_encode_video(codec_context, buffer, buffer_size, picture);
307    // if zero size, it means the image was buffered
308    if (out_size > 0)
309    {
310      av_init_packet(&packet);
311     
312      packet.pts= av_rescale_q(codec_context->coded_frame->pts, codec_context->time_base, video_stream->time_base);
313      if(codec_context->coded_frame->key_frame)
314        packet.flags |= PKT_FLAG_KEY;
315      packet.stream_index = video_stream->index;
316      packet.data= buffer;
317      packet.size= out_size;
318     
319      // write the compressed frame in the media file
320      err = av_write_frame(format_context, &packet);
321    }
322    else
323      err = 0;
324  }
325
326  if(err != 0)
327    PRINTF(1)("Error while writing video frame\n");
328
329  frame_count++;
330}
331
332
333void Recorder::fillYuvImage()
334{
335  unsigned char *outputImage = 0;
336
337  // Allocate the neccessary memory.
338  outputImage = (unsigned char*)malloc(width * height * 3);
339
340  // Clear the variable.
341  memset(outputImage, 0, width * height * 3);
342
343  // You use the glReadPixels() to read every pixel on the screen
344  // that you specify.  You must use one less than each size.
345  glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, outputImage);
346
347  int i = 0;
348  for(int y=height-1;y>=0;y--)
349  {
350    for(int x=0;x<width*3;x++)
351    {
352      RGB_frame->data[0][y*width*3+x] = outputImage[i];
353      i++;
354    }
355  }
356
357
358  img_convert((AVPicture*)picture, PIX_FMT_YUV420P, (AVPicture*)RGB_frame,
359              PIX_FMT_RGB24, width, height);
360
361  // Clear the allocated memory.
362  free(outputImage);
363}
364
365
366void Recorder::draw() const
367{
368}
Note: See TracBrowser for help on using the repository browser.