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