static int dvd_read_sector(stream_t *stream, dvd_priv_t *d, unsigned char *data) { int len; if(d->packs_left==0) { /** * If we're not at the end of this cell, we can determine the next * VOBU to display using the VOBU_SRI information section of the * DSI. Using this value correctly follows the current angle, * avoiding the doubled scenes in The Matrix, and makes our life * really happy. * * Otherwise, we set our next address past the end of this cell to * force the code above to go to the next cell in the program. */ if(d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) { d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn + ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff ); MP_DBG(stream, "Navi new pos=0x%X \n",d->cur_pack); } else { // end of cell! find next cell! MP_VERBOSE(stream, "--- END OF CELL !!! ---\n"); d->cur_pack=d->cell_last_pack+1; } } read_next: if(d->cur_pack>d->cell_last_pack) { // end of cell! int next=dvd_next_cell(stream, d); if(next>=0) { d->cur_cell=next; // if( d->cur_pgc->cell_playback[d->cur_cell].block_type // == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle-1; d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector; d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector; MP_VERBOSE(stream, "DVD next cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack); } else return -1; // EOF } len = DVDReadBlocks(d->title, d->cur_pack, 1, data); // only == 0 should indicate an error, but some dvdread version are buggy when used with dvdcss if(len <= 0) return -1; //error if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF && data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF) { // found a Navi packet!!! #if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0) navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ])); #else navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t)); #endif if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ) { MP_VERBOSE(stream, "Invalid NAVI packet! lba=0x%X navi=0x%X \n", d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn); } else { // process! d->packs_left = d->dsi_pack.dsi_gi.vobu_ea; MP_DBG(stream, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left); //navPrint_DSI(&d->dsi_pack); MP_TRACE(stream, "\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d->cur_cell, d->dsi_pack.dsi_gi.vobu_c_idn,d->dsi_pack.dsi_gi.vobu_vob_idn, d->cur_pgc->cell_position[d->cur_cell].cell_nr, d->cur_pgc->cell_position[d->cur_cell].vob_id_nr); if(d->angle_seek) { int i,skip=0; for(i=0;i<9;i++) // check if all values zero: if((skip=d->dsi_pack.sml_agli.data[i].address)!=0) break; if(skip && skip!=0x7fffffff) { // sml_agli table has valid data (at least one non-zero): d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+ d->dsi_pack.sml_agli.data[d->dvd_angle-1].address; d->angle_seek=0; d->cur_pack--; MP_VERBOSE(stream, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d->cur_pack); } else { // check if we're in the right cell, jump otherwise: if( (d->dsi_pack.dsi_gi.vobu_c_idn==d->cur_pgc->cell_position[d->cur_cell].cell_nr) && (d->dsi_pack.dsi_gi.vobu_vob_idn==d->cur_pgc->cell_position[d->cur_cell].vob_id_nr) ){ d->angle_seek=0; MP_VERBOSE(stream, "Angle-seek synced by cell/vob IDN search! \n"); } else { // wrong angle, skip this vobu: d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+ d->dsi_pack.dsi_gi.vobu_ea; d->angle_seek=2; // DEBUG } } } } ++d->cur_pack; goto read_next; } ++d->cur_pack; if(d->packs_left>=0) --d->packs_left; if(d->angle_seek) { if(d->angle_seek==2) MP_VERBOSE(stream, "!!! warning! reading packet while angle_seek !!!\n"); goto read_next; // searching for Navi packet } return d->cur_pack-1; }
off_t copy_descriptors(int in_fd, int out_fd, off_t content_length, JOINT_FILE_INFO_T *joint_file_info_p) { int i; struct _buff { int inuse; int pos; int len; unsigned char *p; } *buff; unsigned char *p; int read_idx = 0; int write_idx = 0; int idx_count = 0; off_t read_cnt = 0; off_t total_read_size = 0; size_t target_read_size; int len; int flag_finish = 0; int flag_first_time = 0; int flag_verbose = 0; time_t first_timeout = 0; off_t written = 0; struct timeb marker1,marker2; //,marker3; int marker_count; if(global_param.buffer_size < 1) global_param.buffer_size = 1; if(global_param.stream_chunk_size < 512) global_param.stream_chunk_size = 512; ssize_t blocks_read = 0; int offset = 0; if (joint_file_info_p) offset = joint_file_info_p->iso_seek; // ====================== // 送信バッファを確保 // ====================== debug_log_output("Allocating %d buffers of %d bytes each\n", global_param.buffer_size, global_param.stream_chunk_size); buff = malloc(global_param.buffer_size * sizeof(struct _buff)); if ( buff == NULL ) { debug_log_output("malloc() error.\n"); return (-1 ); } p = malloc(global_param.buffer_size * global_param.stream_chunk_size); if ( p == NULL ) { debug_log_output("malloc() error.\n"); return ( 0 ); } for (i=0; i<global_param.buffer_size; i++) { buff[i].pos = 0; buff[i].inuse = 0; buff[i].len = 0; buff[i].p = p + i*global_param.stream_chunk_size; } // ブロックモードの設定 //set_blocking_mode(in_fd, 0); /* blocking */ set_blocking_mode(out_fd, (global_param.buffer_size>1)); /* non-blocking if multiple buffers */ //debug_log_output("set non-blocking mode"); // Do the calculation this way so global_param.buffer_size<4 always gets flag_first_time //No, don't force this!!! : if (content_length < global_param.stream_chunk_size * (global_param.buffer_size>>2)) flag_first_time = 1; if (global_param.buffer_size < 4) flag_first_time = 1; // If only one buffer, then we work the same as the http_simple_file_send() if (global_param.buffer_size == 1) global_param.flag_buffer_send_asap = 1; ftime(&marker1); marker_count = 0; first_timeout = time(NULL); // ================ // 実体転送開始 // ================ while ( flag_finish < 2 ) { struct timeval tv; fd_set writefds; if (flag_first_time == 0 && first_timeout + 13 <= time(NULL)) { debug_log_output( "****************************************************\n" "** Low bandwidth? send it anyway... **\n" "****************************************************"); flag_first_time = 1; } if (flag_finish || flag_first_time || (idx_count >= (global_param.buffer_size-2))) { if (flag_first_time == 0) { debug_log_output( "*********************************************************\n" "** CACHE FILLED! **\n" "*********************************************************"); flag_first_time = 1; } while(buff[write_idx].inuse) { // If there is nothing more to read, concentrate on writing FD_ZERO(&writefds); FD_SET(out_fd, &writefds); tv.tv_sec = 0; if (flag_finish == 0 && !buff[read_idx].inuse) { // Don't wait for the select because we have room to read tv.tv_usec = 0; } else { tv.tv_usec = 100000; // 100msec maximum wait = 10Hz polling rate } //ftime(&marker2); i = select(FD_SETSIZE, NULL, &writefds, NULL, &tv); //ftime(&marker3); //len = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(len>9) fputc('0'+len/10, stderr); if (i < 0) { // Select returned an error - socket must be closed debug_log_output("select failed. err = %s", strerror(errno)); flag_finish = 2; break; } else if(i > 0) { //ftime(&marker2); len = write(out_fd, buff[write_idx].p + buff[write_idx].pos, (buff[write_idx].len < global_param.socket_chunk_size) ? buff[write_idx].len : global_param.socket_chunk_size ); //ftime(&marker3); //i = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(i>9) fputc('a'+i/10, stderr); if(len > 0) { //fputc('.', stderr); written += len; buff[write_idx].len -= len; marker_count += len; if(!global_param.flag_daemon && (marker_count > 2000000)) { // Display the network transfer rate double rate; ftime(&marker2); rate = (marker2.time-marker1.time) + 0.001*(marker2.millitm-marker1.millitm); if(rate > 0) { rate = 8e-6 * marker_count / rate; /* fprintf(stderr, "%g Mbps\n", rate); */ } marker1 = marker2; marker_count = 0; } if (flag_verbose) { debug_log_output("sent: len =%6d, idx = %4d, idxcount = %4d", len, write_idx, idx_count); } else if(0 && !global_param.flag_daemon) { show_progress(idx_count * 100 / global_param.buffer_size, idx_count); } if (buff[write_idx].len <= 0) { buff[write_idx].inuse = 0; buff[write_idx].len = 0; buff[write_idx].pos = 0; write_idx = (write_idx + 1) % global_param.buffer_size; idx_count --; } else { buff[write_idx].pos += len; } } else { //fputc('-', stderr); // Failed to write - end the stream if (len < 0) { if(errno == EAGAIN) { debug_log_output("write would block"); break; } debug_log_output("write failed after %d bytes. err = %s (%d)", written, strerror(errno), (int)errno); } else { debug_log_output("socket closed by player"); } flag_finish = 2; break; } } else { // Not ready to write, exit from the loop to do a read //if (flag_finish == 0 && !buff[read_idx].inuse) { // fputc(',', stderr); //} else { // fputc('o', stderr); //} break; } } if((flag_finish==1) && !buff[write_idx].inuse) { flag_finish = 2; debug_log_output( "*********************************************************\n" "** SEND FINISHED! **\n" "*********************************************************"); } } // Always attempt a read if we have a buffer available //if (FD_ISSET(in_fd, &readfds)) { if(flag_finish == 0 && !buff[read_idx].inuse) { // target_read_size = (content_length - total_read_size) > 1024 ? 1024 : (content_length - total_read_size); target_read_size = global_param.stream_chunk_size - buff[read_idx].len; /* if (buff[read_idx].p == NULL) { debug_log_output("error! idx: %d", read_idx); } */ ftime(&marker2); if ((joint_file_info_p!=NULL) && (joint_file_info_p->dvd_file != NULL)) { blocks_read = DVDReadBlocks(joint_file_info_p->dvd_file, offset, target_read_size/2048, buff[read_idx].p + buff[read_idx].len); len = (off_t)blocks_read*2048; offset += blocks_read; } else if (joint_file_info_p && read_cnt >= joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = 0; read_cnt = 0; debug_log_output("finished file chunk %d\n", joint_file_info_p->current_file_num); //printf("finished file chunk %d\n", joint_file_info_p->current_file_num); } else { if (joint_file_info_p && target_read_size + read_cnt > joint_file_info_p->file[joint_file_info_p->current_file_num].size) { target_read_size = joint_file_info_p->file[joint_file_info_p->current_file_num].size - read_cnt; debug_log_output("finishing last block of %d\n", joint_file_info_p->current_file_num); //printf("finishing last block of %d\n", joint_file_info_p->current_file_num); } len = read(in_fd, buff[read_idx].p + buff[read_idx].len, target_read_size); read_cnt += len; } //ftime(&marker3); //i = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(i>9) fputc('A'+i/10, stderr); if(len == 0) { if( (joint_file_info_p==NULL) || (joint_file_info_p->dvd_file != NULL) || next_file(&in_fd, joint_file_info_p)) { // 読み込み終わり flag_finish = 1; if (flag_verbose) { debug_log_output("recv: len = %d, idx = %d finish!", len, read_idx); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED! **\n" "*********************************************************"); } if (buff[read_idx].len > 0) { buff[read_idx].inuse = 1; buff[read_idx].pos = 0; } } else { // 次のファイルに続く(ここでは何もしない) } } else if (len > 0) { //fputc(':', stderr); if (flag_verbose) { debug_log_output("recv: len =%6d, idx = %4d, idxcount = %4d", len, read_idx, idx_count); } else if(0 && !global_param.flag_daemon) { show_progress(idx_count * 100 / global_param.buffer_size, idx_count); } buff[read_idx].len += len; total_read_size += len; if (global_param.flag_buffer_send_asap == TRUE || buff[read_idx].len >= global_param.stream_chunk_size) { buff[read_idx].inuse = 1; buff[read_idx].pos = 0; idx_count ++; read_idx = (read_idx + 1) % global_param.buffer_size; } /* if (content_length - total_read_size <= 0) { flag_finish = 1; } */ } else { flag_finish = 1; if (flag_verbose) { debug_log_output("read err?: len = %d, idx = %d, err: %s", len, read_idx, strerror(errno)); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED!(ret = %d) **\n" "*********************************************************", len); } } } } free(p); free(buff); // Memory Free. debug_log_output("copy descriptors end."); // 正常終了 return written; }
static int dvd_read_sector(dvd_priv_t *d, unsigned char *data) { int len; if(d->packs_left==0) { /** * If we're not at the end of this cell, we can determine the next * VOBU to display using the VOBU_SRI information section of the * DSI. Using this value correctly follows the current angle, * avoiding the doubled scenes in The Matrix, and makes our life * really happy. * * Otherwise, we set our next address past the end of this cell to * force the code above to go to the next cell in the program. */ if(d->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) { d->cur_pack= d->dsi_pack.dsi_gi.nv_pck_lbn + ( d->dsi_pack.vobu_sri.next_vobu & 0x7fffffff ); mp_msg(MSGT_DVD,MSGL_DBG2, "Navi new pos=0x%X \n",d->cur_pack); } else { // end of cell! find next cell! mp_msg(MSGT_DVD,MSGL_V, "--- END OF CELL !!! ---\n"); d->cur_pack=d->cell_last_pack+1; } } read_next: if(d->cur_pack>d->cell_last_pack) { // end of cell! int next=dvd_next_cell(d); if(next>=0) { d->cur_cell=next; // if( d->cur_pgc->cell_playback[d->cur_cell].block_type // == BLOCK_TYPE_ANGLE_BLOCK ) d->cur_cell+=dvd_angle; d->cur_pack = d->cur_pgc->cell_playback[ d->cur_cell ].first_sector; d->cell_last_pack=d->cur_pgc->cell_playback[ d->cur_cell ].last_sector; mp_msg(MSGT_DVD,MSGL_V, "DVD next cell: %d pack: 0x%X-0x%X \n",d->cur_cell,d->cur_pack,d->cell_last_pack); } else return -1; // EOF } len = DVDReadBlocks(d->title, d->cur_pack, 1, data); // only == 0 should indicate an error, but some dvdread version are buggy when used with dvdcss if(len <= 0) return -1; //error if(data[38]==0 && data[39]==0 && data[40]==1 && data[41]==0xBF && data[1024]==0 && data[1025]==0 && data[1026]==1 && data[1027]==0xBF) { // found a Navi packet!!! #if DVDREAD_VERSION >= LIBDVDREAD_VERSION(0,9,0) navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ])); #else navRead_DSI(&d->dsi_pack, &(data[ DSI_START_BYTE ]), sizeof(dsi_t)); #endif if(d->cur_pack != d->dsi_pack.dsi_gi.nv_pck_lbn ) { mp_msg(MSGT_DVD,MSGL_V, "Invalid NAVI packet! lba=0x%X navi=0x%X \n", d->cur_pack,d->dsi_pack.dsi_gi.nv_pck_lbn); } else { // process! d->packs_left = d->dsi_pack.dsi_gi.vobu_ea; mp_msg(MSGT_DVD,MSGL_DBG2, "Found NAVI packet! lba=0x%X len=%d \n",d->cur_pack,d->packs_left); //navPrint_DSI(&d->dsi_pack); mp_msg(MSGT_DVD,MSGL_DBG3,"\r### CELL %d: Navi: %d/%d IFO: %d/%d \n",d->cur_cell, d->dsi_pack.dsi_gi.vobu_c_idn,d->dsi_pack.dsi_gi.vobu_vob_idn, d->cur_pgc->cell_position[d->cur_cell].cell_nr, d->cur_pgc->cell_position[d->cur_cell].vob_id_nr); if(d->angle_seek) { int i,skip=0; #if defined(__GNUC__) && ( defined(__sparc__) || defined(hpux) ) // workaround for a bug in the sparc/hpux version of gcc 2.95.X ... 3.2, // it generates incorrect code for unaligned access to a packed // structure member, resulting in an mplayer crash with a SIGBUS // signal. // // See also gcc problem report PR c/7847: // http://gcc.gnu.org/cgi-bin/gnatsweb.pl?database=gcc&cmd=view+audit-trail&pr=7847 for(i=0;i<9;i++) { // check if all values zero: __typeof__(d->dsi_pack.sml_agli.data[i].address) tmp_addr; memcpy(&tmp_addr,&d->dsi_pack.sml_agli.data[i].address,sizeof(tmp_addr)); if((skip=tmp_addr)!=0) break; } #else for(i=0;i<9;i++) // check if all values zero: if((skip=d->dsi_pack.sml_agli.data[i].address)!=0) break; #endif if(skip && skip!=0x7fffffff) { // sml_agli table has valid data (at least one non-zero): d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+ d->dsi_pack.sml_agli.data[dvd_angle].address; d->angle_seek=0; d->cur_pack--; mp_msg(MSGT_DVD,MSGL_V, "Angle-seek synced using sml_agli map! new_lba=0x%X \n",d->cur_pack); } else { // check if we're in the right cell, jump otherwise: if( (d->dsi_pack.dsi_gi.vobu_c_idn==d->cur_pgc->cell_position[d->cur_cell].cell_nr) && (d->dsi_pack.dsi_gi.vobu_vob_idn==d->cur_pgc->cell_position[d->cur_cell].vob_id_nr) ){ d->angle_seek=0; mp_msg(MSGT_DVD,MSGL_V, "Angle-seek synced by cell/vob IDN search! \n"); } else { // wrong angle, skip this vobu: d->cur_pack=d->dsi_pack.dsi_gi.nv_pck_lbn+ d->dsi_pack.dsi_gi.vobu_ea; d->angle_seek=2; // DEBUG } } } } ++d->cur_pack; goto read_next; } ++d->cur_pack; if(d->packs_left>=0) --d->packs_left; if(d->angle_seek) { if(d->angle_seek==2) mp_msg(MSGT_DVD,MSGL_V, "!!! warning! reading packet while angle_seek !!!\n"); goto read_next; // searching for Navi packet } return d->cur_pack-1; }
off_t copy_descriptors(int in_fd, int out_fd, off_t content_length, JOINT_FILE_INFO_T *joint_file_info_p) { int i; pthread_t id; pthread_attr_t attr; thread_param_type param; int index = 0; int next_index; int oneback,twoback,shortcount; off_t total_read_size = 0; int len; off_t read_cnt = 0; int flag_verbose = 0; // Set to 1 for debugging sessions, set to 0 for release int flag_send_asap = global_param.flag_buffer_send_asap; ssize_t blocks_read = 0; int offset = 0; if(global_param.buffer_size < 1) global_param.buffer_size = 1; if(global_param.stream_chunk_size < 512) global_param.stream_chunk_size = 512; if (joint_file_info_p) offset = joint_file_info_p->iso_seek; if(flag_verbose && global_param.flag_debug_log_output) { // Redirect streaming debug log to stderr debug_log_initialize(NULL); } // ====================== // 送信バッファを確保 // ====================== param.count = global_param.buffer_size; // Need at least 4 buffers for this to be reasonable if(param.count < 4) param.count = 4; param.length = global_param.stream_chunk_size; debug_log_output("Allocating %d buffers of %d bytes each\n", param.count, param.length); param.bytes = (int *)malloc(param.count * sizeof(int)); param.total = (off_t *)malloc(param.count * sizeof(off_t)); param.buffer = (char **)malloc(param.count * sizeof(char *)); param.mutex = (pthread_mutex_t *)malloc(param.count * sizeof(pthread_mutex_t)); if ( ( param.bytes == NULL ) || ( param.buffer == NULL ) || ( param.mutex == NULL ) ) { debug_log_output("malloc() error.\n"); return (-1 ); } for(i=0; i<param.count; i++) { param.bytes[i] = 0; param.buffer[i] = (char *)malloc(param.length); if(param.buffer[i] == NULL) { debug_log_output("malloc() error.\n"); return ( 0 ); } pthread_mutex_init(¶m.mutex[i], NULL); } // Launch a separate thread to send the data out param.fd = out_fd; param.flags = 0; param.written = 0; // Lock the next block in advance pthread_mutex_lock(¶m.mutex[index]); pthread_attr_init(&attr); // This doesn't seek to work well under Cygwin //#define USE_PTHREAD_PRIORITY #ifdef USE_PTHREAD_PRIORITY { int policy,retcode,curr,mn,mx; struct sched_param schp; memset(&schp, 0, sizeof(schp)); // Get the current settings pthread_getschedparam(pthread_self(), &policy, &schp); curr=schp.sched_priority; mn=sched_get_priority_min(policy); mx=sched_get_priority_max(policy); schp.sched_priority = (curr+mx)/2; debug_log_output("Setting policy %d, priority %d (was=%d, min=%d, max=%d)\n", policy, schp.sched_priority, curr, mn, mx); retcode = pthread_attr_setschedpolicy(&attr,policy); if(retcode != 0) { debug_log_output("pthread_attr_setschedpolicy returned %d\n",retcode); } retcode = pthread_attr_setschedparam(&attr,&schp); if(retcode != 0) { debug_log_output("pthread_attr_setschedparam returned %d\n",retcode); } } #endif if((i = pthread_create(&id, &attr, send_buffers, ¶m)) != 0) { debug_log_output("pthread_create returned %d\n",i); return ( 0 ); } // Watch the two bins behind, and shorten the buffer if the write thread is catching up twoback = param.count - 2; oneback = param.count - 1; shortcount = param.length/4; if(shortcount < 4096) shortcount = 4096; while ( param.flags == 0 ) { // Wait for a buffer to free up next_index = index+1; if(next_index >= param.count) next_index = 0; if(pthread_mutex_lock(¶m.mutex[next_index]) == 0) { for(i=0 ; i < param.length; ) { len = param.length - i; if(len > global_param.file_chunk_size) len = global_param.file_chunk_size; if ((joint_file_info_p!=NULL) && (joint_file_info_p->dvd_file != NULL)) { blocks_read = DVDReadBlocks(joint_file_info_p->dvd_file, offset, len/2048, param.buffer[index] + i); len = (off_t)blocks_read*2048; offset += blocks_read; } else if (joint_file_info_p && read_cnt >= joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = 0; read_cnt = 0; debug_log_output("finished file chunk %d\n", joint_file_info_p->current_file_num); //printf("finished file chunk %d\n", joint_file_info_p->current_file_num); } else { if (joint_file_info_p && len + read_cnt > joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = joint_file_info_p->file[joint_file_info_p->current_file_num].size - read_cnt; debug_log_output("finishing last block of %d\n", joint_file_info_p->current_file_num); //printf("finishing last block of %d\n", joint_file_info_p->current_file_num); } len = read(in_fd, param.buffer[index] + i, len); read_cnt += len; } if(len == 0) { // End of file if( (joint_file_info_p==NULL) || (joint_file_info_p->dvd_file != NULL) || next_file(&in_fd, joint_file_info_p)) { // No more files if (flag_verbose) { debug_log_output("recv: len = %d, idx = %d finish!", len, index); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED! **\n" "*********************************************************"); } param.flags |= FLAG_READ_COMPLETE; break; } else { // We opened up a new file, so continue the loop } } else if (len > 0) { i += len; total_read_size += len; if((content_length > 0) && (total_read_size >= content_length)) { // Only send as much as is requested if(total_read_size > content_length) { debug_log_output("Restricting content length, read=%lld, allowed=%lld", total_read_size, content_length); total_read_size -= content_length; i -= (int)total_read_size;; total_read_size = content_length; } param.flags |= FLAG_READ_COMPLETE; break; } } else { // Read error if (flag_verbose) { debug_log_output("read err?: len = %d, idx = %d, err: %s", len, index, strerror(errno)); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED!(ret = %d) **\n" "*********************************************************", len); } param.flags |= FLAG_READ_COMPLETE; break; } // Check the trailing bins to see if the write thread is catching us // with the exception that if we already fell behind, // then we allow it to rebuffer completely for one block if((i >= shortcount) && (param.bytes[twoback] == 0) && (total_read_size > param.length)) { if(param.bytes[oneback] == 0) { //fputc('.',stderr); // Only one buffer full - reloading } else { //fputc(':',stderr); // Only two buffers full, cut this buffer short break; } } } // Flag this block as ready-to-write param.bytes[index] = i; param.total[index] = total_read_size; if(flag_send_asap) { pthread_mutex_unlock(¶m.mutex[index]); } else if(next_index == (param.count-2)) { // Buffers are filled, so release the sending thread flag_send_asap = TRUE; for(i=0; i<next_index; i++) pthread_mutex_unlock(¶m.mutex[i]); } twoback = oneback; oneback = index; index = next_index; } } // Count the number of bytes in the queue for(i=0,len=0; i<param.count; i++) { len += param.bytes[i]; } debug_log_output("Read complete, read %lld bytes, %d bytes to transmit", total_read_size, len); // If we haven't released the sending thread yet, do so now if(!flag_send_asap) { for(i=0; i<index; i++) pthread_mutex_unlock(¶m.mutex[i]); } // Release the last block, unused pthread_mutex_unlock(¶m.mutex[index]); // Wait for the created thread to complete pthread_join(id,NULL); // Free up the allocated memory if(param.bytes != NULL) free(param.bytes); for(i=0; i<param.count; i++) { if(param.buffer[i] != NULL) free(param.buffer[i]); pthread_mutex_destroy(¶m.mutex[i]); } debug_log_output("copy descriptors end."); return param.written; }
int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { int i, use; int start; int size; int incr; uint8_t *read_ahead_buf; int32_t res; if(!self) return 0; use = -1; if(self->dvd_self->use_read_ahead) { /* first check, if sector is in current chunk */ read_cache_chunk_t cur = self->chunk[self->current]; if (cur.cache_valid && sector >= cur.cache_start_sector && sector <= (cur.cache_start_sector + cur.cache_read_count) && sector + block_count <= cur.cache_start_sector + cur.cache_block_count) use = self->current; else for (i = 0; i < READ_CACHE_CHUNKS; i++) if (self->chunk[i].cache_valid && sector >= self->chunk[i].cache_start_sector && sector <= (self->chunk[i].cache_start_sector + self->chunk[i].cache_read_count) && sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count) use = i; } if (use >= 0) { read_cache_chunk_t *chunk; /* Increment read-ahead size if sector follows the last sector */ if (sector == (self->last_sector + 1)) { if (self->read_ahead_incr < READ_AHEAD_SIZE_MAX) self->read_ahead_incr++; } else { self->read_ahead_size = READ_AHEAD_SIZE_MIN; self->read_ahead_incr = 0; } self->last_sector = sector; /* The following resources need to be protected by a mutex : * self->chunk[*].cache_buffer * self->chunk[*].cache_malloc_size * self->chunk[*].usage_count */ pthread_mutex_lock(&self->lock); chunk = &self->chunk[use]; read_ahead_buf = chunk->cache_buffer + chunk->cache_read_count * DVD_VIDEO_LB_LEN; *buf = chunk->cache_buffer + (sector - chunk->cache_start_sector) * DVD_VIDEO_LB_LEN; chunk->usage_count++; pthread_mutex_unlock(&self->lock); dprintf("libdvdnav: sector=%d, start_sector=%d, last_sector=%d\n", sector, chunk->cache_start_sector, chunk->cache_start_sector + chunk->cache_block_count); /* read_ahead_size */ incr = self->read_ahead_incr >> 1; if ((self->read_ahead_size + incr) > READ_AHEAD_SIZE_MAX) { self->read_ahead_size = READ_AHEAD_SIZE_MAX; } else { self->read_ahead_size += incr; } /* real read size */ start = chunk->cache_start_sector + chunk->cache_read_count; if (chunk->cache_read_count + self->read_ahead_size > chunk->cache_block_count) { size = chunk->cache_block_count - chunk->cache_read_count; } else { size = self->read_ahead_size; /* ensure that the sector we want will be read */ if (sector >= chunk->cache_start_sector + chunk->cache_read_count + size) size = sector - chunk->cache_start_sector - chunk->cache_read_count; } dprintf("libdvdnav: read_ahead_size=%d, size=%d\n", self->read_ahead_size, size); if (size) chunk->cache_read_count += DVDReadBlocks(self->dvd_self->file, start, size, read_ahead_buf); res = DVD_VIDEO_LB_LEN * block_count; } else {
static int fs_read(const char *path, char *buf, size_t count, off_t offset, struct fuse_file_info *fi) { int res; struct ext_file_info *xfi = fi->fh; if (!xfi || !xfi->fi->fd) return -ENOENT; /* fprintf(stderr, "read %s, xfi %d, offset %Ld, count %ld\n", path, xfi, offset, count); */ if (IS_VOB(xfi->domain)) { off_t bk_off; size_t bk_cnt; if (xfi->domain == DVD_READ_TITLE_VOBS) offset += PART_TO_OFFSET(xfi->partnum, part_size); if (offset % DVD_VIDEO_LB_LEN) { return -EIO; } if (count % DVD_VIDEO_LB_LEN) { return -EIO; } bk_off = offset / DVD_VIDEO_LB_LEN; bk_cnt = count / DVD_VIDEO_LB_LEN; res = DVDReadBlocks(xfi->fi->fd, bk_off, bk_cnt, (unsigned char*)buf); if (res > 0) res *= DVD_VIDEO_LB_LEN; //Caching can cause problems with slow drive if READ_AHEAD too high //have also not seen an improvement in performance from using it //Therefore disable and just do read as requested #if 0 /* Is this offset/count contained wholly in the cache? */ if (xfi->cache_off <= offset && offset+count <= xfi->cache_off+xfi->cache_len) { /* Yes, wholly in cache, so use cache only */ memcpy(buf, xfi->cache + (offset - xfi->cache_off), count); res = count; } else { /* No. Read the requested data and then do some read-ahead */ res = DVDReadBlocks(xfi->fi->fd, bk_off, bk_cnt, (unsigned char*)buf); if (res > 0) { res *= DVD_VIDEO_LB_LEN; if (!xfi->cache) xfi->cache = malloc(READ_AHEAD); if (xfi->cache && READ_AHEAD >= DVD_VIDEO_LB_LEN) { int res2; xfi->cache_off = offset + res; res2 = DVDReadBlocks(xfi->fi->fd, xfi->cache_off/DVD_VIDEO_LB_LEN, READ_AHEAD/DVD_VIDEO_LB_LEN, xfi->cache); if (res2 < 0) res2 = 0; xfi->cache_len = res2 * DVD_VIDEO_LB_LEN; } } } #endif } else { if (xfi->cache) { if (offset >= xfi->fi->len || offset < 0) { res = 0; } else { res = min(count, xfi->fi->len - offset); memcpy(buf, xfi->cache + offset, res); } } else { res = -EIO; } } /* fprintf(stderr, "read: %d\n", res); */ return res; }
/* This function will do the cache read */ int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { int result, diff; if(!self) return 0; pthread_mutex_lock(&self->cache_lock); dprintf("Read from %i -> +%i (buffer pos=%i, read_point=%i, size=%i)... ", sector, block_count, self->pos, self->read_point, self->size); if((self->size > 0) && (sector >= self->read_point) && (sector + block_count <= self->pos + self->size)) { /* Hit */ /* Drop any skipped blocks */ diff = sector - self->read_point; if(diff > 0) self->read_point += diff; diff = self->read_point - self->pos; if(((self->start + diff) % CACHE_BUFFER_SIZE) + block_count <= CACHE_BUFFER_SIZE) { dprintf("************** Single read\n"); memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN), block_count * DVD_VIDEO_LB_LEN); self->read_point += block_count; pthread_mutex_unlock(&self->cache_lock); return (int)block_count; } else { int32_t boundary = CACHE_BUFFER_SIZE - self->start; dprintf("************** Multiple read\n"); memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN), boundary * DVD_VIDEO_LB_LEN); memcpy(*buf + (boundary * DVD_VIDEO_LB_LEN), self->buffer, (block_count-boundary) * DVD_VIDEO_LB_LEN); self->read_point += block_count; pthread_mutex_unlock(&self->cache_lock); return (int)block_count; } } else { /* Miss */ fprintf(MSG_OUT, "libdvdnav: DVD read cache miss! (not bad but a performance hit) sector=%d\n", sector); result = DVDReadBlocks( self->dvd_self->file, sector, block_count, *buf); self->read_point = sector+block_count; if(self->read_point > self->pos + self->size) { /* Flush the cache as its not much use */ dprintf("Contents irrelevent... flushing\n"); self->size = 0; self->start = 0; self->pos = sector+block_count; } pthread_mutex_unlock(&self->cache_lock); usleep(300); return result; } /* Should never get here */ return 0; }
void * read_cache_read_thread (void * this_gen) { int cont = 1; int32_t diff, start; uint32_t pos, size, startp, endp; uint32_t s,c; uint8_t *at; read_cache_t *self = (read_cache_t*)this_gen; while(cont) { pthread_mutex_lock(&self->cache_lock); if(self->size >= 0) { diff = self->read_point - self->pos; if(diff >= self->size/2) { dprintf("(II) Read thread -- "); startp = (self->start) % CACHE_BUFFER_SIZE; endp = abs((self->start + diff - 1) % CACHE_BUFFER_SIZE); dprintf("startp = %i, endp = %i -- ",startp, endp); pos = self->pos + diff; size = self->size - diff; start = (self->start + diff) % CACHE_BUFFER_SIZE; /* Fill remainder of buffer */ if(startp > endp) { s = pos + size; c = CACHE_BUFFER_SIZE - startp; at = self->buffer + (startp * DVD_VIDEO_LB_LEN); if(c > 0) { dprintf("(1) Reading from %i to %i to %i ", s, s+c-1, startp); pthread_mutex_unlock(&self->cache_lock); DVDReadBlocks(self->dvd_self->file, s,c, at); pthread_mutex_lock(&self->cache_lock); } s = pos + size + c; c = CACHE_BUFFER_SIZE - size - c; at = self->buffer; if(c > 0) { dprintf("(2) Reading from %i to %i to %i ", s, s+c-1, 0); pthread_mutex_unlock(&self->cache_lock); DVDReadBlocks(self->dvd_self->file, s,c, at); pthread_mutex_lock(&self->cache_lock); } } else { s = pos + size; c = CACHE_BUFFER_SIZE - size; at = self->buffer + (startp * DVD_VIDEO_LB_LEN); if(c > 0) { dprintf("(3) Reading from %i to %i to %i ", s, s+c-1, startp); pthread_mutex_unlock(&self->cache_lock); DVDReadBlocks(self->dvd_self->file, s,c, at); pthread_mutex_lock(&self->cache_lock); } } dprintf("\n"); self->pos = pos; self->start = start; self->size = CACHE_BUFFER_SIZE; } } pthread_mutex_unlock(&self->cache_lock); cont = (self->buffer != NULL); usleep(100); } return NULL; }