Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10266 was 10114, checked in by patrick, 18 years ago

merged network back to trunk

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