trunk/src/emu/video.c
| r29410 | r29411 | |
| 101 | 101 | m_snap_native(true), |
| 102 | 102 | m_snap_width(0), |
| 103 | 103 | m_snap_height(0), |
| 104 | | m_avifile(NULL), |
| 105 | | m_movie_frame_period(attotime::zero), |
| 106 | | m_movie_next_frame_time(attotime::zero), |
| 107 | | m_movie_frame(0) |
| 104 | m_mng_frame_period(attotime::zero), |
| 105 | m_mng_next_frame_time(attotime::zero), |
| 106 | m_mng_frame(0), |
| 107 | m_avi_file(NULL), |
| 108 | m_avi_frame_period(attotime::zero), |
| 109 | m_avi_next_frame_time(attotime::zero), |
| 110 | m_avi_frame(0) |
| 108 | 111 | { |
| 109 | 112 | // request a callback upon exiting |
| 110 | 113 | machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(video_manager::exit), this)); |
| r29410 | r29411 | |
| 359 | 362 | |
| 360 | 363 | void video_manager::begin_recording(const char *name, movie_format format) |
| 361 | 364 | { |
| 362 | | // stop any existign recording |
| 363 | | end_recording(); |
| 364 | | |
| 365 | 365 | // create a snapshot bitmap so we know what the target size is |
| 366 | 366 | create_snapshot_bitmap(NULL); |
| 367 | 367 | |
| 368 | | // reset the state |
| 369 | | m_movie_frame = 0; |
| 370 | | m_movie_next_frame_time = machine().time(); |
| 371 | | |
| 372 | 368 | // start up an AVI recording |
| 373 | 369 | if (format == MF_AVI) |
| 374 | 370 | { |
| 371 | // stop any existing recording |
| 372 | end_recording(format); |
| 373 | |
| 374 | // reset the state |
| 375 | m_avi_frame = 0; |
| 376 | m_avi_next_frame_time = machine().time(); |
| 377 | |
| 375 | 378 | // build up information about this new movie |
| 376 | 379 | avi_movie_info info; |
| 377 | 380 | info.video_format = 0; |
| r29410 | r29411 | |
| 400 | 403 | else |
| 401 | 404 | filerr = open_next(tempfile, "avi"); |
| 402 | 405 | |
| 403 | | // compute the frame time |
| 404 | | m_movie_frame_period = attotime::from_seconds(1000) / info.video_timescale; |
| 405 | | |
| 406 | 406 | // if we succeeded, make a copy of the name and create the real file over top |
| 407 | 407 | if (filerr == FILERR_NONE) |
| 408 | 408 | fullpath = tempfile.fullpath(); |
| r29410 | r29411 | |
| 410 | 410 | |
| 411 | 411 | if (filerr == FILERR_NONE) |
| 412 | 412 | { |
| 413 | // compute the frame time |
| 414 | m_avi_frame_period = attotime::from_seconds(1000) / info.video_timescale; |
| 415 | |
| 413 | 416 | // create the file and free the string |
| 414 | | avi_error avierr = avi_create(fullpath, &info, &m_avifile); |
| 417 | avi_error avierr = avi_create(fullpath, &info, &m_avi_file); |
| 415 | 418 | if (avierr != AVIERR_NONE) |
| 419 | { |
| 416 | 420 | mame_printf_error("Error creating AVI: %s\n", avi_error_string(avierr)); |
| 421 | return end_recording(format); |
| 422 | } |
| 417 | 423 | } |
| 418 | 424 | } |
| 419 | 425 | |
| 420 | 426 | // start up a MNG recording |
| 421 | 427 | else if (format == MF_MNG) |
| 422 | 428 | { |
| 429 | // stop any existing recording |
| 430 | end_recording(format); |
| 431 | |
| 432 | // reset the state |
| 433 | m_mng_frame = 0; |
| 434 | m_mng_next_frame_time = machine().time(); |
| 435 | |
| 423 | 436 | // create a new movie file and start recording |
| 424 | | m_mngfile.reset(global_alloc(emu_file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS))); |
| 437 | m_mng_file.reset(global_alloc(emu_file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS))); |
| 425 | 438 | file_error filerr; |
| 426 | 439 | if (name != NULL) |
| 427 | | filerr = m_mngfile->open(name); |
| 440 | filerr = m_mng_file->open(name); |
| 428 | 441 | else |
| 429 | | filerr = open_next(*m_mngfile, "mng"); |
| 442 | filerr = open_next(*m_mng_file, "mng"); |
| 430 | 443 | |
| 431 | 444 | if (filerr == FILERR_NONE) |
| 432 | 445 | { |
| 433 | 446 | // start the capture |
| 434 | 447 | int rate = (machine().first_screen() != NULL) ? ATTOSECONDS_TO_HZ(machine().first_screen()->frame_period().attoseconds) : screen_device::DEFAULT_FRAME_RATE; |
| 435 | | png_error pngerr = mng_capture_start(*m_mngfile, m_snap_bitmap, rate); |
| 448 | png_error pngerr = mng_capture_start(*m_mng_file, m_snap_bitmap, rate); |
| 436 | 449 | if (pngerr != PNGERR_NONE) |
| 437 | | return end_recording(); |
| 450 | { |
| 451 | mame_printf_error("Error capturing MNG, png_error=%d\n", pngerr); |
| 452 | return end_recording(format); |
| 453 | } |
| 438 | 454 | |
| 439 | 455 | // compute the frame time |
| 440 | | m_movie_frame_period = attotime::from_hz(rate); |
| 456 | m_mng_frame_period = attotime::from_hz(rate); |
| 441 | 457 | } |
| 442 | 458 | else |
| 443 | 459 | { |
| 444 | | mame_printf_error("Error creating MNG\n"); |
| 445 | | m_mngfile.reset(); |
| 460 | mame_printf_error("Error creating MNG, file_error=%d\n", filerr); |
| 461 | m_mng_file.reset(); |
| 446 | 462 | } |
| 447 | 463 | } |
| 448 | 464 | } |
| r29410 | r29411 | |
| 452 | 468 | // end_recording - stop recording of a movie |
| 453 | 469 | //------------------------------------------------- |
| 454 | 470 | |
| 455 | | void video_manager::end_recording() |
| 471 | void video_manager::end_recording(movie_format format) |
| 456 | 472 | { |
| 457 | | // close the file if it exists |
| 458 | | if (m_avifile != NULL) |
| 473 | if (format == MF_AVI) |
| 459 | 474 | { |
| 460 | | avi_close(m_avifile); |
| 461 | | m_avifile = NULL; |
| 475 | // close the file if it exists |
| 476 | if (m_avi_file != NULL) |
| 477 | { |
| 478 | avi_close(m_avi_file); |
| 479 | m_avi_file = NULL; |
| 480 | |
| 481 | // reset the state |
| 482 | m_avi_frame = 0; |
| 483 | } |
| 462 | 484 | } |
| 485 | else if (format == MF_MNG) |
| 486 | { |
| 487 | // close the file if it exists |
| 488 | if (m_mng_file != NULL) |
| 489 | { |
| 490 | mng_capture_stop(*m_mng_file); |
| 491 | m_mng_file.reset(); |
| 463 | 492 | |
| 464 | | // close the file if it exists |
| 465 | | if (m_mngfile != NULL) |
| 466 | | { |
| 467 | | mng_capture_stop(*m_mngfile); |
| 468 | | m_mngfile.reset(); |
| 493 | // reset the state |
| 494 | m_mng_frame = 0; |
| 495 | } |
| 469 | 496 | } |
| 470 | | |
| 471 | | // reset the state |
| 472 | | m_movie_frame = 0; |
| 473 | 497 | } |
| 474 | 498 | |
| 475 | 499 | |
| r29410 | r29411 | |
| 481 | 505 | void video_manager::add_sound_to_recording(const INT16 *sound, int numsamples) |
| 482 | 506 | { |
| 483 | 507 | // only record if we have a file |
| 484 | | if (m_avifile != NULL) |
| 508 | if (m_avi_file != NULL) |
| 485 | 509 | { |
| 486 | 510 | g_profiler.start(PROFILER_MOVIE_REC); |
| 487 | 511 | |
| 488 | 512 | // write the next frame |
| 489 | | avi_error avierr = avi_append_sound_samples(m_avifile, 0, sound + 0, numsamples, 1); |
| 513 | avi_error avierr = avi_append_sound_samples(m_avi_file, 0, sound + 0, numsamples, 1); |
| 490 | 514 | if (avierr == AVIERR_NONE) |
| 491 | | avierr = avi_append_sound_samples(m_avifile, 1, sound + 1, numsamples, 1); |
| 515 | avierr = avi_append_sound_samples(m_avi_file, 1, sound + 1, numsamples, 1); |
| 492 | 516 | if (avierr != AVIERR_NONE) |
| 493 | | end_recording(); |
| 517 | end_recording(MF_AVI); |
| 494 | 518 | |
| 495 | 519 | g_profiler.stop(); |
| 496 | 520 | } |
| r29410 | r29411 | |
| 505 | 529 | void video_manager::exit() |
| 506 | 530 | { |
| 507 | 531 | // stop recording any movie |
| 508 | | end_recording(); |
| 532 | end_recording(MF_AVI); |
| 533 | end_recording(MF_MNG); |
| 509 | 534 | |
| 510 | 535 | // free the snapshot target |
| 511 | 536 | machine().render().target_free(m_snap_target); |
| r29410 | r29411 | |
| 541 | 566 | |
| 542 | 567 | void video_manager::postload() |
| 543 | 568 | { |
| 544 | | m_movie_next_frame_time = machine().time(); |
| 569 | m_avi_next_frame_time = machine().time(); |
| 570 | m_mng_next_frame_time = machine().time(); |
| 545 | 571 | } |
| 546 | 572 | |
| 547 | 573 | |
| r29410 | r29411 | |
| 1189 | 1215 | void video_manager::record_frame() |
| 1190 | 1216 | { |
| 1191 | 1217 | // ignore if nothing to do |
| 1192 | | if (m_mngfile == NULL && m_avifile == NULL) |
| 1218 | if (m_mng_file == NULL && m_avi_file == NULL) |
| 1193 | 1219 | return; |
| 1194 | 1220 | |
| 1195 | 1221 | // start the profiler and get the current time |
| r29410 | r29411 | |
| 1199 | 1225 | // create the bitmap |
| 1200 | 1226 | create_snapshot_bitmap(NULL); |
| 1201 | 1227 | |
| 1202 | | // loop until we hit the right time |
| 1203 | | while (m_movie_next_frame_time <= curtime) |
| 1228 | // handle an AVI recording |
| 1229 | if (m_avi_file != NULL) |
| 1204 | 1230 | { |
| 1205 | | // handle an AVI recording |
| 1206 | | if (m_avifile != NULL) |
| 1231 | // loop until we hit the right time |
| 1232 | while (m_avi_next_frame_time <= curtime) |
| 1207 | 1233 | { |
| 1208 | 1234 | // write the next frame |
| 1209 | | avi_error avierr = avi_append_video_frame(m_avifile, m_snap_bitmap); |
| 1235 | avi_error avierr = avi_append_video_frame(m_avi_file, m_snap_bitmap); |
| 1210 | 1236 | if (avierr != AVIERR_NONE) |
| 1211 | 1237 | { |
| 1212 | 1238 | g_profiler.stop(); |
| 1213 | | return end_recording(); |
| 1239 | end_recording(MF_AVI); |
| 1240 | break; |
| 1214 | 1241 | } |
| 1242 | |
| 1243 | // advance time |
| 1244 | m_avi_next_frame_time += m_avi_frame_period; |
| 1245 | m_avi_frame++; |
| 1215 | 1246 | } |
| 1216 | | |
| 1217 | | // handle a MNG recording |
| 1218 | | if (m_mngfile != NULL) |
| 1247 | } |
| 1248 | |
| 1249 | // handle a MNG recording |
| 1250 | if (m_mng_file != NULL) |
| 1251 | { |
| 1252 | // loop until we hit the right time |
| 1253 | while (m_mng_next_frame_time <= curtime) |
| 1219 | 1254 | { |
| 1220 | 1255 | // set up the text fields in the movie info |
| 1221 | 1256 | png_info pnginfo = { 0 }; |
| 1222 | | if (m_movie_frame == 0) |
| 1257 | if (m_mng_frame == 0) |
| 1223 | 1258 | { |
| 1224 | 1259 | astring text1(emulator_info::get_appname(), " ", build_version); |
| 1225 | 1260 | astring text2(machine().system().manufacturer, " ", machine().system().description); |
| r29410 | r29411 | |
| 1230 | 1265 | // write the next frame |
| 1231 | 1266 | const rgb_t *palette = (machine().first_screen() !=NULL && machine().first_screen()->palette() != NULL) ? machine().first_screen()->palette()->palette()->entry_list_adjusted() : NULL; |
| 1232 | 1267 | int entries = (machine().first_screen() !=NULL && machine().first_screen()->palette() != NULL) ? machine().first_screen()->palette()->entries() : 0; |
| 1233 | | png_error error = mng_capture_frame(*m_mngfile, &pnginfo, m_snap_bitmap, entries, palette); |
| 1268 | png_error error = mng_capture_frame(*m_mng_file, &pnginfo, m_snap_bitmap, entries, palette); |
| 1234 | 1269 | png_free(&pnginfo); |
| 1235 | 1270 | if (error != PNGERR_NONE) |
| 1236 | 1271 | { |
| 1237 | 1272 | g_profiler.stop(); |
| 1238 | | return end_recording(); |
| 1273 | end_recording(MF_MNG); |
| 1274 | break; |
| 1239 | 1275 | } |
| 1276 | |
| 1277 | // advance time |
| 1278 | m_mng_next_frame_time += m_mng_frame_period; |
| 1279 | m_mng_frame++; |
| 1240 | 1280 | } |
| 1241 | | |
| 1242 | | // advance time |
| 1243 | | m_movie_next_frame_time += m_movie_frame_period; |
| 1244 | | m_movie_frame++; |
| 1245 | 1281 | } |
| 1282 | |
| 1246 | 1283 | g_profiler.stop(); |
| 1247 | 1284 | } |
| 1248 | 1285 | |
| r29410 | r29411 | |
| 1264 | 1301 | { |
| 1265 | 1302 | if (!is_recording()) |
| 1266 | 1303 | { |
| 1267 | | begin_recording(NULL, video_manager::MF_MNG); |
| 1304 | begin_recording(NULL, MF_MNG); |
| 1268 | 1305 | popmessage("REC START"); |
| 1269 | 1306 | } |
| 1270 | 1307 | else |
| 1271 | 1308 | { |
| 1272 | | end_recording(); |
| 1309 | end_recording(MF_MNG); |
| 1273 | 1310 | popmessage("REC STOP"); |
| 1274 | 1311 | } |
| 1275 | 1312 | } |
trunk/src/emu/video.h
| r29410 | r29411 | |
| 64 | 64 | bool throttled() const { return m_throttled; } |
| 65 | 65 | float throttle_rate() const { return m_throttle_rate; } |
| 66 | 66 | bool fastforward() const { return m_fastforward; } |
| 67 | | bool is_recording() const { return (m_mngfile != NULL || m_avifile != NULL); } |
| 67 | bool is_recording() const { return (m_mng_file != NULL || m_avi_file != NULL); } |
| 68 | 68 | |
| 69 | 69 | // setters |
| 70 | 70 | void set_frameskip(int frameskip); |
| r29410 | r29411 | |
| 89 | 89 | void save_active_screen_snapshots(); |
| 90 | 90 | |
| 91 | 91 | // movies |
| 92 | | void begin_recording(const char *name, movie_format format = MF_AVI); |
| 93 | | void end_recording(); |
| 92 | void begin_recording(const char *name, movie_format format); |
| 93 | void end_recording(movie_format format); |
| 94 | 94 | void add_sound_to_recording(const INT16 *sound, int numsamples); |
| 95 | 95 | |
| 96 | 96 | private: |
| r29410 | r29411 | |
| 165 | 165 | INT32 m_snap_width; // width of snapshots (0 == auto) |
| 166 | 166 | INT32 m_snap_height; // height of snapshots (0 == auto) |
| 167 | 167 | |
| 168 | | // movie recording |
| 169 | | auto_pointer<emu_file> m_mngfile; // handle to the open movie file |
| 170 | | avi_file * m_avifile; // handle to the open movie file |
| 171 | | attotime m_movie_frame_period; // period of a single movie frame |
| 172 | | attotime m_movie_next_frame_time; // time of next frame |
| 173 | | UINT32 m_movie_frame; // current movie frame number |
| 168 | // movie recording - MNG |
| 169 | auto_pointer<emu_file> m_mng_file; // handle to the open movie file |
| 170 | attotime m_mng_frame_period; // period of a single movie frame |
| 171 | attotime m_mng_next_frame_time; // time of next frame |
| 172 | UINT32 m_mng_frame; // current movie frame number |
| 173 | |
| 174 | // movie recording - AVI |
| 175 | avi_file * m_avi_file; // handle to the open movie file |
| 176 | attotime m_avi_frame_period; // period of a single movie frame |
| 177 | attotime m_avi_next_frame_time; // time of next frame |
| 178 | UINT32 m_avi_frame; // current movie frame number |
| 174 | 179 | |
| 175 | 180 | static const UINT8 s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS]; |
| 176 | 181 | |