1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-11-26 19:01:44 +02:00
FFmpeg/libavformat/vividas.c
Andreas Rheinhardt 7001ff74ba avformat/aviobuf: Add ffio_init_(read|write)_context()
Most users of ffio_init_context() simply want to wrap
a buffer into an AVIOContext; they do not provide
function pointers at all.

Therefore this commit adds shortcuts for these two common
operations. This also allows to accept const data when reading
(i.e. the const is now cast away at a central place in
ffio_init_read_context() instead of at several callers).
This also allows to constify the data in ff_text_init_buf().

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2023-09-07 00:41:45 +02:00

804 lines
21 KiB
C

/*
* Vividas VIV format Demuxer
* Copyright (c) 2012 Krzysztof Klinikowski
* 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
*/
#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "avio_internal.h"
#include "avformat.h"
#include "internal.h"
#define MAX_AUDIO_SUBPACKETS 100
typedef struct VIV_SB_block {
int size, n_packets;
int64_t byte_offset;
int64_t 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 VividasDemuxContext {
int n_sb_blocks;
VIV_SB_block *sb_blocks;
int num_audio;
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;
int64_t audio_sample;
VIV_AudioSubpacket audio_subpackets[MAX_AUDIO_SUBPACKETS];
} VividasDemuxContext;
static int viv_probe(const AVProbeData *p)
{
if (memcmp(p->buf, "vividas03", 9))
return 0;
return AVPROBE_SCORE_MAX;
}
static const uint8_t keybits[32] = {
20, 52, 111, 10, 27, 71, 142, 53,
82, 138, 1, 78, 86, 121, 183, 85,
105, 152, 39, 140, 172, 11, 64, 144,
155, 6, 71, 163, 186, 49, 126, 43,
};
static uint32_t decode_key(uint8_t *buf)
{
uint32_t key = 0;
for (int i = 0; i < 32; i++) {
unsigned p = keybits[i];
key |= ((buf[p] >> ((i*5+3)&7)) & 1u) << i;
}
return key;
}
static void put_v(uint8_t *p, unsigned 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;
}
static unsigned recover_key(unsigned char sample[4], unsigned expected_size)
{
unsigned char plaintext[8] = { 'S', 'B' };
put_v(plaintext+2, expected_size);
return AV_RL32(sample) ^ AV_RL32(plaintext);
}
static void xor_block(void *p1, void *p2, unsigned size, int key, unsigned *key_ptr)
{
unsigned *d1 = p1;
unsigned *d2 = p2;
unsigned k = *key_ptr;
size >>= 2;
while (size > 0) {
*d2 = *d1 ^ (HAVE_BIGENDIAN ? av_bswap32(k) : k);
k += key;
d1++;
d2++;
size--;
}
*key_ptr = k;
}
static void decode_block(uint8_t *src, uint8_t *dest, unsigned size,
uint32_t key, uint32_t *key_ptr,
int align)
{
unsigned s = size;
char tmp[4];
int a2;
if (!size)
return;
align &= 3;
a2 = (4 - align) & 3;
if (align) {
uint32_t tmpkey = *key_ptr - key;
if (a2 > s) {
a2 = s;
avpriv_request_sample(NULL, "tiny aligned block");
}
memcpy(tmp + align, src, a2);
xor_block(tmp, tmp, 4, key, &tmpkey);
memcpy(dest, tmp + align, a2);
s -= a2;
}
if (s >= 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, int len)
{
uint32_t v = 0;
const uint8_t *end = p + len;
do {
if (p >= end || v >= UINT_MAX / 128 - *p)
return v;
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, 4);
if (n < 4)
return NULL;
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);
} else {
av_free(buf);
buf = NULL;
}
return buf;
}
static uint8_t *read_sb_block(AVIOContext *src, unsigned *size,
uint32_t *key, unsigned expected_size)
{
uint8_t *buf;
uint8_t ibuf[8], sbuf[8];
uint32_t k2;
unsigned 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, 6);
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, 6);
if (sbuf[0] != 'S' || sbuf[1] != 'B' || expected_size != n)
return NULL;
*key = tmpkey;
}
if (n < 8)
return NULL;
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 int track_header(VividasDemuxContext *viv, AVFormatContext *s,
const uint8_t *buf, int size)
{
int i, j, ret;
int64_t off;
int val_1;
int num_video;
FFIOContext pb0;
AVIOContext *const pb = &pb0.pub;
ffio_init_read_context(&pb0, buf, size);
ffio_read_varlen(pb); // track_header_len
avio_r8(pb); // '1'
val_1 = ffio_read_varlen(pb);
for (i=0;i<val_1;i++) {
int c = avio_r8(pb);
if (avio_feof(pb))
return AVERROR_EOF;
for (j=0;j<c;j++) {
if (avio_feof(pb))
return AVERROR_EOF;
avio_r8(pb); // val_3
avio_r8(pb); // val_4
}
}
avio_r8(pb); // num_streams
off = avio_tell(pb);
off += ffio_read_varlen(pb); // val_5
avio_r8(pb); // '2'
num_video = avio_r8(pb);
avio_seek(pb, off, SEEK_SET);
if (num_video != 1) {
av_log(s, AV_LOG_ERROR, "number of video tracks %d is not 1\n", num_video);
return AVERROR_PATCHWELCOME;
}
for (i = 0; i < num_video; i++) {
AVStream *st = avformat_new_stream(s, NULL);
int num, den;
if (!st)
return AVERROR(ENOMEM);
st->id = i;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = AV_CODEC_ID_VP6;
off = avio_tell(pb);
off += ffio_read_varlen(pb);
avio_r8(pb); // '3'
avio_r8(pb); // val_7
num = avio_rl32(pb); // frame_time
den = avio_rl32(pb); // time_base
avpriv_set_pts_info(st, 64, num, den);
st->nb_frames = avio_rl32(pb); // n frames
st->codecpar->width = avio_rl16(pb); // width
st->codecpar->height = avio_rl16(pb); // height
avio_r8(pb); // val_8
avio_rl32(pb); // val_9
avio_seek(pb, off, SEEK_SET);
}
off = avio_tell(pb);
off += ffio_read_varlen(pb); // val_10
avio_r8(pb); // '4'
viv->num_audio = avio_r8(pb);
avio_seek(pb, off, SEEK_SET);
if (viv->num_audio != 1)
av_log(s, AV_LOG_WARNING, "number of audio tracks %d is not 1\n", viv->num_audio);
for(i=0;i<viv->num_audio;i++) {
int q;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
st->id = num_video + i;
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_VORBIS;
off = avio_tell(pb);
off += ffio_read_varlen(pb); // length
avio_r8(pb); // '5'
avio_r8(pb); //codec_id
avio_rl16(pb); //codec_subid
st->codecpar->ch_layout.nb_channels = avio_rl16(pb); // channels
st->codecpar->sample_rate = avio_rl32(pb); // sample_rate
if (st->codecpar->sample_rate <= 0 || st->codecpar->ch_layout.nb_channels <= 0)
return AVERROR_INVALIDDATA;
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 = 1;
int data_len[256];
int offset = 1;
uint8_t *p;
ffio_read_varlen(pb); // val_13
avio_r8(pb); // '19'
ffio_read_varlen(pb); // len_3
num_data = avio_r8(pb);
for (j = 0; j < num_data; j++) {
int64_t len = ffio_read_varlen(pb);
if (len < 0 || len > INT_MAX/2 - xd_size) {
return AVERROR_INVALIDDATA;
}
data_len[j] = len;
xd_size += len + 1 + len/255;
}
ret = ff_alloc_extradata(st->codecpar, xd_size);
if (ret < 0)
return ret;
p = st->codecpar->extradata;
p[0] = 2;
for (j = 0; j < num_data - 1; j++) {
unsigned delta = av_xiphlacing(&p[offset], data_len[j]);
av_assert0(delta <= xd_size - offset);
offset += delta;
}
for (j = 0; j < num_data; j++) {
int ret = avio_read(pb, &p[offset], data_len[j]);
if (ret < data_len[j]) {
st->codecpar->extradata_size = 0;
av_freep(&st->codecpar->extradata);
break;
}
av_assert0(data_len[j] <= xd_size - offset);
offset += data_len[j];
}
if (offset < st->codecpar->extradata_size)
st->codecpar->extradata_size = offset;
}
}
return 0;
}
static int track_index(VividasDemuxContext *viv, AVFormatContext *s,
const uint8_t *buf, unsigned size)
{
int64_t off;
int64_t poff;
int maxnp=0;
FFIOContext pb0;
AVIOContext *const pb = &pb0.pub;
int i;
int64_t filesize = avio_size(s->pb);
uint64_t n_sb_blocks_tmp;
ffio_init_read_context(&pb0, buf, size);
ffio_read_varlen(pb); // track_index_len
avio_r8(pb); // 'c'
n_sb_blocks_tmp = ffio_read_varlen(pb);
if (n_sb_blocks_tmp > size / 2)
return AVERROR_INVALIDDATA;
viv->sb_blocks = av_calloc(n_sb_blocks_tmp, sizeof(*viv->sb_blocks));
if (!viv->sb_blocks) {
return AVERROR(ENOMEM);
}
viv->n_sb_blocks = n_sb_blocks_tmp;
off = 0;
poff = 0;
for (i = 0; i < viv->n_sb_blocks; i++) {
uint64_t size_tmp = ffio_read_varlen(pb);
uint64_t n_packets_tmp = ffio_read_varlen(pb);
if (size_tmp > INT_MAX || n_packets_tmp > INT_MAX)
return AVERROR_INVALIDDATA;
viv->sb_blocks[i].byte_offset = off;
viv->sb_blocks[i].packet_offset = poff;
viv->sb_blocks[i].size = size_tmp;
viv->sb_blocks[i].n_packets = n_packets_tmp;
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;
}
if (filesize > 0 && poff > filesize)
return AVERROR_INVALIDDATA;
viv->sb_entries = av_calloc(maxnp, sizeof(VIV_SB_entry));
if (!viv->sb_entries)
return AVERROR(ENOMEM);
return 0;
}
static void load_sb_block(AVFormatContext *s, VividasDemuxContext *viv, unsigned 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);
if (!pb)
return;
viv->sb_pb = pb;
avio_r8(pb); // 'S'
avio_r8(pb); // 'B'
ffio_read_varlen(pb); // size
avio_r8(pb); // junk
ffio_read_varlen(pb); // first packet
viv->n_sb_entries = viv->sb_blocks[viv->current_sb].n_packets;
for (i = 0; i < viv->n_sb_entries; i++) {
viv->sb_entries[i].size = ffio_read_varlen(pb);
viv->sb_entries[i].flag = avio_r8(pb);
}
ffio_read_varlen(pb);
avio_r8(pb);
viv->current_sb_entry = 0;
}
static int viv_read_header(AVFormatContext *s)
{
VividasDemuxContext *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;
int ret;
avio_skip(pb, 9);
header_end = avio_tell(pb);
header_end += ffio_read_varlen(pb);
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);
for (;;) {
int64_t here = avio_tell(pb);
int block_len, block_type;
if (here >= header_end)
break;
block_len = ffio_read_varlen(pb);
if (avio_feof(pb) || block_len <= 0)
return AVERROR_INVALIDDATA;
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);
ret = track_header(viv, s, buf, v);
av_free(buf);
if (ret < 0)
return ret;
buf = read_vblock(pb, &v, key, &k2, v);
if (!buf)
return AVERROR(EIO);
ret = track_index(viv, s, buf, v);
av_free(buf);
if (ret < 0)
return ret;
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)
{
VividasDemuxContext *viv = s->priv_data;
AVIOContext *pb;
int64_t off;
int ret;
if (!viv->sb_pb)
return AVERROR(EIO);
if (avio_feof(viv->sb_pb))
return AVERROR_EOF;
if (viv->current_audio_subpacket < viv->n_audio_subpackets) {
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;
ret = av_get_packet(pb, pkt, size);
if (ret < 0)
return ret;
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 = av_rescale_q(viv->audio_sample, av_make_q(1, astream->codecpar->sample_rate), astream->time_base);
viv->audio_sample += viv->audio_subpackets[viv->current_audio_subpacket].pcm_bytes / 2 /
astream->codecpar->ch_layout.nb_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;
if (!pb)
return AVERROR(EIO);
off = avio_tell(pb);
if (viv->current_sb_entry >= viv->n_sb_entries)
return AVERROR_INVALIDDATA;
off += viv->sb_entries[viv->current_sb_entry].size;
if (viv->sb_entries[viv->current_sb_entry].flag == 0) {
uint64_t v_size = ffio_read_varlen(pb);
int last = 0, last_start;
if (!viv->num_audio)
return AVERROR_INVALIDDATA;
ffio_read_varlen(pb);
if (v_size > INT_MAX || !v_size)
return AVERROR_INVALIDDATA;
ret = av_get_packet(pb, pkt, v_size);
if (ret < 0)
return ret;
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 (int i = 0; i < MAX_AUDIO_SUBPACKETS - 1; i++) {
int start, pcm_bytes;
start = ffio_read_varlen(pb);
pcm_bytes = ffio_read_varlen(pb);
if (i > 0 && start == 0)
break;
if (start < last)
return AVERROR_INVALIDDATA;
viv->n_audio_subpackets = i + 1;
last =
viv->audio_subpackets[i].start = start;
viv->audio_subpackets[i].pcm_bytes = pcm_bytes;
}
last_start =
viv->audio_subpackets[viv->n_audio_subpackets].start = (int)(off - avio_tell(pb));
if (last_start < last)
return AVERROR_INVALIDDATA;
viv->current_audio_subpacket = 0;
} else {
uint64_t v_size = ffio_read_varlen(pb);
if (v_size > INT_MAX || !v_size)
return AVERROR_INVALIDDATA;
ret = av_get_packet(pb, pkt, v_size);
if (ret < 0)
return ret;
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++;
return 0;
}
static int viv_read_close(AVFormatContext *s)
{
VividasDemuxContext *viv = s->priv_data;
av_freep(&viv->sb_pb);
av_freep(&viv->sb_buf);
av_freep(&viv->sb_blocks);
av_freep(&viv->sb_entries);
return 0;
}
static int viv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
{
VividasDemuxContext *viv = s->priv_data;
int64_t frame;
if (stream_index == 0)
frame = timestamp;
else
frame = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[stream_index]->time_base);
for (int i = 0; i < viv->n_sb_blocks; i++) {
if (frame >= viv->sb_blocks[i].packet_offset && frame < viv->sb_blocks[i].packet_offset + viv->sb_blocks[i].n_packets) {
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);
if (viv->num_audio) {
const AVCodecParameters *par = s->streams[1]->codecpar;
// flush audio packet queue
viv->current_audio_subpacket = 0;
viv->n_audio_subpackets = 0;
// most problematic part: guess audio offset
viv->audio_sample = av_rescale_q(viv->sb_blocks[i].packet_offset,
av_make_q(par->sample_rate, 1),
av_inv_q(s->streams[0]->time_base));
// hand-tuned 1.s a/v offset
viv->audio_sample += par->sample_rate;
}
viv->current_sb_entry = 0;
return 1;
}
}
return 0;
}
const AVInputFormat ff_vividas_demuxer = {
.name = "vividas",
.long_name = NULL_IF_CONFIG_SMALL("Vividas VIV"),
.priv_data_size = sizeof(VividasDemuxContext),
.flags_internal = FF_FMT_INIT_CLEANUP,
.read_probe = viv_probe,
.read_header = viv_read_header,
.read_packet = viv_read_packet,
.read_close = viv_read_close,
.read_seek = viv_read_seek,
};