|
Lines 43-48
Link Here
|
| 43 |
nuv_position_t *current_position; |
43 |
nuv_position_t *current_position; |
| 44 |
} nuv_priv_t; |
44 |
} nuv_priv_t; |
| 45 |
|
45 |
|
|
|
46 |
/* stolen from libmp3lame -mdz */ |
| 47 |
|
| 48 |
/* Used to find nearest matching bitrate |
| 49 |
* we need bitrate-based values |
| 50 |
* determined using tables |
| 51 |
* |
| 52 |
* bitrate in kbps |
| 53 |
* |
| 54 |
* Gabriel Bouvigne 2002-11-03 |
| 55 |
*/ |
| 56 |
int nearestBitrate(const int bitrate) |
| 57 |
{ |
| 58 |
/* borrowed from DM abr presets*/ |
| 59 |
|
| 60 |
int index; // resolved range |
| 61 |
|
| 62 |
const int bitrate_table[] = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}; |
| 63 |
|
| 64 |
|
| 65 |
int lower_range = 0, lower_range_kbps = 0, |
| 66 |
upper_range = 0, upper_range_kbps = 0; |
| 67 |
|
| 68 |
|
| 69 |
int b; |
| 70 |
|
| 71 |
|
| 72 |
// We assume specified bitrate will be 320kbps |
| 73 |
upper_range_kbps = bitrate_table[16]; |
| 74 |
upper_range = 16; |
| 75 |
lower_range_kbps = bitrate_table[16]; |
| 76 |
lower_range = 16; |
| 77 |
|
| 78 |
// Determine which significant bitrates the value specified falls between, |
| 79 |
// if loop ends without breaking then we were correct above that the value was 320 |
| 80 |
for (b = 0; b < 16; b++) { |
| 81 |
if (bitrate < bitrate_table[b+1]) { |
| 82 |
upper_range_kbps = bitrate_table[b+1]; |
| 83 |
upper_range = b+1; |
| 84 |
lower_range_kbps = bitrate_table[b]; |
| 85 |
lower_range = (b); |
| 86 |
break; // We found upper range |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
// Determine which range the value specified is closer to |
| 91 |
if ((upper_range_kbps - bitrate) > (bitrate - lower_range_kbps)) |
| 92 |
index = lower_range; |
| 93 |
else |
| 94 |
index = upper_range; |
| 95 |
|
| 96 |
return bitrate_table[index]; |
| 97 |
} |
| 98 |
|
| 99 |
#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24)) |
| 46 |
|
100 |
|
| 47 |
/** |
101 |
/** |
| 48 |
* Seek to a position relative to the current position, indicated in time. |
102 |
* Seek to a position relative to the current position, indicated in time. |
|
Lines 147-163
Link Here
|
| 147 |
return 0; /* EOF */ |
201 |
return 0; /* EOF */ |
| 148 |
|
202 |
|
| 149 |
#if 0 |
203 |
#if 0 |
| 150 |
printf("NUV frame: frametype: %c, comptype: %c, packetlength: %d\n", |
204 |
printf("NUV frame: frametype: %c, comptype: %c, packetlength: %d, timecode: %d\n", |
| 151 |
rtjpeg_frameheader.frametype, rtjpeg_frameheader.comptype, |
205 |
rtjpeg_frameheader.frametype, rtjpeg_frameheader.comptype, |
| 152 |
rtjpeg_frameheader.packetlength); |
206 |
rtjpeg_frameheader.packetlength, rtjpeg_frameheader.timecode); |
| 153 |
#endif |
207 |
#endif |
| 154 |
|
208 |
|
| 155 |
/* Skip Seekpoint, Text and Sync for now */ |
209 |
/* Skip Seekpoint, Extended header and Sync for now */ |
| 156 |
if ((rtjpeg_frameheader.frametype == 'R') || |
210 |
if ((rtjpeg_frameheader.frametype == 'R') || |
| 157 |
(rtjpeg_frameheader.frametype == 'T') || |
211 |
(rtjpeg_frameheader.frametype == 'X') || |
| 158 |
(rtjpeg_frameheader.frametype == 'S')) |
212 |
(rtjpeg_frameheader.frametype == 'S')) |
| 159 |
return 1; |
213 |
return 1; |
| 160 |
|
214 |
|
|
|
215 |
/* Skip seektable and text (these have a payload) */ |
| 216 |
if (rtjpeg_frameheader.frametype == 'Q' || |
| 217 |
rtjpeg_frameheader.frametype == 'T') { |
| 218 |
stream_skip(demuxer->stream, rtjpeg_frameheader.packetlength); |
| 219 |
return 1; |
| 220 |
} |
| 221 |
|
| 222 |
|
| 161 |
if (((rtjpeg_frameheader.frametype == 'D') && |
223 |
if (((rtjpeg_frameheader.frametype == 'D') && |
| 162 |
(rtjpeg_frameheader.comptype == 'R')) || |
224 |
(rtjpeg_frameheader.comptype == 'R')) || |
| 163 |
(rtjpeg_frameheader.frametype == 'V')) |
225 |
(rtjpeg_frameheader.frametype == 'V')) |
|
Lines 175-187
Link Here
|
| 175 |
/* put RTjpeg tables, Video info to video buffer */ |
237 |
/* put RTjpeg tables, Video info to video buffer */ |
| 176 |
stream_seek ( demuxer->stream, orig_pos ); |
238 |
stream_seek ( demuxer->stream, orig_pos ); |
| 177 |
ds_read_packet ( demuxer->video, demuxer->stream, rtjpeg_frameheader.packetlength + 12, |
239 |
ds_read_packet ( demuxer->video, demuxer->stream, rtjpeg_frameheader.packetlength + 12, |
| 178 |
rtjpeg_frameheader.timecode*0.001, orig_pos, 0 ); |
240 |
rtjpeg_frameheader.timecode*0.001, orig_pos, 0 ); |
| 179 |
|
241 |
|
| 180 |
|
242 |
|
| 181 |
} else |
243 |
} else |
| 182 |
/* copy PCM only */ |
244 |
/* copy PCM only */ |
| 183 |
if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A') && |
245 |
if (demuxer->audio && (rtjpeg_frameheader.frametype == 'A') && |
| 184 |
(rtjpeg_frameheader.comptype == '0')) |
246 |
1 /*(rtjpeg_frameheader.comptype == '0')*/) |
| 185 |
{ |
247 |
{ |
| 186 |
priv->current_audio_frame++; |
248 |
priv->current_audio_frame++; |
| 187 |
if (want_audio) { |
249 |
if (want_audio) { |
|
Lines 192-206
Link Here
|
| 192 |
orig_pos + 12, 0 ); |
254 |
orig_pos + 12, 0 ); |
| 193 |
} else { |
255 |
} else { |
| 194 |
/* skip audio block */ |
256 |
/* skip audio block */ |
| 195 |
stream_seek ( demuxer->stream, |
257 |
stream_skip ( demuxer->stream, |
| 196 |
stream_tell ( demuxer->stream ) |
258 |
rtjpeg_frameheader.packetlength ); |
| 197 |
+ rtjpeg_frameheader.packetlength ); |
|
|
| 198 |
} |
259 |
} |
| 199 |
} |
260 |
} |
| 200 |
|
261 |
|
| 201 |
return 1; |
262 |
return 1; |
| 202 |
} |
263 |
} |
| 203 |
|
264 |
|
|
|
265 |
/* Scan for the extended data in MythTV nuv streams */ |
| 266 |
int demux_xscan_nuv ( demuxer_t* demuxer, int width, int height ) |
| 267 |
{ |
| 268 |
int i; |
| 269 |
struct rtframeheader rtjpeg_frameheader; |
| 270 |
struct extendeddata ext; |
| 271 |
sh_video_t* sh_video = demuxer->video->sh; |
| 272 |
sh_audio_t* sh_audio = demuxer->audio->sh; |
| 273 |
|
| 274 |
for( i = 0 ; i < 2 ; ++i ) { |
| 275 |
if (stream_read ( demuxer->stream, (char*)& rtjpeg_frameheader, sizeof ( rtjpeg_frameheader ) ) < sizeof(rtjpeg_frameheader)) |
| 276 |
return 0; /* EOF */ |
| 277 |
|
| 278 |
if (rtjpeg_frameheader.frametype != 'X') |
| 279 |
stream_skip( demuxer->stream, rtjpeg_frameheader.packetlength ); |
| 280 |
} |
| 281 |
|
| 282 |
if ( rtjpeg_frameheader.frametype != 'X' ) { |
| 283 |
stream_reset( demuxer->stream ); |
| 284 |
return 0; /* No X frame in the expected place */ |
| 285 |
} |
| 286 |
|
| 287 |
if ( rtjpeg_frameheader.packetlength != sizeof(ext) ) { |
| 288 |
printf("NUV extended frame does not have expected length, ignoring\n"); |
| 289 |
stream_reset( demuxer->stream ); |
| 290 |
return 0; |
| 291 |
} |
| 292 |
|
| 293 |
if (stream_read( demuxer->stream, (char*)& ext, sizeof(ext)) < sizeof(ext)) { |
| 294 |
stream_reset( demuxer->stream ); |
| 295 |
return 0; /* EOF */ |
| 296 |
} |
| 297 |
|
| 298 |
if ( ext.version != 1 ) { |
| 299 |
printf("NUV extended frame has unknown version number (%d), ignoring\n", |
| 300 |
ext.version); |
| 301 |
stream_reset( demuxer->stream ); |
| 302 |
return 0; |
| 303 |
} |
| 304 |
|
| 305 |
printf("Detected MythTV stream, reading extended format information\n"); |
| 306 |
|
| 307 |
/* Video parameters */ |
| 308 |
printf("FOURCC: %c%c%c%c\n", |
| 309 |
(ext.video_fourcc >> 24) & 0xff, |
| 310 |
(ext.video_fourcc >> 16) & 0xff, |
| 311 |
(ext.video_fourcc >> 8) & 0xff, |
| 312 |
(ext.video_fourcc) & 0xff); |
| 313 |
if ( ext.video_fourcc == mmioFOURCC('R', 'J', 'P', 'G') ) { |
| 314 |
long buf[128]; |
| 315 |
|
| 316 |
/* Ignore the fact that this is initialising the compression |
| 317 |
Doing this will set the quality factor correctly */ |
| 318 |
RTjpeg_init_compress(buf,width,height,ext.rtjpeg_quality); |
| 319 |
RTjpeg_init_decompress(buf,width,height); |
| 320 |
|
| 321 |
sh_video->format = mmioFOURCC('N', 'U', 'V', '1'); |
| 322 |
} else { |
| 323 |
sh_video->format = ext.video_fourcc; |
| 324 |
sh_video->i_bps = ext.lavc_bitrate; |
| 325 |
} |
| 326 |
|
| 327 |
/* Audio parameters */ |
| 328 |
if ( ext.audio_fourcc == mmioFOURCC('L', 'A', 'M', 'E') ) { |
| 329 |
sh_audio->format = 0x55; |
| 330 |
} else if ( ext.audio_fourcc == mmioFOURCC('R', 'A', 'W', 'A') ) { |
| 331 |
sh_audio->format = 0x1; |
| 332 |
} else { |
| 333 |
printf("Warning! unknown audio format %d\n", ext.audio_fourcc); |
| 334 |
} |
| 335 |
|
| 336 |
sh_audio->samplerate = ext.audio_sample_rate; |
| 337 |
sh_audio->channels = ext.audio_channels; |
| 338 |
|
| 339 |
/* this is a little silly so that we can use libmp3lame's |
| 340 |
nearestBitrate verbatim */ |
| 341 |
if (sh_audio->format != 0x1) |
| 342 |
sh_audio->i_bps = nearestBitrate(ext.audio_channels |
| 343 |
* ext.audio_bits_per_sample |
| 344 |
* ext.audio_sample_rate / |
| 345 |
ext.audio_compression_ratio / 1000) * 1000; |
| 346 |
|
| 347 |
sh_audio->wf->wBitsPerSample = ext.audio_bits_per_sample; |
| 348 |
sh_audio->wf->nAvgBytesPerSec = sh_audio->i_bps / 8; |
| 349 |
sh_audio->wf->nBlockAlign = sh_audio->channels * 2; |
| 350 |
sh_audio->wf->cbSize = 0; |
| 351 |
sh_audio->wf->nSamplesPerSec = ext.audio_sample_rate; |
| 352 |
sh_audio->wf->wFormatTag = sh_audio->format; |
| 353 |
sh_audio->wf->nChannels = ext.audio_channels; |
| 354 |
|
| 355 |
printf("channels=%d bitspersample=%d samplerate=%d audio_compression_ratio=%d\n", ext.audio_channels, ext.audio_bits_per_sample, ext.audio_sample_rate, ext.audio_compression_ratio); |
| 356 |
|
| 357 |
stream_reset( demuxer->stream ); |
| 358 |
|
| 359 |
return 1; |
| 360 |
} |
| 204 |
|
361 |
|
| 205 |
demuxer_t* demux_open_nuv ( demuxer_t* demuxer ) |
362 |
demuxer_t* demux_open_nuv ( demuxer_t* demuxer ) |
| 206 |
{ |
363 |
{ |
|
Lines 240-247
Link Here
|
| 240 |
*/ |
397 |
*/ |
| 241 |
sh_video->ds = demuxer->video; |
398 |
sh_video->ds = demuxer->video; |
| 242 |
|
399 |
|
| 243 |
/* Custom fourcc for internal MPlayer use */ |
|
|
| 244 |
sh_video->format = mmioFOURCC('N', 'U', 'V', '1'); |
| 245 |
|
400 |
|
| 246 |
sh_video->disp_w = rtjpeg_fileheader.width; |
401 |
sh_video->disp_w = rtjpeg_fileheader.width; |
| 247 |
sh_video->disp_h = rtjpeg_fileheader.height; |
402 |
sh_video->disp_h = rtjpeg_fileheader.height; |
|
Lines 258-283
Link Here
|
| 258 |
sh_video->fps = rtjpeg_fileheader.fps; |
413 |
sh_video->fps = rtjpeg_fileheader.fps; |
| 259 |
sh_video->frametime = 1 / sh_video->fps; |
414 |
sh_video->frametime = 1 / sh_video->fps; |
| 260 |
|
415 |
|
| 261 |
if (rtjpeg_fileheader.audioblocks != 0) |
416 |
if (rtjpeg_fileheader.audioblocks != 0) |
| 262 |
{ |
417 |
{ |
| 263 |
sh_audio = new_sh_audio(demuxer, 0); |
418 |
sh_audio = new_sh_audio(demuxer, 0); |
| 264 |
demuxer->audio->sh = sh_audio; |
419 |
demuxer->audio->sh = sh_audio; |
| 265 |
sh_audio->ds = demuxer->audio; |
420 |
sh_audio->ds = demuxer->audio; |
| 266 |
sh_audio->format = 0x1; |
421 |
sh_audio->wf = malloc(sizeof(WAVEFORMATEX)); |
| 267 |
sh_audio->channels = 2; |
422 |
memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX)); |
| 268 |
sh_audio->samplerate = 44100; |
423 |
} |
| 269 |
|
424 |
|
| 270 |
sh_audio->wf = malloc(sizeof(WAVEFORMATEX)); |
425 |
/* Check for extended data (X frame) and read settings from it */ |
| 271 |
memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX)); |
426 |
if (! demux_xscan_nuv( demuxer, |
| 272 |
sh_audio->wf->wFormatTag = sh_audio->format; |
427 |
rtjpeg_fileheader.width, |
| 273 |
sh_audio->wf->nChannels = sh_audio->channels; |
428 |
rtjpeg_fileheader.height) ) { |
| 274 |
sh_audio->wf->wBitsPerSample = 16; |
429 |
/* Otherwise assume defaults */ |
| 275 |
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate; |
430 |
printf("No NUV extended frame, using defaults\n"); |
| 276 |
sh_audio->wf->nAvgBytesPerSec = sh_audio->wf->nChannels* |
431 |
|
| 277 |
sh_audio->wf->wBitsPerSample*sh_audio->wf->nSamplesPerSec/8; |
432 |
/* Custom fourcc for internal MPlayer use */ |
| 278 |
sh_audio->wf->nBlockAlign = sh_audio->channels * 2; |
433 |
sh_video->format = mmioFOURCC('N', 'U', 'V', '1'); |
| 279 |
sh_audio->wf->cbSize = 0; |
434 |
|
| 280 |
} |
435 |
if (rtjpeg_fileheader.audioblocks != 0) |
|
|
436 |
{ |
| 437 |
sh_audio->format = 0x1; |
| 438 |
sh_audio->channels = 2; |
| 439 |
sh_audio->samplerate = 44100; |
| 440 |
sh_audio->wf->wBitsPerSample = 16; |
| 441 |
} |
| 442 |
|
| 443 |
if (rtjpeg_fileheader.audioblocks != 0) |
| 444 |
{ |
| 445 |
sh_audio->wf->wFormatTag = sh_audio->format; |
| 446 |
sh_audio->wf->nChannels = sh_audio->channels; |
| 447 |
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate; |
| 448 |
sh_audio->wf->nAvgBytesPerSec = sh_audio->wf->nChannels* |
| 449 |
sh_audio->wf->wBitsPerSample*sh_audio->wf->nSamplesPerSec/8; |
| 450 |
sh_audio->wf->nBlockAlign = sh_audio->channels * 2; |
| 451 |
sh_audio->wf->cbSize = 0; |
| 452 |
} |
| 453 |
} |
| 281 |
|
454 |
|
| 282 |
priv->index_list = (nuv_position_t*) malloc(sizeof(nuv_position_t)); |
455 |
priv->index_list = (nuv_position_t*) malloc(sizeof(nuv_position_t)); |
| 283 |
priv->index_list->frame = 0; |
456 |
priv->index_list->frame = 0; |
|
Lines 300-308
Link Here
|
| 300 |
|
473 |
|
| 301 |
stream_read(demuxer->stream,(char*)&ns,sizeof(ns)); |
474 |
stream_read(demuxer->stream,(char*)&ns,sizeof(ns)); |
| 302 |
|
475 |
|
| 303 |
if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) ) |
476 |
if ( strncmp ( ns.finfo, "NuppelVideo", 12 ) && |
|
|
477 |
strncmp ( ns.finfo, "MythTVVideo", 12 ) ) |
| 304 |
return 0; /* Not a NuppelVideo file */ |
478 |
return 0; /* Not a NuppelVideo file */ |
| 305 |
if ( strncmp ( ns.version, "0.05", 5 ) ) |
479 |
if ( strncmp ( ns.version, "0.05", 5 ) && |
|
|
480 |
strncmp ( ns.version, "0.06", 5 ) && |
| 481 |
strncmp ( ns.version, "0.07", 5 ) ) |
| 306 |
return 0; /* Wrong version NuppelVideo file */ |
482 |
return 0; /* Wrong version NuppelVideo file */ |
| 307 |
|
483 |
|
| 308 |
/* Return to original position */ |
484 |
/* Return to original position */ |