Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/new_class_id/src/world_entities/recorder.cc @ 9727

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

orxonox/new_class_id: new Executor construct, that is much more typesafe, faster, and easier to extend…

Also changed the LoadParam process, and adapted ScriptEngine calls

Then at the end, some missing headers appeared, and appended them to all the cc-files again.

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