📄 ty.c
字号:
vlc_epg_t *p_epg; /* Channel meta data */ p_meta = vlc_meta_New(); if( m->psz_channel_name ) vlc_meta_SetPublisher( p_meta, m->psz_channel_name ); if( m->psz_channel_call_letter ) vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter ); if( m->psz_channel_number ) vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number ); es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta ); vlc_meta_Delete( p_meta ); /* Event meta data (current/future) */ p_epg = vlc_epg_New( NULL ); if( m->current.psz_name ) { vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL ); //if( m->current.psz_rating ) // TODO but VLC cannot yet handle rating per epg event vlc_epg_SetCurrent( p_epg, 0 ); } if( m->future.psz_name ) { } if( p_epg->i_event > 0 ) es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg ); vlc_epg_Delete( p_epg ); } p_demux->p_sys->xds.b_meta_changed = false;}/* seek to an exact time position within the stream, if possible. * l_seek_time is in nanoseconds, the TIVO time standard. */static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time){ demux_sys_t *p_sys = p_demux->p_sys; int i, i_seq_entry = 0; int i_skip_cnt; long l_cur_pos = stream_Tell(p_demux->s); int i_cur_part = l_cur_pos / TIVO_PART_LENGTH; long l_seek_secs = l_seek_time / 1000000000; uint64_t l_fwd_stamp = 1; /* if we're not seekable, there's nothing to do */ if (!p_sys->b_seekable || !p_sys->b_have_master) return VLC_EGENERIC; msg_Dbg(p_demux, "Skipping to time %02ld:%02ld:%02ld", l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60); /* seek to the proper segment if necessary */ /* first see if we need to go back */ while (l_seek_time < p_sys->l_first_ty_pts) { msg_Dbg(p_demux, "skipping to prior segment."); /* load previous part */ if (i_cur_part == 0) { stream_Seek(p_demux->s, l_cur_pos); msg_Err(p_demux, "Attempt to seek past BOF"); return VLC_EGENERIC; } stream_Seek(p_demux->s, (i_cur_part - 1) * TIVO_PART_LENGTH); i_cur_part--; parse_master(p_demux); } /* maybe we need to go forward */ while (l_seek_time > p_sys->l_final_ty_pts) { msg_Dbg(p_demux, "skipping to next segment."); /* load next part */ if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) { /* error; restore previous file position */ stream_Seek(p_demux->s, l_cur_pos); msg_Err(p_demux, "seek error"); return VLC_EGENERIC; } stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH); i_cur_part++; parse_master(p_demux); } /* our target is somewhere within this part; find the proper chunk using seq_table */ for (i=1; i<p_sys->i_seq_table_size; i++) { if (p_sys->seq_table[i].l_timestamp > l_seek_time) { /* i-1 is the section we want; remember the next timestamp in case we have to use it (this section may not have a proper SEQ hdr for the time we're seeking) */ msg_Dbg(p_demux, "stopping at seq entry %d.", i); l_fwd_stamp = p_sys->seq_table[i].l_timestamp; i_seq_entry = i-1; break; } } /* if we went through the entire last loop and didn't find our target, then we skip to the next part. What has happened is that the actual time we're seeking is within this part, but there isn't a SEQ hdr for it here. So we skip to the next part */ if (i == p_sys->i_seq_table_size) { if ((i_cur_part + 1) * TIVO_PART_LENGTH > p_sys->i_stream_size) { /* error; restore previous file position */ stream_Seek(p_demux->s, l_cur_pos); msg_Err(p_demux, "seek error"); return VLC_EGENERIC; } stream_Seek(p_demux->s, (i_cur_part + 1) * TIVO_PART_LENGTH); i_cur_part++; parse_master(p_demux); i_seq_entry = 0; } /* determine which chunk has our seek_time */ for (i=0; i<p_sys->i_bits_per_seq_entry; i++) { long l_chunk_nr = i_seq_entry * p_sys->i_bits_per_seq_entry + i; long l_chunk_offset = (l_chunk_nr + 1) * CHUNK_SIZE; msg_Dbg(p_demux, "testing part %d chunk %ld mask 0x%02X bit %d", i_cur_part, l_chunk_nr, p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8], i%8); if (p_sys->seq_table[i_seq_entry].chunk_bitmask[i/8] & (1 << (i%8))) { /* check this chunk's SEQ header timestamp */ msg_Dbg(p_demux, "has SEQ. seeking to chunk at 0x%lX", (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset); stream_Seek(p_demux->s, (i_cur_part * TIVO_PART_LENGTH) + l_chunk_offset); // TODO: we don't have to parse the full header set; // just test the seq_rec entry for its timestamp p_sys->i_stuff_cnt = 0; get_chunk_header(p_demux); // check ty PTS for the SEQ entry in this chunk if (p_sys->i_seq_rec < 0 || p_sys->i_seq_rec > p_sys->i_num_recs) { msg_Err(p_demux, "no SEQ hdr in chunk; table had one."); /* Seek to beginning of original chunk & reload it */ stream_Seek(p_demux->s, (l_cur_pos / CHUNK_SIZE) * CHUNK_SIZE); p_sys->i_stuff_cnt = 0; get_chunk_header(p_demux); return VLC_EGENERIC; } l_seek_secs = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts / 1000000000; msg_Dbg(p_demux, "found SEQ hdr for timestamp %02ld:%02ld:%02ld", l_seek_secs / 3600, (l_seek_secs / 60) % 60, l_seek_secs % 60); if (p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts >= l_seek_time) { // keep this one? go back? /* for now, we take this one. it's the first SEQ hdr AFTER the time we were searching for. */ msg_Dbg(p_demux, "seek target found."); break; } msg_Dbg(p_demux, "timestamp too early. still scanning."); } } /* if we made it through this entire loop without finding our target, then we skip to the next section. What has happened is that the actual time we're seeking is within this section, but there isn't a SEQ hdr for it here. So we skip to the next closest one (l_fwd_stamp) */ if (i == p_sys->i_bits_per_seq_entry) return ty_stream_seek_time(p_demux, l_fwd_stamp); /* current stream ptr is at beginning of data for this chunk, so we need to skip past any stream data prior to the seq_rec in this chunk */ i_skip_cnt = 0; for (i=0; i<p_sys->i_seq_rec; i++) i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size; stream_Read(p_demux->s, NULL, i_skip_cnt); p_sys->i_cur_rec = p_sys->i_seq_rec; //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts; //p_sys->l_last_ty_pts_sync = p_sys->lastAudioPTS; return VLC_SUCCESS;}/* parse a master chunk, filling the SEQ table and other variables. * We assume the stream is currently pointing to it. */static void parse_master(demux_t *p_demux){ demux_sys_t *p_sys = p_demux->p_sys; uint8_t mst_buf[32]; int i, i_map_size; int64_t i_save_pos = stream_Tell(p_demux->s); int64_t i_pts_secs; /* Note that the entries in the SEQ table in the stream may have different sizes depending on the bits per entry. We store them all in the same size structure, so we have to parse them out one by one. If we had a dynamic structure, we could simply read the entire table directly from the stream into memory in place. */ /* clear the SEQ table */ free(p_sys->seq_table); /* parse header info */ stream_Read(p_demux->s, mst_buf, 32); i_map_size = U32_AT(&mst_buf[20]); /* size of bitmask, in bytes */ p_sys->i_bits_per_seq_entry = i_map_size * 8; i = U32_AT(&mst_buf[28]); /* size of SEQ table, in bytes */ p_sys->i_seq_table_size = i / (8 + i_map_size); /* parse all the entries */ p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t)); for (i=0; i<p_sys->i_seq_table_size; i++) { stream_Read(p_demux->s, mst_buf, 8 + i_map_size); p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]); if (i_map_size > 8) { msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk"); memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0); } else { memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size); } } /* set up a few of our variables */ p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp; p_sys->l_final_ty_pts = p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp; p_sys->b_have_master = true; i_pts_secs = p_sys->l_first_ty_pts / 1000000000; msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d", (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) ); i_pts_secs = p_sys->l_final_ty_pts / 1000000000; msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d", (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) ); /* seek past this chunk */ stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE);}/* ======================================================================== *//* "Peek" at some chunks. Skip over the Part header if we find it. * We parse the peeked data and determine audio type, * SA vs. DTivo, & Tivo Series. * Set global vars i_Pes_Length, i_Pts_Offset, * p_sys->tivo_series, p_sys->tivo_type, p_sys->audio_type */static int probe_stream(demux_t *p_demux){ demux_sys_t *p_sys = p_demux->p_sys; const uint8_t *p_buf; int i; bool b_probe_error = false; /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */ if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) < CHUNK_PEEK_COUNT * CHUNK_SIZE) { msg_Err(p_demux, "Can't peek %d chunks", CHUNK_PEEK_COUNT); /* TODO: if seekable, then loop reading chunks into a temp buffer */ return VLC_EGENERIC; } /* the real work: analyze this chunk */ for (i = 0; i < CHUNK_PEEK_COUNT; i++) { analyze_chunk(p_demux, p_buf); if (p_sys->tivo_series != TIVO_SERIES_UNKNOWN && p_sys->audio_type != TIVO_AUDIO_UNKNOWN && p_sys->tivo_type != TIVO_TYPE_UNKNOWN) break; p_buf += CHUNK_SIZE; } /* the final tally */ if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Series."); b_probe_error = true; } if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Audio Type."); b_probe_error = true; } if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo)."); b_probe_error = true; } return b_probe_error?VLC_EGENERIC:VLC_SUCCESS;}/* ======================================================================== *//* gather statistics for this chunk & set our tivo-type vars accordingly */static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk){ demux_sys_t *p_sys = p_demux->p_sys; int i_num_recs, i; ty_rec_hdr_t *p_hdrs; int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0; int i_payload_size; /* skip if it's a Part header */ if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID ) return; /* number of records in chunk (we ignore high order byte; * rarely are there > 256 chunks & we don't need that many anyway) */ i_num_recs = p_chunk[0]; if (i_num_recs < 5) { /* try again with the next chunk. Sometimes there are dead ones */ return; } p_chunk += 4; /* skip past rec count & SEQ bytes */ //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs); p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size); /* scan headers. * 1. check video packets. Presence of 0x6e0 means S1. * No 6e0 but have be0 means S2. * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg) * If AC-3, then we have DTivo. * If MPEG, search for PTS offset. This will determine SA vs. DTivo. */ i_num_6e0 = i_num_be0 = i_num_9c0 = i_num_3c0 = 0; for (i=0; i<i_num_recs; i++) { //msg_Dbg(p_demux, "probe: rec is %d/%d = 0x%04x", p_hdrs[i].subrec_type, //p_hdrs[i].rec_type, //p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type); switch (p_hdrs[i].subrec_type << 8 | p_hdrs[i].rec_type) { case 0x6e0: i_num_6e0++; break; case 0xbe0: i_num_be0++; break; case 0x3c0: i_num_3c0++; break; case 0x9c0: i_num_9c0++; break; } } msg_Dbg(p_demux, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.", i_num_6e0, i_num_be0); /* set up our variables */ if (i_num_6e0 > 0) { msg_Dbg(p_demux, "detected Series 1 Tivo"); p_sys->tivo_series = TIVO_SERIES1; p_sys->i_Pes_Length = SERIES1_PES_LENGTH; } else if (i_num_be0 > 0) { msg_Dbg(p_demux, "detected Series 2 Tivo"); p_sys->tivo_series = TIVO_SERIES2; p_sys->i_Pes_Length = SERIES2_PES_LENGTH; } if (i_num_9c0 > 0) { msg_Dbg(p_demux, "detected AC-3 Audio (DTivo)" ); p_sys->audio_type = TIVO_AUDIO_AC3; p_sys->tivo_type = TIVO_TYPE_DTIVO; p_sys->i_Pts_Offset = AC3_PTS_OFFSET; p_sys->i_Pes_Length = AC3_PES_LENGTH; } else if (i_num_3c0 > 0) { p_sys->audio_type = TIVO_AUDIO_MPEG; msg_Dbg(p_demux, "detected MPEG Audio" ); } /* if tivo_type still unknown, we can check PTS location * in MPEG packets to determine tivo_type */ if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) { uint32_t i_data_offset = (16 * i_num
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -