Panda3D
 All Classes Functions Variables Enumerations
recorderController.cxx
1 // Filename: recorderController.cxx
2 // Created by: drose (24Jan04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "recorderController.h"
16 #include "recorderFrame.h"
17 #include "bamReader.h"
18 #include "bamWriter.h"
19 #include "config_recorder.h"
20 #include "bam.h"
21 #include "clockObject.h"
22 
23 TypeHandle RecorderController::_type_handle;
24 RecorderController::RecorderFactory *RecorderController::_factory = NULL;
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: RecorderController::Constructor
28 // Access: Published
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 RecorderController::
32 RecorderController() {
33  _clock_offset = 0.0;
34  _frame_offset = 0;
35  _writer = (BamWriter *)NULL;
36  _reader = (BamReader *)NULL;
37  _frame_tie = true;
38  _user_table = new RecorderTable;
39  _user_table_modified = false;
40  _file_table = NULL;
41  _active_table = NULL;
42  _eof = false;
43 }
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: RecorderController::Destructor
47 // Access: Published
48 // Description:
49 ////////////////////////////////////////////////////////////////////
50 RecorderController::
51 ~RecorderController() {
52  close();
53  delete _user_table;
54 }
55 
56 ////////////////////////////////////////////////////////////////////
57 // Function: RecorderController::begin_record
58 // Access: Published
59 // Description: Begins recording data to the indicated filename. All
60 // of the recorders in use should already have been
61 // added.
62 ////////////////////////////////////////////////////////////////////
64 begin_record(const Filename &filename) {
65  close();
66  _filename = filename;
68  _clock_offset = global_clock->get_frame_time();
69  _frame_offset = global_clock->get_frame_count();
70 
71  time(&_header._start_time);
72 
73  if (!_dout.open(_filename)) {
74  recorder_cat.error() << "Unable to open " << _filename << "\n";
75  return false;
76  }
77 
78  if (!_dout.write_header(_bam_header)) {
79  recorder_cat.error() << "Unable to write to " << _filename << "\n";
80  return false;
81  }
82 
83  _writer = new BamWriter(&_dout);
84 
85  if (!_writer->init()) {
86  close();
87  return false;
88  }
89 
90  // Write out the header information.
91  _writer->write_object(&_header);
92 
93  _user_table_modified = true;
94 
95  // Tell all of our recorders that they're live now.
96  _user_table->set_flags(RecorderBase::F_recording);
97 
98  recorder_cat.info()
99  << "Recording session to " << _filename << "\n";
100 
101  return true;
102 }
103 
104 ////////////////////////////////////////////////////////////////////
105 // Function: RecorderController::begin_playback
106 // Access: Published
107 // Description: Begins playing back data from the indicated filename.
108 // All of the recorders in use should already have been
109 // added, although this may define additional recorders
110 // if they are present in the file (these new recorders
111 // will not be used). This may also undefine recorders
112 // that were previously added but are not present in the
113 // file.
114 ////////////////////////////////////////////////////////////////////
116 begin_playback(const Filename &filename) {
117  close();
118  _filename = filename;
119  ClockObject *global_clock = ClockObject::get_global_clock();
120  _clock_offset = global_clock->get_frame_time();
121  _frame_offset = global_clock->get_frame_count();
122 
123  if (!_din.open(_filename)) {
124  recorder_cat.error() << "Unable to open " << _filename << "\n";
125  return false;
126  }
127 
128  string head;
129  if (!_din.read_header(head, _bam_header.size()) || head != _bam_header) {
130  recorder_cat.error() << "Unable to read " << _filename << "\n";
131  return false;
132  }
133 
134  _reader = new BamReader(&_din);
135  if (!_reader->init()) {
136  close();
137  return false;
138  }
139 
140  _user_table_modified = true;
141  _active_table = new RecorderTable;
142  _eof = false;
143 
144  // Start out by reading the RecorderHeader.
145  TypedWritable *object = _reader->read_object();
146 
147  if (object == (TypedWritable *)NULL ||
148  !object->is_of_type(RecorderHeader::get_class_type())) {
149  recorder_cat.error()
150  << _filename << " does not contain a recorded session.\n";
151  close();
152  return false;
153  }
154 
155  if (!_reader->resolve()) {
156  recorder_cat.warning()
157  << "Unable to resolve header data.\n";
158  }
159 
160  RecorderHeader *new_header = DCAST(RecorderHeader, object);
161  _header = (*new_header);
162  delete new_header;
163 
164  // Now read the first frame.
165  _next_frame = read_frame();
166  if (_next_frame == (RecorderFrame *)NULL) {
167  recorder_cat.error()
168  << _filename << " does not contain any frames.\n";
169  close();
170  return false;
171  }
172 
173  recorder_cat.info()
174  << "Playing back session from " << _filename << "\n";
175 
176  return true;
177 }
178 
179 ////////////////////////////////////////////////////////////////////
180 // Function: RecorderController::close
181 // Access: Published
182 // Description: Finishes recording data to the indicated filename.
183 ////////////////////////////////////////////////////////////////////
185 close() {
186  if (_writer != (BamWriter *)NULL) {
187  delete _writer;
188  _writer = NULL;
189 
190  // Tell all of our recorders that they're no longer recording.
191  _user_table->clear_flags(RecorderBase::F_recording);
192  }
193  if (_reader != (BamReader *)NULL) {
194  delete _reader;
195  _reader = NULL;
196 
197  // Tell all of our recorders that they're no longer playing.
198  _active_table->clear_flags(RecorderBase::F_playing);
199  }
200  _dout.close();
201  _din.close();
202 
203  if (_file_table != (RecorderTable *)NULL) {
204  delete _file_table;
205  _file_table = (RecorderTable *)NULL;
206  }
207 
208  if (_active_table != (RecorderTable *)NULL) {
209  delete _active_table;
210  _active_table = (RecorderTable *)NULL;
211  }
212 }
213 
214 ////////////////////////////////////////////////////////////////////
215 // Function: RecorderController::record_frame
216 // Access: Published
217 // Description: Gets the next frame of data from all of the active
218 // recorders and adds it to the output file.
219 ////////////////////////////////////////////////////////////////////
222  if (is_recording()) {
223  ClockObject *global_clock = ClockObject::get_global_clock();
224  double now = global_clock->get_frame_time() - _clock_offset;
225  int frame = global_clock->get_frame_count() - _frame_offset;
226 
227  RecorderFrame data(now, frame, _user_table_modified, _user_table);
228  _user_table_modified = false;
229 
230  _writer->write_object(&data);
231  }
232 }
233 
234 ////////////////////////////////////////////////////////////////////
235 // Function: RecorderController::play_frame
236 // Access: Published
237 // Description: Gets the next frame of data from all of the active
238 // recorders and adds it to the output file.
239 ////////////////////////////////////////////////////////////////////
242  if (is_playing()) {
243  if (_eof) {
244  close();
245  return;
246  }
247 
248  ClockObject *global_clock = ClockObject::get_global_clock();
249  double now = global_clock->get_frame_time() - _clock_offset;
250  int frame = global_clock->get_frame_count() - _frame_offset;
251 
252  while (_next_frame != (RecorderFrame *)NULL) {
253  if (_frame_tie) {
254  if (frame < _next_frame->_frame) {
255  // We haven't reached the next frame yet.
256  return;
257  }
258 
259  // Insist that the clock runs at the same rate as it did in
260  // the previous session.
261  //global_clock->set_frame_time(_next_frame->_timestamp + _clock_offset);
262  //global_clock->set_real_time(_next_frame->_timestamp + _clock_offset);
263 
264  // Hmm, that's crummy. Just keep the clock offset up-to-date.
265  _clock_offset = global_clock->get_frame_time() - _next_frame->_timestamp;
266 
267  } else {
268  if (now < _next_frame->_timestamp) {
269  // We haven't reached the next frame yet.
270  return;
271  }
272 
273  // Keep our frame_offset up-to-date.
274  _frame_offset = global_clock->get_frame_count() - _next_frame->_frame;
275  }
276 
277  if (_next_frame->_table_changed && _file_table != _next_frame->_table) {
278  delete _file_table;
279  _file_table = _next_frame->_table;
280  }
281 
282  if (_next_frame->_table_changed || _user_table_modified) {
283  // We're about to change the active table. Temporarily
284  // disable the playing flag on the currently-active recorders.
285  _active_table->clear_flags(RecorderBase::F_playing);
286  delete _active_table;
287  _active_table = new RecorderTable(*_file_table);
288  _active_table->merge_from(*_user_table);
289  _user_table_modified = false;
290 
291  // Now reenable the playing flag on the newly-active
292  // recorders.
293  _active_table->set_flags(RecorderBase::F_playing);
294  }
295 
296  _next_frame->_table = _active_table;
297  _next_frame->play_frame(_reader);
298 
299  delete _next_frame;
300  _next_frame = read_frame();
301  }
302 
303  if (_reader->is_eof()) {
304  recorder_cat.info()
305  << "End of recorded session.\n";
306  } else {
307  recorder_cat.error()
308  << "Unable to read datagram from recorded session.\n";
309  }
310  _eof = true;
311  }
312 }
313 
314 
315 ////////////////////////////////////////////////////////////////////
316 // Function: RecorderController::read_frame
317 // Access: Private
318 // Description: Loads the next frame data from the playback session
319 // file. Returns the frame data pointer on success, or
320 // NULL on failure.
321 ////////////////////////////////////////////////////////////////////
322 RecorderFrame *RecorderController::
323 read_frame() {
324  TypedWritable *object = _reader->read_object();
325 
326  if (object == (TypedWritable *)NULL ||
327  !object->is_of_type(RecorderFrame::get_class_type())) {
328  return NULL;
329  }
330 
331  if (!_reader->resolve()) {
332  recorder_cat.warning()
333  << "Unable to resolve frame data.\n";
334  }
335 
336  return DCAST(RecorderFrame, object);
337 }
bool is_recording() const
Returns true if the controller has been opened for output, false otherwise.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
bool begin_playback(const Filename &filename)
Begins playing back data from the indicated filename.
bool is_eof() const
Returns true if the reader has reached end-of-file, false otherwise.
Definition: bamReader.I:82
A Factory can be used to create an instance of a particular subclass of some general base class...
Definition: factory.h:41
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
bool resolve()
This may be called at any time during processing of the Bam file to resolve all the known pointers so...
Definition: bamReader.cxx:353
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:243
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
bool write_header(const string &header)
Writes a sequence of bytes to the beginning of the datagram file.
void close()
Finishes recording data to the indicated filename.
void close()
Closes the file.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool open(const FileReference *file)
Opens the indicated filename for reading.
bool read_header(string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
bool open(const FileReference *file)
Opens the indicated filename for writing.
bool is_playing() const
Returns true if the controller has been opened for input, false otherwise.
void record_frame()
Gets the next frame of data from all of the active recorders and adds it to the output file...
void play_frame(BamReader *manager)
Once the raw data has been read in from the session file, and the table has been decoded, decode the raw data and call play_frame on each recorder.
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:94
This object is used by the RecorderController to write (and read) a record of the set of recorders in...
Definition: recorderTable.h:35
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:66
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
Definition: clockObject.I:113
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:152
void clear_flags(short flags)
Clears the given flags on all recorders.
void merge_from(const RecorderTable &other)
Combines the data in the current table (presumably just read from disk, and matching exactly with the...
bool begin_record(const Filename &filename)
Begins recording data to the indicated filename.
This object represents one frame of data in the recorded session file.
Definition: recorderFrame.h:35
void play_frame()
Gets the next frame of data from all of the active recorders and adds it to the output file...
This object contains the header information written out at the beginning of a recorded session file...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
void set_flags(short flags)
Sets the given flags on all recorders.
void close()
Closes the file.
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:98