#include "funcprotos.h" #include "quicktime.h" #include // Update during close: // length // samples per chunk #define JUNK_SIZE 0x1018 quicktime_strl_t* quicktime_new_strl() { quicktime_strl_t *strl = calloc(1, sizeof(quicktime_strl_t)); return strl; } void quicktime_init_strl(quicktime_t *file, quicktime_audio_map_t *atrack, quicktime_video_map_t *vtrack, quicktime_trak_t *trak, quicktime_strl_t *strl) { quicktime_atom_t list_atom, strh_atom, strf_atom; quicktime_atom_t junk_atom; int i; /* Construct tag */ if(vtrack) { strl->tag[0] = '0' + (trak->tkhd.track_id - 1) / 10; strl->tag[1] = '0' + (trak->tkhd.track_id - 1) % 10; strl->tag[2] = 'd'; strl->tag[3] = 'c'; } else if(atrack) { strl->tag[0] = '0' + (trak->tkhd.track_id - 1) / 10; strl->tag[1] = '0' + (trak->tkhd.track_id - 1) % 10; strl->tag[2] = 'w'; strl->tag[3] = 'b'; } /* LIST 'strl' */ quicktime_atom_write_header(file, &list_atom, "LIST"); quicktime_write_char32(file, "strl"); /* 'strh' */ quicktime_atom_write_header(file, &strh_atom, "strh"); /* vids */ if(vtrack) { quicktime_write_char32(file, "vids"); quicktime_write_char32(file, trak->mdia.minf.stbl.stsd.table[0].format); /* flags */ quicktime_write_int32_le(file, 0); /* priority */ quicktime_write_int16_le(file, 0); /* language */ quicktime_write_int16_le(file, 0); /* initial frame */ quicktime_write_int32_le(file, 0); /* framerate denominator */ quicktime_write_int32_le(file, trak->mdia.minf.stbl.stts.table[0].sample_duration); /* framerate numerator */ quicktime_write_int32_le(file, trak->mdia.mdhd.time_scale); /* start */ quicktime_write_int32_le(file, 0); strl->length_offset = quicktime_position(file); /* length: fill later */ quicktime_write_int32_le(file, 0); /* suggested buffer size */ quicktime_write_int32_le(file, 0); /* quality */ quicktime_write_int32_le(file, -1); /* sample size */ quicktime_write_int32_le(file, 0); quicktime_write_int16_le(file, 0); quicktime_write_int16_le(file, 0); quicktime_write_int16_le(file, trak->tkhd.track_width); quicktime_write_int16_le(file, trak->tkhd.track_height); } else /* auds */ if(atrack) { quicktime_write_char32(file, "auds"); quicktime_write_int32_le(file, 0); /* flags */ quicktime_write_int32_le(file, 0); /* priority */ quicktime_write_int16_le(file, 0); /* language */ quicktime_write_int16_le(file, 0); /* initial frame */ quicktime_write_int32_le(file, 0); strl->samples_per_chunk_offset = quicktime_position(file); /* samples per chunk */ quicktime_write_int32_le(file, 0); /* sample rate * samples per chunk if uncompressed */ /* sample rate if compressed */ quicktime_write_int32_le(file, 0); /* start */ quicktime_write_int32_le(file, 0); strl->length_offset = quicktime_position(file); /* length, XXX: filled later */ quicktime_write_int32_le(file, 0); /* suggested buffer size */ quicktime_write_int32_le(file, 0); /* quality */ quicktime_write_int32_le(file, -1); /* sample size: 0 for compressed and number of bytes for uncompressed */ strl->sample_size_offset = quicktime_position(file); quicktime_write_int32_le(file, 0); quicktime_write_int32_le(file, 0); quicktime_write_int32_le(file, 0); } quicktime_atom_write_footer(file, &strh_atom); /* strf */ quicktime_atom_write_header(file, &strf_atom, "strf"); if(vtrack) { /* atom size repeated */ quicktime_write_int32_le(file, 40); quicktime_write_int32_le(file, trak->tkhd.track_width); quicktime_write_int32_le(file, trak->tkhd.track_height); /* planes */ quicktime_write_int16_le(file, 1); /* depth */ quicktime_write_int16_le(file, 24); quicktime_write_char32(file, trak->mdia.minf.stbl.stsd.table[0].format); quicktime_write_int32_le(file, trak->tkhd.track_width * trak->tkhd.track_height * 3); quicktime_write_int32_le(file, 0); quicktime_write_int32_le(file, 0); quicktime_write_int32_le(file, 0); quicktime_write_int32_le(file, 0); } else if(atrack) { /* By now the codec is instantiated so the WAV ID is available. */ quicktime_codec_t *codec_base = atrack->codec; int wav_id = codec_base->wav_id; quicktime_write_int16_le(file, wav_id); quicktime_write_int16_le(file, trak->mdia.minf.stbl.stsd.table[0].channels); /* nSamplesPerSec as per MSDN */ quicktime_write_int32_le(file, trak->mdia.minf.stbl.stsd.table[0].sample_rate); if (wav_id == 0x01) // PCM { /* nAvgBytesPerSec as per MSDN*/ quicktime_write_int32_le(file, trak->mdia.minf.stbl.stsd.table[0].sample_rate * trak->mdia.minf.stbl.stsd.table[0].sample_size * trak->mdia.minf.stbl.stsd.table[0].channels / 8); /* nBlockAlign as per MSDN, very important value */ /* nBlockAlign have to be the same is used in many players instead of dwSampleSize - so they should be the same */ /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcemultimedia5/html/wce50conAVIStreamHeaders.asp */ /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcemultimedia5/html/wce50lrfwaveformatex91.asp */ quicktime_write_int16_le(file, trak->mdia.minf.stbl.stsd.table[0].sample_size * trak->mdia.minf.stbl.stsd.table[0].channels / 8); } else { /* FIXME: These two are complete rubbish, according to my knowledge, they depend on the codec */ quicktime_write_int32_le(file, 256000 / 8); // nAvgBytesPerSec quicktime_write_int16_le(file, 1); // nBlockAling } /* bits per sample */ quicktime_write_int16_le(file, trak->mdia.minf.stbl.stsd.table[0].sample_size); quicktime_write_int16_le(file, 0); } quicktime_atom_write_footer(file, &strf_atom); /* Junk is required in Windows. */ /* In Heroine Kernel it's padding for the super index */ strl->indx_offset = quicktime_position(file); strl->padding_size = JUNK_SIZE; quicktime_atom_write_header(file, &junk_atom, "JUNK"); for(i = 0; i < strl->padding_size; i += 4) quicktime_write_int32_le(file, 0); quicktime_atom_write_footer(file, &junk_atom); /* Initialize super index */ quicktime_init_indx(file, &strl->indx, strl); quicktime_atom_write_footer(file, &list_atom); } void quicktime_delete_strl(quicktime_strl_t *strl) { quicktime_delete_indx(&strl->indx); free(strl); } void quicktime_read_strl(quicktime_t *file, quicktime_strl_t *strl, quicktime_atom_t *parent_atom) { // These are 0 if no track is currently being processed. // Set to 1 if audio or video track is being processed. char data[4], codec[4]; int denominator = 0; int numerator = 0; double frame_rate = 0; int width = 0; int height = 0; int depth = 0; int frames = 0; int bytes_per_sample = 0; int sample_size = 0; int samples_per_chunk = 0; int channels = 0; int sample_rate = 0; int compression_id = 0; quicktime_trak_t *trak = 0; codec[0] = codec[1] = codec[2] = codec[3] = 0; /* AVI translation: */ /* vids -> trak */ /* auds -> trak */ /* Only one track is in each strl object */ do { quicktime_atom_t leaf_atom; quicktime_atom_read_header(file, &leaf_atom); // strh if(quicktime_atom_is(&leaf_atom, "strh")) { // stream type quicktime_read_data(file, data, 4); if(quicktime_match_32(data, "vids")) { trak = quicktime_add_trak(file); width = 0; height = 0; depth = 24; frames = 0; strl->is_video = 1; trak->tkhd.track_id = file->moov.mvhd.next_track_id; file->moov.mvhd.next_track_id++; /* Codec */ quicktime_read_data(file, codec, 4); /* Blank */ quicktime_set_position(file, quicktime_position(file) + 12); denominator = quicktime_read_int32_le(file); numerator = quicktime_read_int32_le(file); /* * printf("quicktime_read_strl 1 %c%c%c%c %d %d\n", * codec[0], * codec[1], * codec[2], * codec[3], * numerator, * denominator); */ if(denominator != 0) frame_rate = (double)numerator / denominator; else frame_rate = numerator; // Canon TX1 if(numerator == 1000000 && denominator == 33333) frame_rate = 30.00; /* Blank */ quicktime_set_position(file, quicktime_position(file) + 4); frames = quicktime_read_int32_le(file); } else /* AVI auds */ if(quicktime_match_32(data, "auds")) { trak = quicktime_add_trak(file); sample_size = 16; channels = 2; sample_rate = 0; compression_id = 0; strl->is_audio = 1; trak->tkhd.track_id = file->moov.mvhd.next_track_id; file->moov.mvhd.next_track_id++; // codec tag quicktime_read_data(file, codec, 4); //printf("quicktime_read_strl 2 %c%c%c%c\n", codec[0], codec[1], codec[2], codec[3]); // flags 32, priority 16, language 16, initial frame 32 quicktime_set_position(file, quicktime_position(file) + 12); samples_per_chunk = quicktime_read_int32_le(file); strl->bytes_per_second = quicktime_read_int32_le(file); quicktime_set_position(file, quicktime_position(file) + 4); /* length of track */ strl->samples = quicktime_read_int32_le(file); /* suggested buffer size, quality */ quicktime_set_position(file, quicktime_position(file) + 8); // If this is 0 use constant samples_per_chunk to guess locations. // If it isn't 0 synthesize samples per chunk table to get locations. // McRowesoft doesn't really obey this rule so we may need to base it on codec ID. bytes_per_sample = quicktime_read_int32_le(file); //printf("quicktime_read_strl 20 %d\n", samples_per_chunk); } } // strf else if(quicktime_atom_is(&leaf_atom, "strf")) { if(strl->is_video) { /* atom size repeated */ quicktime_read_int32_le(file); width = quicktime_read_int32_le(file); height = quicktime_read_int32_le(file); /* Panes */ quicktime_read_int16_le(file); /* Depth in bits */ depth = quicktime_read_int16_le(file); quicktime_read_data(file, codec, 4); } else if(strl->is_audio) { compression_id = quicktime_read_int16_le(file); channels = quicktime_read_int16_le(file); sample_rate = quicktime_read_int32_le(file); quicktime_set_position(file, quicktime_position(file) + 6); sample_size = quicktime_read_int16_le(file); //printf("quicktime_read_strl 40 %d %d %d\n", channels, sample_rate, sample_size); } } else // Super index. // Read the super index + all the partial indexes now if(quicktime_atom_is(&leaf_atom, "indx")) { //printf("quicktime_read_strl 50\n"); quicktime_read_indx(file, strl, &leaf_atom); strl->have_indx = 1; } //printf("quicktime_read_strl 60\n"); // Next object quicktime_atom_skip(file, &leaf_atom); }while(quicktime_position(file) < parent_atom->end); //printf("quicktime_read_strl 70 %d %d\n", strl->is_audio, strl->is_video); if(strl->is_video) { /* Generate quicktime structures */ quicktime_trak_init_video(file, trak, width, height, frame_rate, codec); quicktime_mhvd_init_video(file, &file->moov.mvhd, frame_rate); trak->mdia.mdhd.duration = frames; // trak->mdia.mdhd.time_scale = 1; memcpy(trak->mdia.minf.stbl.stsd.table[0].format, codec, 4); trak->mdia.minf.stbl.stsd.table[0].depth = depth; } else if(strl->is_audio) { /* Generate quicktime structures */ //printf("quicktime_read_strl 70 %d\n", sample_size); quicktime_trak_init_audio(file, trak, channels, sample_rate, sample_size, codec); // We store a constant samples per chunk based on the // packet size if sample_size zero // and calculate the samples per chunk based on the chunk size if sample_size // is nonzero. // trak->mdia.minf.stbl.stsd.table[0].sample_size = bytes_per_sample; trak->mdia.minf.stbl.stsd.table[0].compression_id = compression_id; /* Synthesize stsc table for constant samples per chunk */ if(!bytes_per_sample) { /* Should be enough entries allocated in quicktime_stsc_init_table */ trak->mdia.minf.stbl.stsc.table[0].samples = samples_per_chunk; trak->mdia.minf.stbl.stsc.total_entries = 1; } //printf("quicktime_read_strl samples=%d samples_per_chunk=%d bytes_per_sample=%d bytes_per_second=%d\n", //strl->samples, samples_per_chunk, bytes_per_sample, strl->bytes_per_second); } //printf("quicktime_read_strl 100\n"); }