Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

more renamings

File size: 9.2 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
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.