2 JPC-RR: A x86 PC Hardware Emulator
5 Copyright (C) 2009-2010 H. Ilari Liusvaara
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as published by
9 the Free Software Foundation.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 Based on JPC x86 PC Hardware emulator,
21 A project from the Physics Dept, The University of Oxford
23 Details about original JPC can be found at:
25 www-jpc.physics.ox.ac.uk
29 #include "newpacket.hpp"
31 #include "resampler.hpp"
32 #include "hardsubs.hpp"
34 #include "digital-filter.hpp"
35 #include "timecounter.hpp"
41 #define MINSAMPLES 512
42 #define BUFSAMPLES 2048
43 #define MAXSAMPLES 8192
45 std::vector
<sample_number_t
> audiobuffer
;
47 uint32_t audio_clear
= 0;
48 uint32_t audio_stamp
= 0;
49 uint64_t audio_samples
;
50 uint32_t audiorate
= 44100;
51 uint32_t audio_lag
= 0;
53 void audio_callback(void* x
, Uint8
* stream
, int bytes
)
55 unsigned samples
= bytes
/ 4;
56 audio_samples
+= samples
;
57 audio_stamp
= SDL_GetTicks();
58 audio_clear
= 1000 * audio_samples
/ audiorate
;
60 //Remove the samples that have been missed.
62 if(audio_lag
> audiobuffer
.size()) {
63 std::cerr
<< "Throwing away " << audiobuffer
.size() << " samples as missed" << std::endl
;
64 audio_lag
-= audiobuffer
.size();
65 audiobuffer
.resize(0);
67 std::cerr
<< "Throwing away " << audio_lag
<< " samples as missed" << std::endl
;
68 memmove((Uint8
*)&audiobuffer
[0], (Uint8
*)&audiobuffer
[audio_lag
], 4 * (audiobuffer
.size() - audio_lag
));
69 audiobuffer
.resize(audiobuffer
.size() - audio_lag
);
74 if(samples
> audiobuffer
.size()) {
75 std::cerr
<< "Audio underflow!" << std::endl
;
76 memcpy(stream
, (Uint8
*)&audiobuffer
[0], 4 * audiobuffer
.size());
77 audio_lag
+= (bytes
- 4 * audiobuffer
.size()) / 4;
78 memset(stream
+ 4 * audiobuffer
.size(), 0, bytes
- 4 * audiobuffer
.size());
79 samples
= audiobuffer
.size();
81 memcpy(stream
, (Uint8
*)&audiobuffer
[0], 4 * samples
);
82 memmove((Uint8
*)&audiobuffer
[0], (Uint8
*)&audiobuffer
[samples
], 4 * (audiobuffer
.size() - samples
));
83 audiobuffer
.resize(audiobuffer
.size() - samples
);
86 int next_filename_index(int argc
, char** argv
, int currentindex
)
89 for(int i
= 1; i
< argc
; i
++) {
90 std::string arg
= argv
[i
];
91 if(!split
&& arg
== "--") {
96 if(split
|| !isstringprefix(argv
[i
], "--")) {
103 int main(int argc
, char** argv
)
105 int filenameindex
= -1;
106 uint64_t timecorrection
= 0;
107 uint64_t last_timestamp
= 0;
109 unsigned long percentspeed
= 100;
111 for(int i
= 1; i
< argc
; i
++)
113 if(split
|| !isstringprefix(argv
[i
], "--")) {
114 if(filenameindex
== -1)
116 } else if(isstringprefix(argv
[i
], "--audio-rate=")) {
117 std::string val
= settingvalue(argv
[i
]);
119 unsigned long rate
= strtoul(val
.c_str(), &x
, 10);
120 if(*x
|| !rate
|| rate
> 1000000) {
121 std::cerr
<< "--audio-rate: Bad audio rate" << std::endl
;
125 } else if(isstringprefix(argv
[i
], "--speed=")) {
126 std::string val
= settingvalue(argv
[i
]);
128 unsigned long rate
= strtoul(val
.c_str(), &x
, 10);
129 if(*x
|| !rate
|| rate
> 1000) {
130 std::cerr
<< "--speed: Bad speed" << std::endl
;
134 } else if(isstringprefix(argv
[i
], "--audio-mixer-")) {
135 //We process these later.
136 } else if(isstringprefix(argv
[i
], "--video-hardsub-")) {
137 //We process these later.
138 } else if(!strcmp(argv
[i
], "--"))
141 std::cerr
<< "Bad option: " << argv
[i
] << "." << std::endl
;
144 } catch(std::exception
& e
) {
145 std::cerr
<< "Error processing option: " << argv
[i
] << ":" << e
.what() << std::endl
;
149 std::list
<subtitle
*> subtitles
;
150 hardsub_settings stsettings
;
151 //Initialize audio processing.
153 packet_demux
ademux(mix
, audiorate
);
154 timecounter
acounter(audiorate
);
157 process_audio_resampler_options(ademux
, "--audio-mixer-", argc
, argv
);
158 subtitles
= process_hardsubs_options(stsettings
, "--video-hardsub-", argc
, argv
);
159 } catch(std::exception
& e
) {
160 std::cerr
<< "Error processing options: " << e
.what() << std::endl
;
164 if(filenameindex
< 0) {
165 std::cout
<< "usage: " << argv
[0] << " [<options>] [--] <filename>..." << std::endl
;
166 std::cout
<< "Show video contained in stream <filename> in window." << std::endl
;
167 std::cout
<< "--speed=<speed>" << std::endl
;
168 std::cout
<< "\tSet speed to <speed>%." << std::endl
;
169 print_hardsubs_help("--video-hardsub-");
170 print_audio_resampler_help("--audio-mixer-");
173 read_channel
* in
= new read_channel(argv
[filenameindex
]);
176 SDL_Surface
* swsurf
= NULL
;
177 SDL_Surface
* hwsurf
= NULL
;
178 struct packet
* p
= NULL
;
179 unsigned prev_width
= -1;
180 unsigned prev_height
= -1;
182 if(SDL_Init(SDL_INIT_VIDEO
) < 0) {
183 std::cerr
<< "Can't initialize SDL." << std::endl
;
187 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
188 uint32_t rmask
= 0xFF000000;
189 uint32_t gmask
= 0x00FF0000;
190 uint32_t bmask
= 0x0000FF00;
192 uint32_t rmask
= 0x000000FF;
193 uint32_t gmask
= 0x0000FF00;
194 uint32_t bmask
= 0x00FF0000;
196 int enable_debug
= 0;
197 if(getenv("PLAYDUMP_STATS"))
200 uint64_t lagged_frames
= 0, total_frames
= 0;
201 uint32_t last_realtime_second
= 0;
202 timebase
= SDL_GetTicks();
203 uint64_t audiosamples
= 0;
204 std::list
<image_frame_rgbx
*> picture_buffer
;
205 std::list
<uint64_t> picture_stamp
;
206 uint64_t first_stamp
= 0;
209 aspec
.freq
= audiorate
* percentspeed
/ 100;
210 aspec
.format
= AUDIO_S16LSB
;
212 aspec
.samples
= BUFSAMPLES
/ 2;
213 aspec
.callback
= audio_callback
;
214 if(SDL_OpenAudio(&aspec
, NULL
) < 0) {
215 std::cerr
<< "Can't initialize audio." << std::endl
;
222 //Correct the timestamp and update last seen time;
224 //Exhausted current file, switch to next.
226 timecorrection
= last_timestamp
;
227 std::cerr
<< "Time correction set to " << timecorrection
<< "." << std::endl
;
228 filenameindex
= next_filename_index(argc
, argv
, filenameindex
);
229 if(filenameindex
< 0)
230 break; //No more files.
231 in
= new read_channel(argv
[filenameindex
]);
234 p
->rp_timestamp
+= timecorrection
;
235 last_timestamp
= p
->rp_timestamp
;
237 //If we are too far ahead, slow down a bit.
238 if(audiobuffer
.size() > MAXSAMPLES
)
240 while(p
->rp_timestamp
> acounter
) {
242 //Extract audio sample.
243 sample_number_t s
= ademux
.nextsample();
245 audiobuffer
.push_back(s
);
249 if(p
->rp_major
== 5) {
250 //This is gameinfo packet.
251 subtitle_process_gameinfo(subtitles
, *p
);
253 } else if(p
->rp_major
!= 0) {
254 //Process the audio packet.
255 ademux
.sendpacket(*p
);
257 continue; //Not image.
259 uint32_t timenow
= SDL_GetTicks();
260 uint32_t realtime
= timenow
- timebase
;
262 if(audiobuffer
.size() < MINSAMPLES
&& p
->rp_timestamp
> 0) {
265 continue; //Behind deadline, try to catch up.
267 if(enable_debug
&& realtime
/ 1000 > last_realtime_second
) {
268 last_realtime_second
= realtime
/ 1000;
269 std::cout
<< "\e[1GTime " << last_realtime_second
<< "s: Frames: " << total_frames
270 << "(lagged:" << lagged_frames
<< "), Audio: " << audiosamples
<< "("
271 << audiobuffer
.size() << ")";
275 picture_buffer
.push_back(new image_frame_rgbx(*p
));
276 if(picture_stamp
.empty())
277 first_stamp
= p
->rp_timestamp
/ 1000000;
278 picture_stamp
.push_back(p
->rp_timestamp
);
281 if(picture_stamp
.empty())
283 uint32_t audiocorr
= (SDL_GetTicks() - audio_stamp
) * 100 / percentspeed
;
284 if(first_stamp
>= audio_clear
)
285 if(first_stamp
- audio_clear
> audiocorr
)
287 uint64_t stamp
= *picture_stamp
.begin();
288 picture_stamp
.pop_front();
289 if(!picture_stamp
.empty())
290 first_stamp
= *picture_stamp
.begin() / 1000000;
292 image_frame_rgbx
& frame
= **picture_buffer
.begin();
293 picture_buffer
.pop_front();
295 if(prev_width
!= frame
.get_width() || prev_height
!= frame
.get_height()) {
296 hwsurf
= SDL_SetVideoMode(frame
.get_width(), frame
.get_height(), 0, SDL_SWSURFACE
|
297 SDL_DOUBLEBUF
| SDL_ANYFORMAT
);
298 swsurf
= SDL_CreateRGBSurface(SDL_SWSURFACE
, frame
.get_width(), frame
.get_height(), 32,
299 rmask
, gmask
, bmask
, 0);
301 prev_width
= frame
.get_width();
302 prev_height
= frame
.get_height();
303 for(std::list
<subtitle
*>::iterator j
= subtitles
.begin(); j
!= subtitles
.end(); ++j
)
304 if((*j
)->timecode
<= stamp
&& (*j
)->timecode
+ (*j
)->duration
> stamp
)
305 render_subtitle(frame
, **j
);
307 SDL_LockSurface(swsurf
);
308 memcpy((unsigned char*)swsurf
->pixels
, frame
.get_pixels(), 4 * prev_width
* prev_height
);
309 SDL_UnlockSurface(swsurf
);
310 SDL_BlitSurface(swsurf
, NULL
, hwsurf
, NULL
);
313 if(SDL_PollEvent(&e
) == 1 && e
.type
== SDL_QUIT
)