void buffered_seek (struct ccx_demuxer *ctx, int offset) { position_sanity_check(ctx); if (offset < 0) { ctx->filebuffer_pos += offset; if (ctx->filebuffer_pos < 0) { // We got into the start buffer (hopefully) if ((ctx->filebuffer_pos + ctx->startbytes_pos) < 0) { fatal (CCX_COMMON_EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!"); } ctx->startbytes_pos += ctx->filebuffer_pos; ctx->filebuffer_pos = 0; } } else { buffered_read_opt (ctx, NULL, offset); position_sanity_check(ctx); } }
// read_header will read all the required information from // the wtv header/root sections and calculate the skip_chunks. // If successful, will return with the file positioned // at the start of the data dir int read_header(struct ccx_demuxer *ctx, struct wtv_chunked_buffer *cb) { ctx->startbytes_avail = (int)buffered_read_opt(ctx, ctx->startbytes, STARTBYTESLENGTH); return_to_buffer(ctx, ctx->startbytes, ctx->startbytes_avail); uint8_t *parsebuf; parsebuf = (uint8_t*)malloc(1024); buffered_read(ctx, parsebuf,0x42); ctx->past+=result; if (result!=0x42) { mprint("\nPremature end of file!\n"); return CCX_EOF; } // Expecting WTV header if( !memcmp(parsebuf, WTV_HEADER, 16 ) ) { dbg_print(CCX_DMT_PARSE, "\nWTV header\n"); } else { mprint("\nMissing WTV header. Abort.\n"); return CCX_EOF; } //Next read just enough to get the location of the root directory uint32_t filelen; uint32_t root_dir; memcpy(&filelen, parsebuf+0x30, 4); dbg_print(CCX_DMT_PARSE, "filelen: %x\n", filelen); memcpy(&root_dir, parsebuf+0x38, 4); dbg_print(CCX_DMT_PARSE, "root_dir: %x\n", root_dir); //Seek to start of the root dir. Typically 0x1100 buffered_skip(ctx,(root_dir*WTV_CHUNK_SIZE)-0x42); ctx->past+=(root_dir*WTV_CHUNK_SIZE)-0x42; if (result!=(root_dir*WTV_CHUNK_SIZE)-0x42) return CCX_EOF; // Read and calculate the meta data chunks in the file we need to skip over // while parsing the file. int end=0; while(!end) { buffered_read(ctx, parsebuf, 32); int x; for(x=0; x<16; x++) dbg_print(CCX_DMT_PARSE, "%02X ", parsebuf[x]); dbg_print(CCX_DMT_PARSE, "\n"); if (result!=32) { mprint("\nPremature end of file!\n"); free(parsebuf); return CCX_EOF; } ctx->past+=32; if( !memcmp(parsebuf, WTV_EOF, 16 )) { dbg_print(CCX_DMT_PARSE, "WTV EOF\n"); end=1; } else { uint16_t len; uint64_t file_length; memcpy(&len, parsebuf+16, 2); dbg_print(CCX_DMT_PARSE, "len: %x\n", len); memcpy(&file_length, parsebuf+24, 8); dbg_print(CCX_DMT_PARSE, "file_length: %x\n", file_length); if(len>1024) { mprint("Too large for buffer!\n"); free(parsebuf); return CCX_EOF; } buffered_read(ctx, parsebuf, len-32); if (result!=len-32) { mprint("Premature end of file!\n"); free(parsebuf); return CCX_EOF; } ctx->past+=len-32; // Read a unicode string uint32_t text_len; memcpy(&text_len, parsebuf, 4); //text_len is number of unicode chars, not bytes. dbg_print(CCX_DMT_PARSE, "text_len: %x\n", text_len); char *string; string = (char*)malloc(text_len+1); // alloc for ascii string[text_len]='\0'; for(x=0; x<text_len; x++) { memcpy(&string[x], parsebuf+8+(x*2), 1); // unicode converted to ascii } dbg_print(CCX_DMT_PARSE, "string: %s\n", string); // Ignore everything that doesn't contain the text ".entries." if(strstr(string, WTV_TABLE_ENTRIES)!=NULL) { uint32_t value; uint32_t flag; memcpy(&value, parsebuf+(text_len*2)+8, 4); memcpy(&flag, parsebuf+(text_len*2)+4+8, 4); dbg_print(CCX_DMT_PARSE, "value: %x\n", value); dbg_print(CCX_DMT_PARSE, "flag: %x\n", flag); if(!add_skip_chunks(ctx, cb, value, flag)) { mprint("Premature end of file!\n"); free(parsebuf); return CCX_EOF; } } free(string); } } // Our list of skip_chunks is now complete // Sort it (not sure if this is needed, but it doesn't hurt). qsort(cb->skip_chunks, cb->count, sizeof(uint64_t), qsort_cmpint); dbg_print(CCX_DMT_PARSE, "skip_chunks: "); int x; for(x=0; x<cb->count; x++) dbg_print(CCX_DMT_PARSE, "%llx, ", cb->skip_chunks[x]); dbg_print(CCX_DMT_PARSE, "\n"); // Seek forward to the start of the data dir // Typically 0x40000 buffered_skip(ctx,(int)((cb->skip_chunks[cb->chunk]+WTV_META_CHUNK_SIZE)-ctx->past)); cb->filepos=(cb->skip_chunks[cb->chunk]+WTV_META_CHUNK_SIZE); cb->chunk++; ctx->past=cb->filepos; free(parsebuf); return CCX_OK; }
void detect_stream_type (struct ccx_demuxer *ctx) { ctx->stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND; // Not found ctx->startbytes_avail = (int) buffered_read_opt(ctx, ctx->startbytes, STARTBYTESLENGTH); if( ctx->startbytes_avail == -1) fatal (EXIT_READ_ERROR, "Error reading input file!\n"); if (ctx->startbytes_avail>=4) { // Check for ASF magic bytes if (ctx->startbytes[0]==0x30 && ctx->startbytes[1]==0x26 && ctx->startbytes[2]==0xb2 && ctx->startbytes[3]==0x75) ctx->stream_mode=CCX_SM_ASF; } if (ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail >= 4) { if(ctx->startbytes[0]==0xb7 && ctx->startbytes[1]==0xd8 && ctx->startbytes[2]==0x00 && ctx->startbytes[3]==0x20) ctx->stream_mode = CCX_SM_WTV; } #ifdef WTV_DEBUG if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail>=6) { // Check for hexadecimal dump generated by wtvccdump // ; CCHD if (ctx->startbytes[0]==';' && ctx->startbytes[1]==' ' && ctx->startbytes[2]=='C' && ctx->startbytes[3]=='C' && ctx->startbytes[4]=='H' && ctx->startbytes[5]=='D') ctx->stream_mode= CCX_SM_HEX_DUMP; } #endif if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail>=11) { // Check for CCExtractor magic bytes if (ctx->startbytes[0]==0xCC && ctx->startbytes[1]==0xCC && ctx->startbytes[2]==0xED && ctx->startbytes[8]==0 && ctx->startbytes[9]==0 && ctx->startbytes[10]==0) ctx->stream_mode=CCX_SM_RCWT; } if ((ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND || ccx_options.print_file_reports) && ctx->startbytes_avail >= 4) // Still not found { long idx = 0, nextBoxLocation = 0, lastBoxLocation = 0; int boxScore = 0; // Scan the buffer for valid succeeding MP4 boxes. while (idx < ctx->startbytes_avail - 7) { lastBoxLocation = idx; // Check if we have a valid box if (isValidMP4Box(ctx->startbytes, idx, &nextBoxLocation, &boxScore)) { idx = nextBoxLocation; // If the box is valid, a new box should be found on the next location... Not somewhere in between. if (boxScore > 7) { break; } continue; } else { // Not a valid box, reset score. We need a couple of successive boxes to identify a MP4 file. boxScore = 0; idx++; } } if (boxScore > 1) { // We had at least one box (or multiple) at the end to "claim" this is MP4. A single valid box at the end is doubtful... ctx->stream_mode = CCX_SM_MP4; } } if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND) // Still not found { if (ctx->startbytes_avail > 188*8) // Otherwise, assume no TS { // First check for TS for (unsigned i=0; i<188; i++) { if (ctx->startbytes[i]==0x47 && ctx->startbytes[i+188]==0x47 && ctx->startbytes[i+188*2]==0x47 && ctx->startbytes[i+188*3]==0x47 && ctx->startbytes[i+188*4]==0x47 && ctx->startbytes[i+188*5]==0x47 && ctx->startbytes[i+188*6]==0x47 && ctx->startbytes[i+188*7]==0x47 ) { // Eight sync bytes, that's good enough ctx->startbytes_pos=i; ctx->stream_mode=CCX_SM_TRANSPORT; ctx->m2ts = 0; break; } } if (ctx->stream_mode == CCX_SM_TRANSPORT) { dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as TS\n"); return_to_buffer (ctx, ctx->startbytes, (unsigned int)ctx->startbytes_avail); return; } // Check for M2TS for (unsigned i = 0; i<192; i++) { if (ctx->startbytes[i+4] == 0x47 && ctx->startbytes[i + 4 + 192] == 0x47 && ctx->startbytes[i + 192 * 2+4] == 0x47 && ctx->startbytes[i + 192 * 3+4] == 0x47 && ctx->startbytes[i + 192 * 4+4] == 0x47 && ctx->startbytes[i + 192 * 5+4] == 0x47 && ctx->startbytes[i + 192 * 6+4] == 0x47 && ctx->startbytes[i + 192 * 7+4] == 0x47 ) { // Eight sync bytes, that's good enough ctx->startbytes_pos = i; ctx->stream_mode = CCX_SM_TRANSPORT; ctx->m2ts = 1; break; } } if (ctx->stream_mode == CCX_SM_TRANSPORT) { dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as M2TS\n"); return_to_buffer (ctx, ctx->startbytes, (unsigned int)ctx->startbytes_avail); return; } // Now check for PS (Needs PACK header) for (unsigned i=0; i < (unsigned) (ctx->startbytes_avail<50000?ctx->startbytes_avail-3:49997); i++) { if (ctx->startbytes[i]==0x00 && ctx->startbytes[i+1]==0x00 && ctx->startbytes[i+2]==0x01 && ctx->startbytes[i+3]==0xBA) { // If we find a PACK header it is not an ES ctx->startbytes_pos=i; ctx->stream_mode=CCX_SM_PROGRAM; break; } } if (ctx->stream_mode == CCX_SM_PROGRAM) { dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as PS\n"); } // TiVo is also a PS if (ctx->startbytes[0]=='T' && ctx->startbytes[1]=='i' && ctx->startbytes[2]=='V' && ctx->startbytes[3]=='o') { // The TiVo header is longer, but the PS loop will find the beginning dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as Tivo PS\n"); ctx->startbytes_pos=187; ctx->stream_mode=CCX_SM_PROGRAM; strangeheader=1; // Avoid message about unrecognized header } } else { ctx->startbytes_pos=0; ctx->stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND; } } // Don't use STARTBYTESLENGTH. It might be longer than the file length! return_to_buffer (ctx, ctx->startbytes, ctx->startbytes_avail); }