# HG changeset patch # User melanson # Date 1079237291 0 # Node ID f2760852ed1856d38d9d33c44113a63608a0dd03 # Parent c152849ee64300bd6adf3ad358e21333ae66453e minor VMD system update; still not perfect, but should not crash either diff -r c152849ee643 -r f2760852ed18 sierravmd.c --- a/sierravmd.c Sun Mar 14 04:04:08 2004 +0000 +++ b/sierravmd.c Sun Mar 14 04:08:11 2004 +0000 @@ -21,16 +21,12 @@ * @file sierravmd.c * Sierra VMD file demuxer * by Vladimir "VAG" Gneushev (vagsoft at mail.ru) + * for more information on the Sierra VMD file format, visit: + * http://www.pcisys.net/~melanson/codecs/ */ #include "avformat.h" -#define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0]) -#define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ - (((uint8_t*)(x))[2] << 16) | \ - (((uint8_t*)(x))[1] << 8) | \ - ((uint8_t*)(x))[0]) - #define VMD_HEADER_SIZE 0x0330 #define BYTES_PER_FRAME_RECORD 16 @@ -56,6 +52,11 @@ vmd_frame_t *frame_table; unsigned int current_frame; + int sample_rate; + int64_t audio_sample_counter; + int audio_frame_divisor; + int audio_block_align; + unsigned char vmd_header[VMD_HEADER_SIZE]; } VmdDemuxContext; @@ -73,6 +74,32 @@ return AVPROBE_SCORE_MAX / 2; } +/* This is a support function to determine the duration, in sample + * frames, of a particular audio chunk, taking into account silent + * encodings. */ +static int vmd_calculate_audio_duration(unsigned char *audio_chunk, + int audio_chunk_size, int block_align) +{ + unsigned char *p = audio_chunk + 16; + unsigned char *p_end = audio_chunk + audio_chunk_size; + int total_samples = 0; + unsigned int sound_flags; + + if (audio_chunk_size < 16) + return 0; + + sound_flags = LE_32(p); + p += 4; + while (p < p_end) { + total_samples += block_align; + if ((sound_flags & 0x01) == 0) + p += block_align; + sound_flags >>= 1; + } + + return total_samples; +} + static int vmd_read_header(AVFormatContext *s, AVFormatParameters *ap) { @@ -85,15 +112,19 @@ unsigned char *current_frame_record; offset_t current_offset; int i; - int sample_rate; unsigned int total_frames; -int video_frame_count = 0; + int64_t video_pts_inc; + int64_t current_video_pts = 0; /* fetch the main header, including the 2 header length bytes */ url_fseek(pb, 0, SEEK_SET); if (get_buffer(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE) return -EIO; + vmd->audio_sample_counter = 0; + vmd->audio_frame_divisor = 1; + vmd->audio_block_align = 1; + /* start up the decoders */ st = av_new_stream(s, 0); if (!st) @@ -109,8 +140,8 @@ memcpy(st->codec.extradata, vmd->vmd_header, VMD_HEADER_SIZE); /* if sample rate is 0, assume no audio */ - sample_rate = LE_16(&vmd->vmd_header[804]); - if (sample_rate) { + vmd->sample_rate = LE_16(&vmd->vmd_header[804]); + if (vmd->sample_rate) { st = av_new_stream(s, 0); if (!st) return AVERROR_NOMEM; @@ -119,15 +150,28 @@ st->codec.codec_id = CODEC_ID_VMDAUDIO; st->codec.codec_tag = 0; /* no codec tag */ st->codec.channels = (vmd->vmd_header[811] & 0x80) ? 2 : 1; - st->codec.sample_rate = sample_rate; - st->codec.bit_rate = st->codec.sample_rate * - st->codec.bits_per_sample * st->codec.channels; - st->codec.block_align = LE_16(&vmd->vmd_header[806]); + st->codec.sample_rate = vmd->sample_rate; + st->codec.block_align = vmd->audio_block_align = + LE_16(&vmd->vmd_header[806]); if (st->codec.block_align & 0x8000) { st->codec.bits_per_sample = 16; st->codec.block_align = -(st->codec.block_align - 0x10000); } else - st->codec.bits_per_sample = 8; + st->codec.bits_per_sample = 16; +// st->codec.bits_per_sample = 8; + st->codec.bit_rate = st->codec.sample_rate * + st->codec.bits_per_sample * st->codec.channels; + + /* for calculating pts */ + vmd->audio_frame_divisor = st->codec.bits_per_sample / 8 / + st->codec.channels; + + video_pts_inc = 90000; + video_pts_inc *= st->codec.block_align; + video_pts_inc /= st->codec.sample_rate; + } else { + /* if no audio, assume 10 frames/second */ + video_pts_inc = 90000 / 10; } /* skip over the offset table and load the table of contents; don't @@ -184,12 +228,14 @@ memcpy(vmd->frame_table[i].frame_record, current_frame_record, BYTES_PER_FRAME_RECORD); -if (current_frame_record[0] == 0x02) { - /* assume 15 fps for now */ - vmd->frame_table[i].pts = video_frame_count++; - vmd->frame_table[i].pts *= 90000; - vmd->frame_table[i].pts /= 15; -} + /* figure out the pts for this frame */ + if (current_frame_record[0] == 0x02) { + vmd->frame_table[i].pts = current_video_pts; + current_video_pts += video_pts_inc; + } else if (current_frame_record[0] == 0x01) { + /* figure out the pts during the dispatch phase */ + vmd->frame_table[i].pts = 0; + } current_frame_record += BYTES_PER_FRAME_RECORD; i++; @@ -227,10 +273,26 @@ ret = get_buffer(pb, pkt->data + BYTES_PER_FRAME_RECORD, frame->frame_size); - if (ret != frame->frame_size) + if (ret != frame->frame_size) { + av_free_packet(pkt); ret = -EIO; + } pkt->stream_index = frame->stream_index; - pkt->pts = frame->pts; + if (frame->frame_record[0] == 0x02) + pkt->pts = frame->pts; + else { + pkt->pts = vmd->audio_sample_counter; + pkt->pts *= 90000; + pkt->pts /= vmd->sample_rate; +// pkt->pts /= vmd->audio_frame_divisor; + vmd->audio_sample_counter += vmd_calculate_audio_duration( + pkt->data, pkt->size, vmd->audio_block_align); + + } +printf (" dispatching %s frame with %d bytes and pts %lld (%0.1f sec)\n", + (frame->frame_record[0] == 0x02) ? "video" : "audio", + frame->frame_size + BYTES_PER_FRAME_RECORD, + pkt->pts, (float)(pkt->pts / 90000.0)); vmd->current_frame++;