From ec459ccb9a7eadee41afd4da2b71a52e18dceae6 Mon Sep 17 00:00:00 2001 From: Tomasz Jezierski Date: Thu, 6 Sep 2012 20:14:27 +0200 Subject: [PATCH] Vividas support for IPLEX for current ffmpeg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ported to current ffmpeg version Copyright (c) 2010 Andrzej Szombierski based on vivparse Copyright (c) 2007 Måns Rullgård --- lib/ffmpeg/libavformat/Makefile | 1 + lib/ffmpeg/libavformat/allformats.c | 1 + lib/ffmpeg/libavformat/vividas.c | 740 ++++++++++++++++++++++++ lib/ffmpeg/patches/0110-vividas-support.patch | 770 +++++++++++++++++++++++++ 4 files changed, 1512 insertions(+) create mode 100644 lib/ffmpeg/libavformat/vividas.c create mode 100644 lib/ffmpeg/patches/0110-vividas-support.patch diff --git a/lib/ffmpeg/libavformat/Makefile b/lib/ffmpeg/libavformat/Makefile index bd2b17b..6642498 100644 --- a/lib/ffmpeg/libavformat/Makefile +++ b/lib/ffmpeg/libavformat/Makefile @@ -315,6 +315,7 @@ OBJS-$(CONFIG_TXD_DEMUXER) += txd.o OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o OBJS-$(CONFIG_VC1T_DEMUXER) += vc1test.o OBJS-$(CONFIG_VC1T_MUXER) += vc1testenc.o +OBJS-$(CONFIG_VIVIDAS_DEMUXER) += vividas.o OBJS-$(CONFIG_VMD_DEMUXER) += sierravmd.o OBJS-$(CONFIG_VOC_DEMUXER) += vocdec.o voc.o OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o diff --git a/lib/ffmpeg/libavformat/allformats.c b/lib/ffmpeg/libavformat/allformats.c index 134839f..0af5ba9 100644 --- a/lib/ffmpeg/libavformat/allformats.c +++ b/lib/ffmpeg/libavformat/allformats.c @@ -232,6 +232,7 @@ void av_register_all(void) REGISTER_DEMUXER (TTY, tty); REGISTER_DEMUXER (VC1, vc1); REGISTER_MUXDEMUX (VC1T, vc1t); + REGISTER_DEMUXER (VIVIDAS, vividas); REGISTER_DEMUXER (VMD, vmd); REGISTER_MUXDEMUX (VOC, voc); REGISTER_DEMUXER (VQF, vqf); diff --git a/lib/ffmpeg/libavformat/vividas.c b/lib/ffmpeg/libavformat/vividas.c new file mode 100644 index 0000000..2d3ba27 --- /dev/null +++ b/lib/ffmpeg/libavformat/vividas.c @@ -0,0 +1,740 @@ +/* + * Vividas VIV format Demuxer + * Copyright (c) 2010 Andrzej Szombierski + * based on vivparse Copyright (c) 2007 Måns Rullgård + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @brief Vividas VIV (.viv) file demuxer + * @author Andrzej Szombierski [qq at kuku eu org] (2010-07) + * @sa http://wiki.multimedia.cx/index.php?title=Vividas_VIV + */ + +#define AV_PKT_FLAG_KEY 0x0001 + +#include "libavutil/intreadwrite.h" +#include "avio_internal.h" +#include "avformat.h" + +#define MAX_AUDIO_SUBPACKETS 100 +#define CODEC_TYPE_VIDEO AVMEDIA_TYPE_VIDEO +#define CODEC_TYPE_AUDIO AVMEDIA_TYPE_AUDIO + +typedef struct VIV_SB_block { + int size, n_packets; + int64_t byte_offset; + int packet_offset; +} VIV_SB_block; + +typedef struct VIV_SB_entry { + int size, flag; +} VIV_SB_entry; + +typedef struct VIV_AudioSubpacket { + int start, pcm_bytes; +} VIV_AudioSubpacket; + +typedef struct VIV_DemuxContext +{ + int n_sb_blocks; + VIV_SB_block *sb_blocks; + + uint32_t sb_key; + int64_t sb_offset; + + int current_sb, current_sb_entry; + uint8_t *sb_buf; + AVIOContext *sb_pb; + int n_sb_entries; + VIV_SB_entry *sb_entries; + + int n_audio_subpackets; + int current_audio_subpacket; + + int audio_sample; + + VIV_AudioSubpacket audio_subpackets[MAX_AUDIO_SUBPACKETS]; +} VIV_DemuxContext; + +static int viv_probe(AVProbeData *p) +{ + if(strncmp((char*)p->buf, "vividas03", 9)) + return 0; + + return AVPROBE_SCORE_MAX; +} + +const unsigned short keybits[32] = { + 163, 416, 893, 82, 223, 572, 1137, 430, + 659, 1104, 13, 626, 695, 972, 1465, 686, + 843, 1216, 317, 1122, 1383, 92, 513, 1158, + 1243, 48, 573, 1306, 1495, 396, 1009, 350, +}; + +static uint32_t decode_key(uint8_t *buf) +{ + uint32_t key = 0; + int i; + + for (i = 0; i < 32; i++) { + unsigned p = keybits[i]; + key |= !!(buf[p>>3] & (1<<(p&7))) << i; + } + + return key; +} + +static void put_v(uint8_t *p, int v) +{ + if(v>>28) + *p++ = ((v>>28)&0x7f)|0x80; + if(v>>21) + *p++ = ((v>>21)&0x7f)|0x80; + if(v>>14) + *p++ = ((v>>14)&0x7f)|0x80; + if(v>>7) + *p++ = ((v>>7)&0x7f)|0x80; + *p++ = v&0x7f; +} + +static unsigned int recover_key(unsigned char sample[4], int expected_size) +{ + unsigned char plaintext[8] = { 'S', 'B' }; + put_v(plaintext+2, expected_size); + + return (sample[0]^plaintext[0])| + ((sample[1]^plaintext[1])<<8)| + ((sample[2]^plaintext[2])<<16)| + ((sample[3]^plaintext[3])<<24); +} + +static void xor_block(void *p1, void *p2, int size, int key, int *key_ptr) +{ + int *d1 = p1; + int *d2 = p2; + int k = *key_ptr; + + size >>= 2; + + while (size--) { + *d2 = *d1 ^ k; + k += key; + d1++; + d2++; + } + + *key_ptr = k; +} + +static void decode_block(uint8_t *src, uint8_t *dest, int size, + uint32_t key, uint32_t *key_ptr, + int align) +{ + int s = size; + char tmp[4]; + int a2; + + if (!size) + return; + + align &= 3; + a2 = (4 - align) & 3; + + if (align) { + uint32_t tmpkey = *key_ptr - key; + memcpy(tmp + align, src, a2); + xor_block(tmp, tmp, 4, key, &tmpkey); + memcpy(dest, tmp + align, a2); + s -= a2; + } + + if (s >= 4) { + if (!align) + align = 4; + xor_block(src + a2, dest + a2, s & ~3, + key, key_ptr); + s &= 3; + } + + if (s) { + size -= s; + memcpy(tmp, src + size, s); + xor_block(&tmp, &tmp, 4, key, key_ptr); + memcpy(dest + size, tmp, s); + } +} + +static uint32_t get_v(uint8_t *p) +{ + uint32_t v = 0; + + do { + v <<= 7; + v += *p & 0x7f; + } while (*p++ & 0x80); + + return v; +} + +static uint8_t *read_vblock(AVIOContext *src, uint32_t *size, uint32_t key, uint32_t *k2, int align) +{ + uint8_t tmp[4]; + uint8_t *buf; + unsigned n; + + if(avio_read(src, tmp, 4) != 4) + return NULL; + + decode_block(tmp, tmp, 4, key, k2, align); + + n = get_v(tmp); + + buf = av_malloc(n); + if (!buf) + return NULL; + + *size = n; + n -= 4; + + memcpy(buf, tmp, 4); + + if (avio_read(src, buf + 4, n) == n) { + decode_block(buf + 4, buf + 4, n, key, k2, align + 4); + } else { + av_free(buf); + buf = NULL; + } + + return buf; +} + +static uint8_t *read_sb_block(AVIOContext *src, unsigned *size, uint32_t *key, int expected_size) +{ + uint8_t *buf; + uint8_t ibuf[8], sbuf[8]; + uint32_t k2; + int n; + + if (avio_read(src, ibuf, 8) < 8) + return NULL; + + k2 = *key; + decode_block(ibuf, sbuf, 8, *key, &k2, 0); + + n = get_v(sbuf+2); + + if (sbuf[0] != 'S' || sbuf[1] != 'B' || (expected_size>0 && n != expected_size)) { + uint32_t tmpkey = recover_key(ibuf, expected_size); + k2 = tmpkey; + decode_block(ibuf, sbuf, 8, tmpkey, &k2, 0); + n = get_v(sbuf+2); + if(sbuf[0] != 'S' || sbuf[1] != 'B' || expected_size != n) + return NULL; + *key = tmpkey; + } + + buf = av_malloc(n); + if (!buf) + return NULL; + + memcpy(buf, sbuf, 8); + + *size = n; + n -= 8; + + if (avio_read(src, buf+8, n) < n) { + av_free(buf); + return NULL; + } + + decode_block(buf + 8, buf + 8, n, *key, &k2, 0); + + return buf; +} + +static void track_header(VIV_DemuxContext *viv, AVFormatContext *s, uint8_t *buf, int size) +{ + int i,j; + int64_t off; + int val_1; + int num_video, num_audio; + AVIOContext *pb=0; + + pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); + //ff_get_v(pb); // track_header_len + ffio_read_varlen(pb); // track_header_len + avio_r8(pb); // '1' + + //val_1 = ff_get_v(pb); + val_1 = ffio_read_varlen(pb); + + for(i=0;icodec; + + st->id = i; + + vcodec->codec_type = CODEC_TYPE_VIDEO; + vcodec->codec_id = CODEC_ID_VP6; + + off = avio_tell(pb); + //off += ff_get_v(pb); + off += ffio_read_varlen(pb); + avio_r8(pb); // '3' + avio_r8(pb); // val_7 + st->time_base.num = avio_rl32(pb); // frame_time + st->time_base.den = avio_rl32(pb); // time_base + st->nb_frames = avio_rl32(pb); // n frames + vcodec->width = avio_rl16(pb); // width + vcodec->height = avio_rl16(pb); // height + avio_r8(pb); // val_8 + avio_rl32(pb); // val_9 + + vcodec->flags |= CODEC_FLAG_GLOBAL_HEADER; // ? + + avio_seek(pb, off, SEEK_SET); + } + + off = avio_tell(pb); + //off += ff_get_v(pb); // val_10 + off += ffio_read_varlen(pb); // val_10 + avio_r8(pb); // '4' + num_audio = avio_r8(pb); + avio_seek(pb, off, SEEK_SET); + + if(num_audio != 1) + av_log(s, AV_LOG_WARNING, "viv: number of audio tracks %d is not 1\n", num_audio); + + for(i=0;icodec; + + st->id = num_video + i; + + acodec->codec_type = CODEC_TYPE_AUDIO; + acodec->codec_id = CODEC_ID_VORBIS; + acodec->flags |= CODEC_FLAG_GLOBAL_HEADER; // ? + + off = avio_tell(pb); + //off += ff_get_v(pb); // length + off += ffio_read_varlen(pb); // length + avio_r8(pb); // '5' + avio_r8(pb); //codec_id + avio_rl16(pb); //codec_subid + acodec->channels = avio_rl16(pb); // channels + acodec->sample_rate = avio_rl32(pb); // sample_rate + avio_seek(pb, 10, SEEK_CUR); // data_1 + q = avio_r8(pb); + avio_seek(pb, q, SEEK_CUR); // data_2 + avio_r8(pb); // zeropad + + if(avio_tell(pb) < off) { + int num_data; + int xd_size = 0; + int data_len[256]; + uint8_t * p; + int offset = 1; + //ff_get_v(pb); // val_13 + ffio_read_varlen(pb); // val_13 + avio_r8(pb); // '19' + //ff_get_v(pb); // len_3 + ffio_read_varlen(pb); // len_3 + num_data = avio_r8(pb); + for(j=0;jextradata_size = 64 + xd_size + xd_size / 255; + acodec->extradata = (uint8_t*)av_mallocz(acodec->extradata_size); + + p = acodec->extradata; + p[0] = 2; + + for(j=0;jextradata_size = offset; + } + } + + av_free(pb); +} + +static void track_index(VIV_DemuxContext *viv, AVFormatContext *s, uint8_t *buf, int size) +{ + int i; + int64_t off; + int poff; + int maxnp=0; + AVIOContext *pb=0; + + pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); + //ff_get_v(pb); // track_index_len + ffio_read_varlen(pb); // track_index_len + avio_r8(pb); // 'c' + //viv->n_sb_blocks = ff_get_v(pb); + viv->n_sb_blocks = ffio_read_varlen(pb); + viv->sb_blocks = av_mallocz(sizeof(VIV_SB_block) * viv->n_sb_blocks); + if(!viv->sb_blocks) { + viv->n_sb_blocks = 0; + av_free(pb); + return; + } + + off = 0; + poff = 0; + + for(i=0;in_sb_blocks;i++) { + viv->sb_blocks[i].byte_offset = off; + viv->sb_blocks[i].packet_offset = poff; + + //viv->sb_blocks[i].size = ff_get_v(pb); + //viv->sb_blocks[i].n_packets = ff_get_v(pb); + viv->sb_blocks[i].size = ffio_read_varlen(pb); + viv->sb_blocks[i].n_packets = ffio_read_varlen(pb); + + off += viv->sb_blocks[i].size; + poff += viv->sb_blocks[i].n_packets; + + + if(maxnp < viv->sb_blocks[i].n_packets) + maxnp = viv->sb_blocks[i].n_packets; + } + + viv->sb_entries = av_mallocz(maxnp * sizeof(VIV_SB_entry)); + av_free(pb); +} + +static void load_sb_block(AVFormatContext *s, VIV_DemuxContext *viv, int expected_size) +{ + uint32_t size=0; + int i; + AVIOContext *pb = 0; + if(viv->sb_pb) { + av_free(viv->sb_pb); + viv->sb_pb = NULL; + } + + if(viv->sb_buf) + av_free(viv->sb_buf); + + viv->sb_buf = read_sb_block(s->pb, &size, &viv->sb_key, expected_size); + if(!viv->sb_buf) { + return; + } + + pb = avio_alloc_context(viv->sb_buf, size, 0, NULL, NULL, NULL, NULL); + viv->sb_pb = pb; + + avio_r8(pb); // 'S' + avio_r8(pb); // 'B' + //ff_get_v(pb); // size + ffio_read_varlen(pb); // size + avio_r8(pb); // junk + //ff_get_v(pb); // first packet + ffio_read_varlen(pb); // first packet + + viv->n_sb_entries = viv->sb_blocks[viv->current_sb].n_packets; + + for(i=0;in_sb_entries;i++) { + //viv->sb_entries[i].size = ff_get_v(pb); + viv->sb_entries[i].size = ffio_read_varlen(pb); + viv->sb_entries[i].flag = avio_r8(pb); + } + + //ff_get_v(pb); // 0 + ffio_read_varlen(pb); // 0 + avio_r8(pb); // 0 + + viv->current_sb_entry = 0; +} + +static int viv_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + VIV_DemuxContext *viv = s->priv_data; + AVIOContext *pb = s->pb; + int64_t header_end; + int num_tracks; + uint32_t key, k2; + uint32_t v; + uint8_t keybuffer[187]; + uint32_t b22_size = 0; + uint32_t b22_key = 0; + uint8_t *buf = 0; + + // string "vividas03" + avio_seek(pb, 9, SEEK_CUR); + + header_end = avio_tell(pb); + + // v: header size + //header_end += ff_get_v(pb); + header_end += ffio_read_varlen(pb); + + // u8: n tracks + num_tracks = avio_r8(pb); + + if(num_tracks != 1) { + av_log(s, AV_LOG_ERROR, "number of tracks %d is not 1\n", num_tracks); + return AVERROR(EINVAL); + } + + v = avio_r8(pb); + avio_seek(pb, v, SEEK_CUR); + + avio_read(pb, keybuffer, 187); + key = decode_key(keybuffer); + viv->sb_key = key; + + avio_rl32(pb); // track_header_len + + for(;;) { + int64_t here = avio_tell(pb); + int block_len, block_type; + + if(here >= header_end) + break; + + //block_len = ff_get_v(pb); + block_len = ffio_read_varlen(pb); + block_type = avio_r8(pb); + + if(block_type == 22) { + avio_read(pb, keybuffer, 187); + b22_key = decode_key(keybuffer); + b22_size = avio_rl32(pb); + } + + avio_seek(pb, here + block_len, SEEK_SET); + } + + if(b22_size) { + k2 = b22_key; + buf = read_vblock(pb, &v, b22_key, &k2, 0); + if(!buf) + return AVERROR(EIO); + + av_free(buf); + } + + k2 = key; + buf = read_vblock(pb, &v, key, &k2, 0); + if(!buf) + return AVERROR(EIO); + track_header(viv, s, buf, v); + av_free(buf); + + buf = read_vblock(pb, &v, key, &k2, v); + if(!buf) + return AVERROR(EIO); + track_index(viv, s, buf, v); + av_free(buf); + + viv->sb_offset = avio_tell(pb); + if(viv->n_sb_blocks > 0) { + viv->current_sb = 0; + load_sb_block(s, viv, viv->sb_blocks[0].size); + } else { + viv->current_sb = -1; + } + + return 0; +} + +static int viv_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + VIV_DemuxContext *viv = s->priv_data; + AVIOContext *pb; + int64_t off; + + if(viv->current_audio_subpacket < viv->n_audio_subpackets) { + // audio packets + AVStream *astream; + int size = viv->audio_subpackets[viv->current_audio_subpacket+1].start - viv->audio_subpackets[viv->current_audio_subpacket].start; + pb = viv->sb_pb; + av_get_packet(pb, pkt, size); + pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; + + pkt->stream_index = 1; + astream = s->streams[pkt->stream_index]; + + pkt->pts = viv->audio_sample * (long long)astream->time_base.den / (long long)astream->time_base.num / (long long)astream->codec->sample_rate; + viv->audio_sample += viv->audio_subpackets[viv->current_audio_subpacket].pcm_bytes / 2 / astream->codec->channels; + pkt->flags |= AV_PKT_FLAG_KEY; + viv->current_audio_subpacket++; + return 0; + } + + if(viv->current_sb_entry >= viv->n_sb_entries) { + if(viv->current_sb+1 >= viv->n_sb_blocks) + return AVERROR(EIO); + viv->current_sb++; + + load_sb_block(s, viv, 0); + viv->current_sb_entry = 0; + } + + pb = viv->sb_pb; + off = avio_tell(pb); + off += viv->sb_entries[viv->current_sb_entry].size; + + if(viv->sb_entries[viv->current_sb_entry].flag == 0) { + // A/V packet + int i; + //int v_size = ff_get_v(pb); + int v_size = ffio_read_varlen(pb); + ///*int a_size = */ff_get_v(pb); + /*int a_size = */ffio_read_varlen(pb); + av_get_packet(pb, pkt, v_size); + pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; + + pkt->pts = viv->sb_blocks[viv->current_sb].packet_offset + viv->current_sb_entry; + pkt->flags |= (pkt->data[0]&0x80)?0:AV_PKT_FLAG_KEY; + pkt->stream_index = 0; + + for(i=0;i 0 && start == 0) + break; + + viv->n_audio_subpackets = i+1; + viv->audio_subpackets[i].start = start; + viv->audio_subpackets[i].pcm_bytes = pcm_bytes; + } + viv->audio_subpackets[viv->n_audio_subpackets].start = (int)(off - avio_tell(pb)); + viv->current_audio_subpacket = 0; + //viv->n_audio_subpackets = 0; + //avio_seek(pb, off, SEEK_SET); + + } else { + // V packet + //int v_size = ff_get_v(pb); + int v_size = ffio_read_varlen(pb); + av_get_packet(pb, pkt, v_size); + pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; + pkt->pts = viv->sb_blocks[viv->current_sb].packet_offset + viv->current_sb_entry; + pkt->flags |= (pkt->data[0]&0x80)?0:AV_PKT_FLAG_KEY; + pkt->stream_index = 0; + } + + viv->current_sb_entry++; + +// avio_seek(pb, off, SEEK_SET); + + return 0; +} + +static int viv_read_close(AVFormatContext *s) +{ + VIV_DemuxContext *viv = s->priv_data; + if(viv->sb_pb) + av_free(viv->sb_pb); + + if(viv->sb_buf) + av_free(viv->sb_buf); + + if(viv->sb_blocks) + av_free(viv->sb_blocks); + + if(viv->sb_entries) + av_free(viv->sb_entries); + + return 0; +} + +static int viv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + VIV_DemuxContext *viv = s->priv_data; + int frame = 0; + int i; + + if(stream_index == 0) + frame = (int)timestamp; + else + frame = (int)timestamp * s->streams[stream_index]->time_base.den * s->streams[0]->time_base.num / s->streams[stream_index]->time_base.num / s->streams[0]->time_base.den; + + for(i=0;in_sb_blocks;i++) { + if(frame >= viv->sb_blocks[i].packet_offset && frame < viv->sb_blocks[i].packet_offset + viv->sb_blocks[i].n_packets) { + // flush audio packet queue + viv->current_audio_subpacket = 0; + viv->n_audio_subpackets = 0; + viv->current_sb = i; + // seek to ith sb block + avio_seek(s->pb, viv->sb_offset + viv->sb_blocks[i].byte_offset, SEEK_SET); + // load the block + load_sb_block(s, viv, 0); + // most problematic part: guess audio offset + viv->audio_sample = (int64_t)viv->sb_blocks[i].packet_offset * (int64_t)s->streams[1]->codec->sample_rate * s->streams[0]->time_base.num / s->streams[0]->time_base.den; + // hand-tuned 1.3s a/v offset + viv->audio_sample += 1300 * s->streams[1]->codec->sample_rate / 1000; + viv->current_sb_entry = 0; + return 1; + } + } + return 0; +} + +AVInputFormat ff_vividas_demuxer = { + "vividas", + "Vividas VIV format", + sizeof(VIV_DemuxContext), + viv_probe, + viv_read_header, + viv_read_packet, + viv_read_close, + viv_read_seek +}; + diff --git a/lib/ffmpeg/patches/0110-vividas-support.patch b/lib/ffmpeg/patches/0110-vividas-support.patch new file mode 100644 index 0000000..713a0a5 --- /dev/null +++ b/lib/ffmpeg/patches/0110-vividas-support.patch @@ -0,0 +1,770 @@ +diff --git a/lib/ffmpeg/libavformat/Makefile b/lib/ffmpeg/libavformat/Makefile +index bd2b17b..6642498 100644 +--- a/lib/ffmpeg/libavformat/Makefile ++++ b/lib/ffmpeg/libavformat/Makefile +@@ -315,6 +315,7 @@ OBJS-$(CONFIG_TXD_DEMUXER) += txd.o + OBJS-$(CONFIG_VC1_DEMUXER) += rawdec.o + OBJS-$(CONFIG_VC1T_DEMUXER) += vc1test.o + OBJS-$(CONFIG_VC1T_MUXER) += vc1testenc.o ++OBJS-$(CONFIG_VIVIDAS_DEMUXER) += vividas.o + OBJS-$(CONFIG_VMD_DEMUXER) += sierravmd.o + OBJS-$(CONFIG_VOC_DEMUXER) += vocdec.o voc.o + OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o +diff --git a/lib/ffmpeg/libavformat/allformats.c b/lib/ffmpeg/libavformat/allformats.c +index 134839f..0af5ba9 100644 +--- a/lib/ffmpeg/libavformat/allformats.c ++++ b/lib/ffmpeg/libavformat/allformats.c +@@ -232,6 +232,7 @@ void av_register_all(void) + REGISTER_DEMUXER (TTY, tty); + REGISTER_DEMUXER (VC1, vc1); + REGISTER_MUXDEMUX (VC1T, vc1t); ++ REGISTER_DEMUXER (VIVIDAS, vividas); + REGISTER_DEMUXER (VMD, vmd); + REGISTER_MUXDEMUX (VOC, voc); + REGISTER_DEMUXER (VQF, vqf); +diff --git a/lib/ffmpeg/libavformat/vividas.c b/lib/ffmpeg/libavformat/vividas.c +new file mode 100644 +index 0000000..2d3ba27 +--- /dev/null ++++ b/lib/ffmpeg/libavformat/vividas.c +@@ -0,0 +1,740 @@ ++/* ++ * Vividas VIV format Demuxer ++ * Copyright (c) 2010 Andrzej Szombierski ++ * based on vivparse Copyright (c) 2007 Måns Rullgård ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * @brief Vividas VIV (.viv) file demuxer ++ * @author Andrzej Szombierski [qq at kuku eu org] (2010-07) ++ * @sa http://wiki.multimedia.cx/index.php?title=Vividas_VIV ++ */ ++ ++#define AV_PKT_FLAG_KEY 0x0001 ++ ++#include "libavutil/intreadwrite.h" ++#include "avio_internal.h" ++#include "avformat.h" ++ ++#define MAX_AUDIO_SUBPACKETS 100 ++#define CODEC_TYPE_VIDEO AVMEDIA_TYPE_VIDEO ++#define CODEC_TYPE_AUDIO AVMEDIA_TYPE_AUDIO ++ ++typedef struct VIV_SB_block { ++ int size, n_packets; ++ int64_t byte_offset; ++ int packet_offset; ++} VIV_SB_block; ++ ++typedef struct VIV_SB_entry { ++ int size, flag; ++} VIV_SB_entry; ++ ++typedef struct VIV_AudioSubpacket { ++ int start, pcm_bytes; ++} VIV_AudioSubpacket; ++ ++typedef struct VIV_DemuxContext ++{ ++ int n_sb_blocks; ++ VIV_SB_block *sb_blocks; ++ ++ uint32_t sb_key; ++ int64_t sb_offset; ++ ++ int current_sb, current_sb_entry; ++ uint8_t *sb_buf; ++ AVIOContext *sb_pb; ++ int n_sb_entries; ++ VIV_SB_entry *sb_entries; ++ ++ int n_audio_subpackets; ++ int current_audio_subpacket; ++ ++ int audio_sample; ++ ++ VIV_AudioSubpacket audio_subpackets[MAX_AUDIO_SUBPACKETS]; ++} VIV_DemuxContext; ++ ++static int viv_probe(AVProbeData *p) ++{ ++ if(strncmp((char*)p->buf, "vividas03", 9)) ++ return 0; ++ ++ return AVPROBE_SCORE_MAX; ++} ++ ++const unsigned short keybits[32] = { ++ 163, 416, 893, 82, 223, 572, 1137, 430, ++ 659, 1104, 13, 626, 695, 972, 1465, 686, ++ 843, 1216, 317, 1122, 1383, 92, 513, 1158, ++ 1243, 48, 573, 1306, 1495, 396, 1009, 350, ++}; ++ ++static uint32_t decode_key(uint8_t *buf) ++{ ++ uint32_t key = 0; ++ int i; ++ ++ for (i = 0; i < 32; i++) { ++ unsigned p = keybits[i]; ++ key |= !!(buf[p>>3] & (1<<(p&7))) << i; ++ } ++ ++ return key; ++} ++ ++static void put_v(uint8_t *p, int v) ++{ ++ if(v>>28) ++ *p++ = ((v>>28)&0x7f)|0x80; ++ if(v>>21) ++ *p++ = ((v>>21)&0x7f)|0x80; ++ if(v>>14) ++ *p++ = ((v>>14)&0x7f)|0x80; ++ if(v>>7) ++ *p++ = ((v>>7)&0x7f)|0x80; ++ *p++ = v&0x7f; ++} ++ ++static unsigned int recover_key(unsigned char sample[4], int expected_size) ++{ ++ unsigned char plaintext[8] = { 'S', 'B' }; ++ put_v(plaintext+2, expected_size); ++ ++ return (sample[0]^plaintext[0])| ++ ((sample[1]^plaintext[1])<<8)| ++ ((sample[2]^plaintext[2])<<16)| ++ ((sample[3]^plaintext[3])<<24); ++} ++ ++static void xor_block(void *p1, void *p2, int size, int key, int *key_ptr) ++{ ++ int *d1 = p1; ++ int *d2 = p2; ++ int k = *key_ptr; ++ ++ size >>= 2; ++ ++ while (size--) { ++ *d2 = *d1 ^ k; ++ k += key; ++ d1++; ++ d2++; ++ } ++ ++ *key_ptr = k; ++} ++ ++static void decode_block(uint8_t *src, uint8_t *dest, int size, ++ uint32_t key, uint32_t *key_ptr, ++ int align) ++{ ++ int s = size; ++ char tmp[4]; ++ int a2; ++ ++ if (!size) ++ return; ++ ++ align &= 3; ++ a2 = (4 - align) & 3; ++ ++ if (align) { ++ uint32_t tmpkey = *key_ptr - key; ++ memcpy(tmp + align, src, a2); ++ xor_block(tmp, tmp, 4, key, &tmpkey); ++ memcpy(dest, tmp + align, a2); ++ s -= a2; ++ } ++ ++ if (s >= 4) { ++ if (!align) ++ align = 4; ++ xor_block(src + a2, dest + a2, s & ~3, ++ key, key_ptr); ++ s &= 3; ++ } ++ ++ if (s) { ++ size -= s; ++ memcpy(tmp, src + size, s); ++ xor_block(&tmp, &tmp, 4, key, key_ptr); ++ memcpy(dest + size, tmp, s); ++ } ++} ++ ++static uint32_t get_v(uint8_t *p) ++{ ++ uint32_t v = 0; ++ ++ do { ++ v <<= 7; ++ v += *p & 0x7f; ++ } while (*p++ & 0x80); ++ ++ return v; ++} ++ ++static uint8_t *read_vblock(AVIOContext *src, uint32_t *size, uint32_t key, uint32_t *k2, int align) ++{ ++ uint8_t tmp[4]; ++ uint8_t *buf; ++ unsigned n; ++ ++ if(avio_read(src, tmp, 4) != 4) ++ return NULL; ++ ++ decode_block(tmp, tmp, 4, key, k2, align); ++ ++ n = get_v(tmp); ++ ++ buf = av_malloc(n); ++ if (!buf) ++ return NULL; ++ ++ *size = n; ++ n -= 4; ++ ++ memcpy(buf, tmp, 4); ++ ++ if (avio_read(src, buf + 4, n) == n) { ++ decode_block(buf + 4, buf + 4, n, key, k2, align + 4); ++ } else { ++ av_free(buf); ++ buf = NULL; ++ } ++ ++ return buf; ++} ++ ++static uint8_t *read_sb_block(AVIOContext *src, unsigned *size, uint32_t *key, int expected_size) ++{ ++ uint8_t *buf; ++ uint8_t ibuf[8], sbuf[8]; ++ uint32_t k2; ++ int n; ++ ++ if (avio_read(src, ibuf, 8) < 8) ++ return NULL; ++ ++ k2 = *key; ++ decode_block(ibuf, sbuf, 8, *key, &k2, 0); ++ ++ n = get_v(sbuf+2); ++ ++ if (sbuf[0] != 'S' || sbuf[1] != 'B' || (expected_size>0 && n != expected_size)) { ++ uint32_t tmpkey = recover_key(ibuf, expected_size); ++ k2 = tmpkey; ++ decode_block(ibuf, sbuf, 8, tmpkey, &k2, 0); ++ n = get_v(sbuf+2); ++ if(sbuf[0] != 'S' || sbuf[1] != 'B' || expected_size != n) ++ return NULL; ++ *key = tmpkey; ++ } ++ ++ buf = av_malloc(n); ++ if (!buf) ++ return NULL; ++ ++ memcpy(buf, sbuf, 8); ++ ++ *size = n; ++ n -= 8; ++ ++ if (avio_read(src, buf+8, n) < n) { ++ av_free(buf); ++ return NULL; ++ } ++ ++ decode_block(buf + 8, buf + 8, n, *key, &k2, 0); ++ ++ return buf; ++} ++ ++static void track_header(VIV_DemuxContext *viv, AVFormatContext *s, uint8_t *buf, int size) ++{ ++ int i,j; ++ int64_t off; ++ int val_1; ++ int num_video, num_audio; ++ AVIOContext *pb=0; ++ ++ pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); ++ //ff_get_v(pb); // track_header_len ++ ffio_read_varlen(pb); // track_header_len ++ avio_r8(pb); // '1' ++ ++ //val_1 = ff_get_v(pb); ++ val_1 = ffio_read_varlen(pb); ++ ++ for(i=0;icodec; ++ ++ st->id = i; ++ ++ vcodec->codec_type = CODEC_TYPE_VIDEO; ++ vcodec->codec_id = CODEC_ID_VP6; ++ ++ off = avio_tell(pb); ++ //off += ff_get_v(pb); ++ off += ffio_read_varlen(pb); ++ avio_r8(pb); // '3' ++ avio_r8(pb); // val_7 ++ st->time_base.num = avio_rl32(pb); // frame_time ++ st->time_base.den = avio_rl32(pb); // time_base ++ st->nb_frames = avio_rl32(pb); // n frames ++ vcodec->width = avio_rl16(pb); // width ++ vcodec->height = avio_rl16(pb); // height ++ avio_r8(pb); // val_8 ++ avio_rl32(pb); // val_9 ++ ++ vcodec->flags |= CODEC_FLAG_GLOBAL_HEADER; // ? ++ ++ avio_seek(pb, off, SEEK_SET); ++ } ++ ++ off = avio_tell(pb); ++ //off += ff_get_v(pb); // val_10 ++ off += ffio_read_varlen(pb); // val_10 ++ avio_r8(pb); // '4' ++ num_audio = avio_r8(pb); ++ avio_seek(pb, off, SEEK_SET); ++ ++ if(num_audio != 1) ++ av_log(s, AV_LOG_WARNING, "viv: number of audio tracks %d is not 1\n", num_audio); ++ ++ for(i=0;icodec; ++ ++ st->id = num_video + i; ++ ++ acodec->codec_type = CODEC_TYPE_AUDIO; ++ acodec->codec_id = CODEC_ID_VORBIS; ++ acodec->flags |= CODEC_FLAG_GLOBAL_HEADER; // ? ++ ++ off = avio_tell(pb); ++ //off += ff_get_v(pb); // length ++ off += ffio_read_varlen(pb); // length ++ avio_r8(pb); // '5' ++ avio_r8(pb); //codec_id ++ avio_rl16(pb); //codec_subid ++ acodec->channels = avio_rl16(pb); // channels ++ acodec->sample_rate = avio_rl32(pb); // sample_rate ++ avio_seek(pb, 10, SEEK_CUR); // data_1 ++ q = avio_r8(pb); ++ avio_seek(pb, q, SEEK_CUR); // data_2 ++ avio_r8(pb); // zeropad ++ ++ if(avio_tell(pb) < off) { ++ int num_data; ++ int xd_size = 0; ++ int data_len[256]; ++ uint8_t * p; ++ int offset = 1; ++ //ff_get_v(pb); // val_13 ++ ffio_read_varlen(pb); // val_13 ++ avio_r8(pb); // '19' ++ //ff_get_v(pb); // len_3 ++ ffio_read_varlen(pb); // len_3 ++ num_data = avio_r8(pb); ++ for(j=0;jextradata_size = 64 + xd_size + xd_size / 255; ++ acodec->extradata = (uint8_t*)av_mallocz(acodec->extradata_size); ++ ++ p = acodec->extradata; ++ p[0] = 2; ++ ++ for(j=0;jextradata_size = offset; ++ } ++ } ++ ++ av_free(pb); ++} ++ ++static void track_index(VIV_DemuxContext *viv, AVFormatContext *s, uint8_t *buf, int size) ++{ ++ int i; ++ int64_t off; ++ int poff; ++ int maxnp=0; ++ AVIOContext *pb=0; ++ ++ pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); ++ //ff_get_v(pb); // track_index_len ++ ffio_read_varlen(pb); // track_index_len ++ avio_r8(pb); // 'c' ++ //viv->n_sb_blocks = ff_get_v(pb); ++ viv->n_sb_blocks = ffio_read_varlen(pb); ++ viv->sb_blocks = av_mallocz(sizeof(VIV_SB_block) * viv->n_sb_blocks); ++ if(!viv->sb_blocks) { ++ viv->n_sb_blocks = 0; ++ av_free(pb); ++ return; ++ } ++ ++ off = 0; ++ poff = 0; ++ ++ for(i=0;in_sb_blocks;i++) { ++ viv->sb_blocks[i].byte_offset = off; ++ viv->sb_blocks[i].packet_offset = poff; ++ ++ //viv->sb_blocks[i].size = ff_get_v(pb); ++ //viv->sb_blocks[i].n_packets = ff_get_v(pb); ++ viv->sb_blocks[i].size = ffio_read_varlen(pb); ++ viv->sb_blocks[i].n_packets = ffio_read_varlen(pb); ++ ++ off += viv->sb_blocks[i].size; ++ poff += viv->sb_blocks[i].n_packets; ++ ++ ++ if(maxnp < viv->sb_blocks[i].n_packets) ++ maxnp = viv->sb_blocks[i].n_packets; ++ } ++ ++ viv->sb_entries = av_mallocz(maxnp * sizeof(VIV_SB_entry)); ++ av_free(pb); ++} ++ ++static void load_sb_block(AVFormatContext *s, VIV_DemuxContext *viv, int expected_size) ++{ ++ uint32_t size=0; ++ int i; ++ AVIOContext *pb = 0; ++ if(viv->sb_pb) { ++ av_free(viv->sb_pb); ++ viv->sb_pb = NULL; ++ } ++ ++ if(viv->sb_buf) ++ av_free(viv->sb_buf); ++ ++ viv->sb_buf = read_sb_block(s->pb, &size, &viv->sb_key, expected_size); ++ if(!viv->sb_buf) { ++ return; ++ } ++ ++ pb = avio_alloc_context(viv->sb_buf, size, 0, NULL, NULL, NULL, NULL); ++ viv->sb_pb = pb; ++ ++ avio_r8(pb); // 'S' ++ avio_r8(pb); // 'B' ++ //ff_get_v(pb); // size ++ ffio_read_varlen(pb); // size ++ avio_r8(pb); // junk ++ //ff_get_v(pb); // first packet ++ ffio_read_varlen(pb); // first packet ++ ++ viv->n_sb_entries = viv->sb_blocks[viv->current_sb].n_packets; ++ ++ for(i=0;in_sb_entries;i++) { ++ //viv->sb_entries[i].size = ff_get_v(pb); ++ viv->sb_entries[i].size = ffio_read_varlen(pb); ++ viv->sb_entries[i].flag = avio_r8(pb); ++ } ++ ++ //ff_get_v(pb); // 0 ++ ffio_read_varlen(pb); // 0 ++ avio_r8(pb); // 0 ++ ++ viv->current_sb_entry = 0; ++} ++ ++static int viv_read_header(AVFormatContext *s, ++ AVFormatParameters *ap) ++{ ++ VIV_DemuxContext *viv = s->priv_data; ++ AVIOContext *pb = s->pb; ++ int64_t header_end; ++ int num_tracks; ++ uint32_t key, k2; ++ uint32_t v; ++ uint8_t keybuffer[187]; ++ uint32_t b22_size = 0; ++ uint32_t b22_key = 0; ++ uint8_t *buf = 0; ++ ++ // string "vividas03" ++ avio_seek(pb, 9, SEEK_CUR); ++ ++ header_end = avio_tell(pb); ++ ++ // v: header size ++ //header_end += ff_get_v(pb); ++ header_end += ffio_read_varlen(pb); ++ ++ // u8: n tracks ++ num_tracks = avio_r8(pb); ++ ++ if(num_tracks != 1) { ++ av_log(s, AV_LOG_ERROR, "number of tracks %d is not 1\n", num_tracks); ++ return AVERROR(EINVAL); ++ } ++ ++ v = avio_r8(pb); ++ avio_seek(pb, v, SEEK_CUR); ++ ++ avio_read(pb, keybuffer, 187); ++ key = decode_key(keybuffer); ++ viv->sb_key = key; ++ ++ avio_rl32(pb); // track_header_len ++ ++ for(;;) { ++ int64_t here = avio_tell(pb); ++ int block_len, block_type; ++ ++ if(here >= header_end) ++ break; ++ ++ //block_len = ff_get_v(pb); ++ block_len = ffio_read_varlen(pb); ++ block_type = avio_r8(pb); ++ ++ if(block_type == 22) { ++ avio_read(pb, keybuffer, 187); ++ b22_key = decode_key(keybuffer); ++ b22_size = avio_rl32(pb); ++ } ++ ++ avio_seek(pb, here + block_len, SEEK_SET); ++ } ++ ++ if(b22_size) { ++ k2 = b22_key; ++ buf = read_vblock(pb, &v, b22_key, &k2, 0); ++ if(!buf) ++ return AVERROR(EIO); ++ ++ av_free(buf); ++ } ++ ++ k2 = key; ++ buf = read_vblock(pb, &v, key, &k2, 0); ++ if(!buf) ++ return AVERROR(EIO); ++ track_header(viv, s, buf, v); ++ av_free(buf); ++ ++ buf = read_vblock(pb, &v, key, &k2, v); ++ if(!buf) ++ return AVERROR(EIO); ++ track_index(viv, s, buf, v); ++ av_free(buf); ++ ++ viv->sb_offset = avio_tell(pb); ++ if(viv->n_sb_blocks > 0) { ++ viv->current_sb = 0; ++ load_sb_block(s, viv, viv->sb_blocks[0].size); ++ } else { ++ viv->current_sb = -1; ++ } ++ ++ return 0; ++} ++ ++static int viv_read_packet(AVFormatContext *s, ++ AVPacket *pkt) ++{ ++ VIV_DemuxContext *viv = s->priv_data; ++ AVIOContext *pb; ++ int64_t off; ++ ++ if(viv->current_audio_subpacket < viv->n_audio_subpackets) { ++ // audio packets ++ AVStream *astream; ++ int size = viv->audio_subpackets[viv->current_audio_subpacket+1].start - viv->audio_subpackets[viv->current_audio_subpacket].start; ++ pb = viv->sb_pb; ++ av_get_packet(pb, pkt, size); ++ pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; ++ ++ pkt->stream_index = 1; ++ astream = s->streams[pkt->stream_index]; ++ ++ pkt->pts = viv->audio_sample * (long long)astream->time_base.den / (long long)astream->time_base.num / (long long)astream->codec->sample_rate; ++ viv->audio_sample += viv->audio_subpackets[viv->current_audio_subpacket].pcm_bytes / 2 / astream->codec->channels; ++ pkt->flags |= AV_PKT_FLAG_KEY; ++ viv->current_audio_subpacket++; ++ return 0; ++ } ++ ++ if(viv->current_sb_entry >= viv->n_sb_entries) { ++ if(viv->current_sb+1 >= viv->n_sb_blocks) ++ return AVERROR(EIO); ++ viv->current_sb++; ++ ++ load_sb_block(s, viv, 0); ++ viv->current_sb_entry = 0; ++ } ++ ++ pb = viv->sb_pb; ++ off = avio_tell(pb); ++ off += viv->sb_entries[viv->current_sb_entry].size; ++ ++ if(viv->sb_entries[viv->current_sb_entry].flag == 0) { ++ // A/V packet ++ int i; ++ //int v_size = ff_get_v(pb); ++ int v_size = ffio_read_varlen(pb); ++ ///*int a_size = */ff_get_v(pb); ++ /*int a_size = */ffio_read_varlen(pb); ++ av_get_packet(pb, pkt, v_size); ++ pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; ++ ++ pkt->pts = viv->sb_blocks[viv->current_sb].packet_offset + viv->current_sb_entry; ++ pkt->flags |= (pkt->data[0]&0x80)?0:AV_PKT_FLAG_KEY; ++ pkt->stream_index = 0; ++ ++ for(i=0;i 0 && start == 0) ++ break; ++ ++ viv->n_audio_subpackets = i+1; ++ viv->audio_subpackets[i].start = start; ++ viv->audio_subpackets[i].pcm_bytes = pcm_bytes; ++ } ++ viv->audio_subpackets[viv->n_audio_subpackets].start = (int)(off - avio_tell(pb)); ++ viv->current_audio_subpacket = 0; ++ //viv->n_audio_subpackets = 0; ++ //avio_seek(pb, off, SEEK_SET); ++ ++ } else { ++ // V packet ++ //int v_size = ff_get_v(pb); ++ int v_size = ffio_read_varlen(pb); ++ av_get_packet(pb, pkt, v_size); ++ pkt->pos += viv->sb_offset + viv->sb_blocks[viv->current_sb].byte_offset; ++ pkt->pts = viv->sb_blocks[viv->current_sb].packet_offset + viv->current_sb_entry; ++ pkt->flags |= (pkt->data[0]&0x80)?0:AV_PKT_FLAG_KEY; ++ pkt->stream_index = 0; ++ } ++ ++ viv->current_sb_entry++; ++ ++// avio_seek(pb, off, SEEK_SET); ++ ++ return 0; ++} ++ ++static int viv_read_close(AVFormatContext *s) ++{ ++ VIV_DemuxContext *viv = s->priv_data; ++ if(viv->sb_pb) ++ av_free(viv->sb_pb); ++ ++ if(viv->sb_buf) ++ av_free(viv->sb_buf); ++ ++ if(viv->sb_blocks) ++ av_free(viv->sb_blocks); ++ ++ if(viv->sb_entries) ++ av_free(viv->sb_entries); ++ ++ return 0; ++} ++ ++static int viv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) ++{ ++ VIV_DemuxContext *viv = s->priv_data; ++ int frame = 0; ++ int i; ++ ++ if(stream_index == 0) ++ frame = (int)timestamp; ++ else ++ frame = (int)timestamp * s->streams[stream_index]->time_base.den * s->streams[0]->time_base.num / s->streams[stream_index]->time_base.num / s->streams[0]->time_base.den; ++ ++ for(i=0;in_sb_blocks;i++) { ++ if(frame >= viv->sb_blocks[i].packet_offset && frame < viv->sb_blocks[i].packet_offset + viv->sb_blocks[i].n_packets) { ++ // flush audio packet queue ++ viv->current_audio_subpacket = 0; ++ viv->n_audio_subpackets = 0; ++ viv->current_sb = i; ++ // seek to ith sb block ++ avio_seek(s->pb, viv->sb_offset + viv->sb_blocks[i].byte_offset, SEEK_SET); ++ // load the block ++ load_sb_block(s, viv, 0); ++ // most problematic part: guess audio offset ++ viv->audio_sample = (int64_t)viv->sb_blocks[i].packet_offset * (int64_t)s->streams[1]->codec->sample_rate * s->streams[0]->time_base.num / s->streams[0]->time_base.den; ++ // hand-tuned 1.3s a/v offset ++ viv->audio_sample += 1300 * s->streams[1]->codec->sample_rate / 1000; ++ viv->current_sb_entry = 0; ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++AVInputFormat ff_vividas_demuxer = { ++ "vividas", ++ "Vividas VIV format", ++ sizeof(VIV_DemuxContext), ++ viv_probe, ++ viv_read_header, ++ viv_read_packet, ++ viv_read_close, ++ viv_read_seek ++}; ++ -- 1.7.9.5