00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <vlc/vlc.h>
00028 #include <vlc/input.h>
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 static int Open ( vlc_object_t * );
00041 static void Close ( vlc_object_t * );
00042
00043 vlc_module_begin();
00044 set_category( CAT_INPUT );
00045 set_subcategory( SUBCAT_INPUT_DEMUX );
00046 set_description( _("Nuv demuxer") );
00047 set_capability( "demux2", 145 );
00048 set_callbacks( Open, Close );
00049 add_shortcut( "nuv" );
00050 vlc_module_end();
00051
00052
00053
00054
00055 static int Demux ( demux_t * );
00056 static int Control( demux_t *, int, va_list );
00057
00058
00059 typedef struct
00060 {
00061 int64_t i_time;
00062 int64_t i_offset;
00063
00064 } demux_index_entry_t;
00065
00066 typedef struct
00067 {
00068 int i_idx;
00069 int i_idx_max;
00070
00071 demux_index_entry_t *idx;
00072 } demux_index_t;
00073
00074
00075 static void demux_IndexInit( demux_index_t * );
00076 static void demux_IndexClean( demux_index_t * );
00077 static void demux_IndexAppend( demux_index_t *,
00078 int64_t i_time, int64_t i_offset );
00079
00080 static int64_t demux_IndexConvertTime( demux_index_t *, int64_t i_time );
00081
00082 static int64_t demux_IndexFindOffset( demux_index_t *, int64_t i_offset );
00083
00084
00085
00086 typedef struct
00087 {
00088 char id[12];
00089 char version[5];
00090
00091 int i_width;
00092 int i_height;
00093 int i_width_desired;
00094 int i_height_desired;
00095
00096 char i_mode;
00097
00098 double d_aspect;
00099 double d_fps;
00100
00101 int i_video_blocks;
00102 int i_audio_blocks;
00103 int i_text_blocks;
00104
00105 int i_keyframe_distance;
00106
00107 } header_t;
00108
00109 typedef struct
00110 {
00111 char i_type;
00112
00113
00114 char i_compression;
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 char i_keyframe;
00134 uint8_t i_filters;
00135
00136
00137
00138 int i_timecode;
00139
00140 int i_length;
00141
00142 } frame_header_t;
00143
00144
00145 typedef struct
00146 {
00147 int i_version;
00148 vlc_fourcc_t i_video_fcc;
00149
00150 vlc_fourcc_t i_audio_fcc;
00151 int i_audio_sample_rate;
00152 int i_audio_bits_per_sample;
00153 int i_audio_channels;
00154 int i_audio_compression_ratio;
00155 int i_audio_quality;
00156 int i_rtjpeg_quality;
00157 int i_rtjpeg_luma_filter;
00158 int i_rtjpeg_chroma_filter;
00159 int i_lavc_bitrate;
00160 int i_lavc_qmin;
00161 int i_lavc_qmax;
00162 int i_lavc_maxqdiff;
00163 int64_t i_seekable_offset;
00164 int64_t i_keyframe_adjust_offset;
00165
00166 } extended_header_t;
00167
00168 struct demux_sys_t
00169 {
00170 header_t hdr;
00171 extended_header_t exh;
00172
00173 int64_t i_pcr;
00174 es_out_id_t *p_es_video;
00175 int i_extra_f;
00176 uint8_t *p_extra_f;
00177
00178 es_out_id_t *p_es_audio;
00179
00180
00181 demux_index_t idx;
00182 };
00183
00184 static int HeaderLoad( demux_t *, header_t *h );
00185 static int FrameHeaderLoad( demux_t *, frame_header_t *h );
00186 static int ExtendedHeaderLoad( demux_t *, extended_header_t *h );
00187
00188
00189
00190
00191 static int Open( vlc_object_t * p_this )
00192 {
00193 demux_t *p_demux = (demux_t*)p_this;
00194 demux_sys_t *p_sys;
00195 uint8_t *p_peek;
00196 frame_header_t fh;
00197 vlc_bool_t b_extended;
00198
00199
00200 if( stream_Peek( p_demux->s, &p_peek, 12 ) != 12 ||
00201 ( strncmp( (char *)p_peek, "MythTVVideo", 11 ) &&
00202 strncmp( (char *)p_peek, "NuppelVideo", 11 ) ) )
00203 return VLC_EGENERIC;
00204
00205 p_sys = malloc( sizeof( demux_sys_t ) );
00206 memset( p_sys, 0, sizeof( demux_sys_t ) );
00207 p_sys->p_es_video = NULL;
00208 p_sys->p_es_audio = NULL;
00209 p_sys->p_extra_f = NULL;
00210 p_sys->i_pcr = -1;
00211 demux_IndexInit( &p_sys->idx );
00212
00213 if( HeaderLoad( p_demux, &p_sys->hdr ) )
00214 goto error;
00215
00216
00217 if( FrameHeaderLoad( p_demux, &fh ) || fh.i_type != 'D' )
00218 goto error;
00219 if( fh.i_length > 0 )
00220 {
00221 if( fh.i_compression == 'F' )
00222 {
00223
00224 p_sys->i_extra_f = fh.i_length;
00225 p_sys->p_extra_f = malloc( fh.i_length );
00226 if( stream_Read( p_demux->s,
00227 p_sys->p_extra_f, fh.i_length ) != fh.i_length )
00228 goto error;
00229 }
00230 else
00231 {
00232
00233 msg_Warn( p_demux, "unsupported 'D' frame (c=%c)", fh.i_compression );
00234 if( stream_Read( p_demux->s, NULL, fh.i_length ) != fh.i_length )
00235 goto error;
00236 }
00237 }
00238
00239
00240 if( stream_Peek( p_demux->s, &p_peek, 1 ) != 1 )
00241 goto error;
00242 if( p_peek[0] == 'X' )
00243 {
00244 b_extended = VLC_TRUE;
00245
00246 if( FrameHeaderLoad( p_demux, &fh ) )
00247 goto error;
00248 if( fh.i_length != 512 )
00249 goto error;
00250
00251 if( ExtendedHeaderLoad( p_demux, &p_sys->exh ) )
00252 goto error;
00253
00254 }
00255 else
00256 {
00257 b_extended = VLC_FALSE;
00258
00259
00260
00261 msg_Err( p_demux, "incomplete NUV support (upload samples)" );
00262 goto error;
00263 }
00264
00265
00266 if( p_sys->hdr.i_video_blocks != 0 )
00267 {
00268 es_format_t fmt;
00269
00270 es_format_Init( &fmt, VIDEO_ES, p_sys->exh.i_video_fcc );
00271 fmt.video.i_width = p_sys->hdr.i_width;
00272 fmt.video.i_height = p_sys->hdr.i_height;
00273 fmt.i_extra = p_sys->i_extra_f;
00274 fmt.p_extra = p_sys->p_extra_f;
00275
00276 p_sys->p_es_video = es_out_Add( p_demux->out, &fmt );
00277 }
00278 if( p_sys->hdr.i_audio_blocks != 0 )
00279 {
00280 es_format_t fmt;
00281
00282 es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC('m','p','g','a') );
00283 fmt.audio.i_rate = p_sys->exh.i_audio_sample_rate;
00284 fmt.audio.i_bitspersample = p_sys->exh.i_audio_bits_per_sample;
00285
00286 p_sys->p_es_audio = es_out_Add( p_demux->out, &fmt );
00287 }
00288 if( p_sys->hdr.i_text_blocks != 0 )
00289 {
00290 msg_Warn( p_demux, "text not yet supported (upload samples)" );
00291 }
00292
00293
00294
00295 p_demux->pf_demux = Demux;
00296 p_demux->pf_control = Control;
00297 p_demux->p_sys = p_sys;
00298
00299 return VLC_SUCCESS;
00300
00301 error:
00302 msg_Warn( p_demux, "cannot load Nuv file" );
00303 free( p_sys );
00304 return VLC_EGENERIC;
00305 }
00306
00307
00308
00309
00310 static void Close( vlc_object_t * p_this )
00311 {
00312 demux_t *p_demux = (demux_t*)p_this;
00313 demux_sys_t *p_sys = p_demux->p_sys;
00314
00315 if( p_sys->p_extra_f )
00316 free( p_sys->p_extra_f );
00317 demux_IndexClean( &p_sys->idx );
00318 free( p_sys );
00319 }
00320
00321
00322
00323
00324
00325
00326 static int Demux( demux_t *p_demux )
00327 {
00328 demux_sys_t *p_sys = p_demux->p_sys;
00329 frame_header_t fh;
00330 block_t *p_data;
00331
00332 for( ;; )
00333 {
00334 if( p_demux->b_die )
00335 return -1;
00336
00337 if( FrameHeaderLoad( p_demux, &fh ) )
00338 return 0;
00339
00340 if( fh.i_type == 'A' || fh.i_type == 'V' )
00341 break;
00342
00343
00344
00345 if( fh.i_type != 'R' )
00346 {
00347 stream_Read( p_demux->s, NULL, fh.i_length );
00348 }
00349 }
00350
00351
00352 p_data = stream_Block( p_demux->s, fh.i_length );
00353 p_data->i_dts = (int64_t)fh.i_timecode * 1000;
00354 p_data->i_pts = (fh.i_type == 'V') ? 0 : p_data->i_dts;
00355
00356
00357 demux_IndexAppend( &p_sys->idx, p_data->i_dts, stream_Tell(p_demux->s) );
00358
00359
00360 if( p_data->i_dts > p_sys->i_pcr )
00361 {
00362 p_sys->i_pcr = p_data->i_dts;
00363 es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
00364 }
00365
00366 if( fh.i_type == 'A' && p_sys->p_es_audio )
00367 {
00368 if( fh.i_compression == '3' )
00369 es_out_Send( p_demux->out, p_sys->p_es_audio, p_data );
00370 else
00371 {
00372 msg_Dbg( p_demux, "unsupported compression %c for audio (upload samples)", fh.i_compression );
00373 block_Release( p_data );
00374 }
00375 }
00376 else if( fh.i_type == 'V' && p_sys->p_es_video )
00377 {
00378 if( fh.i_compression >= '4' )
00379 es_out_Send( p_demux->out, p_sys->p_es_video, p_data );
00380 else
00381 {
00382 msg_Dbg( p_demux, "unsupported compression %c for video (upload samples)", fh.i_compression );
00383 block_Release( p_data );
00384 }
00385 }
00386 else
00387 {
00388 block_Release( p_data );
00389 }
00390
00391 return 1;
00392 }
00393
00394
00395
00396
00397 static int Control( demux_t *p_demux, int i_query, va_list args )
00398 {
00399 demux_sys_t *p_sys = p_demux->p_sys;
00400
00401 double f, *pf;
00402 int64_t i64, *pi64;
00403
00404 switch( i_query )
00405 {
00406
00407 case DEMUX_GET_POSITION:
00408 pf = (double*)va_arg( args, double * );
00409 i64 = stream_Size( p_demux->s );
00410 if( i64 > 0 )
00411 *pf = (double)stream_Tell( p_demux->s ) / (double)i64;
00412 else
00413 *pf = 0.0;
00414 return VLC_SUCCESS;
00415
00416 case DEMUX_SET_POSITION:
00417 {
00418 int64_t i_pos;
00419
00420 f = (double)va_arg( args, double );
00421 i_pos = stream_Size( p_demux->s ) * f;
00422
00423 i64 = demux_IndexFindOffset( &p_sys->idx, i_pos );
00424
00425 p_sys->i_pcr = -1;
00426
00427 if( i64 >= 0 )
00428 return stream_Seek( p_demux->s, i64 );
00429
00430 if( p_sys->idx.i_idx > 0 )
00431 {
00432 if( stream_Seek( p_demux->s, p_sys->idx.idx[p_sys->idx.i_idx-1].i_offset ) )
00433 return VLC_EGENERIC;
00434 }
00435 else
00436 {
00437 if( stream_Seek( p_demux->s, 0 ) )
00438 return VLC_EGENERIC;
00439 }
00440
00441 while( !p_demux->b_die )
00442 {
00443 frame_header_t fh;
00444 int64_t i_tell;
00445
00446 if( ( i_tell = stream_Tell( p_demux->s ) ) >= i_pos )
00447 break;
00448
00449 if( FrameHeaderLoad( p_demux, &fh ) )
00450 return VLC_EGENERIC;
00451
00452 if( fh.i_type == 'A' || fh.i_type == 'V' )
00453 demux_IndexAppend( &p_sys->idx,(int64_t)fh.i_timecode*1000, i_tell );
00454
00455 if( fh.i_type != 'R' )
00456 stream_Read( p_demux->s, NULL, fh.i_length );
00457 }
00458 return VLC_SUCCESS;
00459 }
00460
00461
00462 case DEMUX_GET_TIME:
00463 pi64 = (int64_t*)va_arg( args, int64_t * );
00464 *pi64 = p_sys->i_pcr >= 0 ? p_sys->i_pcr : 0;
00465 return VLC_SUCCESS;
00466
00467 case DEMUX_SET_TIME:
00468 {
00469 int64_t i_pos;
00470 i64 = (int64_t)va_arg( args, int64_t );
00471
00472 i_pos = demux_IndexConvertTime( &p_sys->idx, i64 );
00473 if( i_pos < 0 )
00474 return VLC_EGENERIC;
00475
00476 if( stream_Seek( p_demux->s, i_pos ) )
00477 return VLC_EGENERIC;
00478
00479 p_sys->i_pcr = -1;
00480
00481 while( !p_demux->b_die )
00482 {
00483 frame_header_t fh;
00484
00485 if( FrameHeaderLoad( p_demux, &fh ) )
00486 return VLC_EGENERIC;
00487
00488 if( fh.i_type == 'A' || fh.i_type == 'V' )
00489 {
00490 int64_t i_time = (int64_t)fh.i_timecode*1000;
00491 int64_t i_tell = stream_Tell(p_demux->s)-12;
00492
00493 demux_IndexAppend( &p_sys->idx, i_time, i_tell );
00494
00495 if( i_time >= i64 )
00496 return stream_Seek( p_demux->s, i_tell );
00497 }
00498 if( fh.i_type != 'R' )
00499 stream_Read( p_demux->s, NULL, fh.i_length );
00500 }
00501 return VLC_SUCCESS;
00502 }
00503
00504 case DEMUX_GET_LENGTH:
00505 pi64 = (int64_t*)va_arg( args, int64_t * );
00506 return VLC_EGENERIC;
00507
00508 case DEMUX_GET_FPS:
00509 pf = (double*)va_arg( args, double * );
00510 *pf = p_sys->hdr.d_fps;
00511 return VLC_SUCCESS;
00512
00513 case DEMUX_GET_META:
00514 default:
00515 return VLC_EGENERIC;
00516
00517 }
00518 }
00519
00520
00521
00522
00523 static inline void GetDoubleLE( double *pd, void *src )
00524 {
00525
00526 #ifdef WORDS_BIGENDIAN
00527 uint8_t *p = (uint8_t*)pd, *q = (uint8_t*)src;
00528 int i;
00529 for( i = 0; i < 8; i++ )
00530 p[i] = q[7-i];
00531 #else
00532 memcpy( pd, src, 8 );
00533 #endif
00534 }
00535
00536
00537
00538 static int HeaderLoad( demux_t *p_demux, header_t *h )
00539 {
00540 uint8_t buffer[72];
00541
00542 if( stream_Read( p_demux->s, buffer, 72 ) != 72 )
00543 return VLC_EGENERIC;
00544
00545
00546 memcpy( h->id, &buffer[ 0], 12 );
00547 memcpy( h->version, &buffer[12], 5 );
00548 h->i_width = GetDWLE( &buffer[20] );
00549 h->i_height = GetDWLE( &buffer[24] );
00550 h->i_width_desired = GetDWLE( &buffer[28] );
00551 h->i_height_desired = GetDWLE( &buffer[32] );
00552 h->i_mode = buffer[36];
00553 GetDoubleLE( &h->d_aspect, &buffer[40] );
00554 GetDoubleLE( &h->d_fps, &buffer[48] );
00555 h->i_video_blocks = GetDWLE( &buffer[56] );
00556 h->i_audio_blocks = GetDWLE( &buffer[60] );
00557 h->i_text_blocks = GetDWLE( &buffer[64] );
00558 h->i_keyframe_distance = GetDWLE( &buffer[68] );
00559 #if 0
00560 msg_Dbg( p_demux, "nuv: h=%s v=%s %dx%d a=%f fps=%f v=%d a=%d t=%d kfd=%d",
00561 h->id, h->version, h->i_width, h->i_height, h->d_aspect,
00562 h->d_fps, h->i_video_blocks, h->i_audio_blocks, h->i_text_blocks,
00563 h->i_keyframe_distance );
00564 #endif
00565 return VLC_SUCCESS;
00566 }
00567
00568
00569
00570 static int FrameHeaderLoad( demux_t *p_demux, frame_header_t *h )
00571 {
00572 uint8_t buffer[12];
00573
00574 if( stream_Read( p_demux->s, buffer, 12 ) != 12 )
00575 return VLC_EGENERIC;
00576
00577 h->i_type = buffer[0];
00578 h->i_compression = buffer[1];
00579 h->i_keyframe = buffer[2];
00580 h->i_filters = buffer[3];
00581
00582 h->i_timecode = GetDWLE( &buffer[4] );
00583 h->i_length = GetDWLE( &buffer[8] );
00584 #if 0
00585 msg_Dbg( p_demux, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%d l=%d",
00586 h->i_type,
00587 h->i_compression ? h->i_compression : ' ',
00588 h->i_keyframe ? h->i_keyframe : ' ',
00589 h->i_filters,
00590 h->i_timecode, h->i_length );
00591 #endif
00592 return VLC_SUCCESS;
00593 }
00594
00595 static int ExtendedHeaderLoad( demux_t *p_demux, extended_header_t *h )
00596 {
00597 uint8_t buffer[512];
00598
00599 if( stream_Read( p_demux->s, buffer, 512 ) != 512 )
00600 return VLC_EGENERIC;
00601
00602 h->i_version = GetDWLE( &buffer[0] );
00603 h->i_video_fcc = VLC_FOURCC( buffer[4], buffer[5], buffer[6], buffer[7] );
00604 h->i_audio_fcc = VLC_FOURCC( buffer[8], buffer[9], buffer[10], buffer[11] );
00605 h->i_audio_sample_rate = GetDWLE( &buffer[12] );
00606 h->i_audio_bits_per_sample = GetDWLE( &buffer[16] );
00607 h->i_audio_channels = GetDWLE( &buffer[20] );
00608 h->i_audio_compression_ratio = GetDWLE( &buffer[24] );
00609 h->i_audio_quality = GetDWLE( &buffer[28] );
00610 h->i_rtjpeg_quality = GetDWLE( &buffer[32] );
00611 h->i_rtjpeg_luma_filter = GetDWLE( &buffer[36] );
00612 h->i_rtjpeg_chroma_filter = GetDWLE( &buffer[40] );
00613 h->i_lavc_bitrate = GetDWLE( &buffer[44] );
00614 h->i_lavc_qmin = GetDWLE( &buffer[48] );
00615 h->i_lavc_qmin = GetDWLE( &buffer[52] );
00616 h->i_lavc_maxqdiff = GetDWLE( &buffer[56] );
00617 h->i_seekable_offset = GetQWLE( &buffer[60] );
00618 h->i_keyframe_adjust_offset= GetQWLE( &buffer[68] );
00619 #if 0
00620 msg_Dbg( p_demux, "ex hdr: v=%d vffc=%4.4s afcc=%4.4s %dHz %dbits ach=%d acr=%d aq=%d"
00621 "rtjpeg q=%d lf=%d lc=%d lavc br=%d qmin=%d qmax=%d maxqdiff=%d seekableoff=%lld keyfao=%lld",
00622 h->i_version,
00623 (char*)&h->i_video_fcc,
00624 (char*)&h->i_audio_fcc, h->i_audio_sample_rate, h->i_audio_bits_per_sample, h->i_audio_channels,
00625 h->i_audio_compression_ratio, h->i_audio_quality,
00626 h->i_rtjpeg_quality, h->i_rtjpeg_luma_filter, h->i_rtjpeg_chroma_filter,
00627 h->i_lavc_bitrate, h->i_lavc_qmin, h->i_lavc_qmax, h->i_lavc_maxqdiff,
00628 h->i_seekable_offset, h->i_keyframe_adjust_offset );
00629 #endif
00630 return VLC_SUCCESS;
00631 }
00632
00633
00634 #define DEMUX_INDEX_SIZE_MAX (100000)
00635 static void demux_IndexInit( demux_index_t *p_idx )
00636 {
00637 p_idx->i_idx = 0;
00638 p_idx->i_idx_max = 0;
00639 p_idx->idx = NULL;
00640 }
00641 static void demux_IndexClean( demux_index_t *p_idx )
00642 {
00643 if( p_idx->idx )
00644 {
00645 free( p_idx->idx );
00646 p_idx->idx = NULL;
00647 }
00648 }
00649 static void demux_IndexAppend( demux_index_t *p_idx,
00650 int64_t i_time, int64_t i_offset )
00651 {
00652
00653 if( p_idx->i_idx > 0 && p_idx->idx[p_idx->i_idx-1].i_time >= i_time )
00654 return;
00655
00656
00657 if( p_idx->i_idx >= p_idx->i_idx_max )
00658 {
00659 if( p_idx->i_idx >= DEMUX_INDEX_SIZE_MAX )
00660 {
00661
00662 const int64_t i_length = p_idx->idx[p_idx->i_idx-1].i_time -
00663 p_idx->idx[0].i_time;
00664 const int i_count = DEMUX_INDEX_SIZE_MAX/2;
00665 int i, j;
00666
00667
00668 for( i = 1, j = 1; i < p_idx->i_idx; i++ )
00669 {
00670 if( p_idx->idx[i].i_time < j * i_length / i_count )
00671 continue;
00672
00673 p_idx->idx[j++] = p_idx->idx[i];
00674 }
00675 p_idx->i_idx = j;
00676
00677 if( p_idx->i_idx > 3 * DEMUX_INDEX_SIZE_MAX / 4 )
00678 {
00679
00680
00681 for( i = 0; i < p_idx->i_idx/2; i++ )
00682 p_idx->idx[i] = p_idx->idx[2*i];
00683 p_idx->i_idx /= 2;
00684 }
00685 }
00686 else
00687 {
00688 p_idx->i_idx_max += 1000;
00689 p_idx->idx = realloc( p_idx->idx,
00690 p_idx->i_idx_max*sizeof(demux_index_entry_t));
00691 }
00692 }
00693
00694
00695 p_idx->idx[p_idx->i_idx].i_time = i_time;
00696 p_idx->idx[p_idx->i_idx].i_offset = i_offset;
00697
00698 p_idx->i_idx++;
00699 }
00700 static int64_t demux_IndexConvertTime( demux_index_t *p_idx, int64_t i_time )
00701 {
00702 int i_min = 0;
00703 int i_max = p_idx->i_idx-1;
00704
00705
00706 if( p_idx->i_idx <= 0 )
00707 return -1;
00708
00709
00710 if( i_time <= p_idx->idx[0].i_time )
00711 return p_idx->idx[0].i_offset;
00712 if( i_time >= p_idx->idx[i_max].i_time )
00713 return p_idx->idx[i_max].i_offset;
00714
00715
00716 for( ;; )
00717 {
00718 int i_med;
00719
00720 if( i_max - i_min <= 1 )
00721 break;
00722
00723 i_med = (i_min+i_max)/2;
00724 if( p_idx->idx[i_med].i_time < i_time )
00725 i_min = i_med;
00726 else if( p_idx->idx[i_med].i_time > i_time )
00727 i_max = i_med;
00728 else
00729 return p_idx->idx[i_med].i_offset;
00730 }
00731
00732
00733 if( i_time - p_idx->idx[i_min].i_time < p_idx->idx[i_max].i_time - i_time )
00734 return p_idx->idx[i_min].i_offset;
00735 else
00736 return p_idx->idx[i_max].i_offset;
00737 }
00738
00739
00740 static int64_t demux_IndexFindOffset( demux_index_t *p_idx, int64_t i_offset )
00741 {
00742 int i_min = 0;
00743 int i_max = p_idx->i_idx-1;
00744
00745
00746 if( p_idx->i_idx <= 0 )
00747 return -1;
00748
00749
00750 if( i_offset <= p_idx->idx[0].i_offset )
00751 return p_idx->idx[0].i_offset;
00752 if( i_offset == p_idx->idx[i_max].i_offset )
00753 return p_idx->idx[i_max].i_offset;
00754 if( i_offset > p_idx->idx[i_max].i_offset )
00755 return -1;
00756
00757
00758 for( ;; )
00759 {
00760 int i_med;
00761
00762 if( i_max - i_min <= 1 )
00763 break;
00764
00765 i_med = (i_min+i_max)/2;
00766 if( p_idx->idx[i_med].i_offset < i_offset )
00767 i_min = i_med;
00768 else if( p_idx->idx[i_med].i_offset > i_offset )
00769 i_max = i_med;
00770 else
00771 return p_idx->idx[i_med].i_offset;
00772 }
00773
00774
00775 if( i_offset - p_idx->idx[i_min].i_offset < p_idx->idx[i_max].i_offset - i_offset )
00776 return p_idx->idx[i_min].i_offset;
00777 else
00778 return p_idx->idx[i_max].i_offset;
00779 }