Vividas VIV

From MultimediaWiki
Jump to: navigation, search

Overview

A Vividas file consists of one or more "tracks", each comprising one or more audio and/or video elementary streams.

Encryption

All data relating to a track is encoded using a 32-bit key given in obfuscated form in the file header.

Keys

The header contains, for each track, a 187-byte block, 32 bits of which form the key. The following C function will extract a key from such a block.

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,
};

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;
}

Decoding

To decode a data block with a given key, this C function can be used:

void decode(uint32_t *d, unsigned size, uint32_t key)
{
    uint32_t k = key;

    size >>= 2;

    while (size--) {
        *d++ ^= k;
        k += key;
    }
}

File Format

Data Types

The following data types are used in the syntax description below.

u(n) 
n-byte unsigned little endian integer.
v  
Variable-length integer, the 7 low bits of each byte being data bits, and the MSB indicating whether more bytes follow.
a(n) 
n-byte ASCII string.
z  
NUL-terminated ASCII string.
d(n) 
n bytes unspecified data.
'x'  
Literal byte with hex value x.
"s"  
Literal ASCII string.

Overall Structure

vividas_file() {
    signature                           a(7)
    version                             a(2)
    file_header()
    if (skin_size > 0) {
        skin()
    }
    for (i = 0; i < num_tracks; i++) {
        reset_key()
        track_header()
        track_index()
    }
    for (;;) {
        reset_key()
        sb_block()
    }
}
signature  
The string vividas.
version  
File version as two decimal digits. All known samples have the value 03.
file_header() 
Sets skin_size and num_tracks.
reset_key()  
The decoding key is reset to the value from the file_header.

File Header

file_header() {
    header_length                       v
    num_tracks                          u(1)
    for (i = 0; i < num_tracks; i++) {
        title_length                    u(1)
        track_title                     a(title_length)
        track_key                       d(187)
        track_header_length             d(4)
    }
    for (;;) {
        block_length                    v
        block_type                      u(1)
        if (block_type == 15) {
            key                         d(187)
            for (;;) {
                descriptor()
            }
        } else if (block_type == 22) {
            skin_key                    d(187)
            skin_size                   d(4)
        }
    }
}
header_length  
Total length of the file_header element in bytes.
num_tracks  
Number of tracks in the file.
title_length  
Number of bytes in the immediately following title.
track_title  
Title of track i.
track_key  
Scrambled key for track i.
track_header_length 
Total size in bytes of track_header and track_index for track i.
block_length  
Size in bytes of the following block, including this field.

Skin

skin() {
    length                              v
    'd'                                 u(1)
    for (;;) {    
        size                            v
        tag                             u(1)
        data                            d(size)
    }             
}
length 
Total size of skin data including length field.
size  
Size of data chunk.
tag  
Unique identifier for data chunk.
data  
XML or JPEG data.

Track Header

track_header() {
    track_header_length                 v
    '1'                                 u(1)
    val_1                               u(1)
    for (i = 0; i < val_1; i++) {
        val_2                           u(1)
        for (j = 0; j < val_2; j++) {
            val_3                       u(1)
            val_4                       u(1)
        }
    }
    num_streams                         u(1)
    size_1                              v
    '2'                                 u(1)
    num_video                           u(1)
    def_video                           u(1)
    for (i = 0; i < num_video; i++) {
        length                          v
        '3'                             u(1)
        val_7                           u(1)
        frame_rate_den                  u(4)
        frame_rate_num                  u(4)
        num_frames                      u(4)
        width                           u(2)
        height                          u(2)
        val_8                           u(1)
        val_9                           u(4)
    }
    size_2                              v
    '4'                                 u(1)
    num_audio                           u(1)
    def_audio                           u(1)
    for (i = 0; i < num_audio; i++) {
        length                          v
        '5'                             u(1)
        codec_id                        u(1)
        codec_subid                     u(2)
        channels                        u(2)
        sample_rate                     u(4)
        data_1                          d(10)
        len_2                           u(1)
        data_2                          d(len_2)
        junk                            d(1)
        val_13                          v
        '19'                            u(1)
        len_3                           v
        num_data                        u(1)
        for (i = 0; i < num_data; i++) {
            data_len[i]                 v
        }
        for (i = 0; i < num_data; i++) {
            data[i]                     d(data_len[i])
        }
    }
}
track_header_length 
Length in bytes of track header including this field. Matches element of same name in file_header.
num_streams  
Number of elementary streams in track.
size_1  
Number of bytes preceding video streams loop, including this field.
num_video  
Number of video streams.
def_video_stream  
Default video stream number.
size_2  
Number of bytes preceding audio streams loop, including this field.
num_audio  
Number of audio streams.
def_audio_stream  
Default audio stream number.
data[i]  
Vorbis (or speex?) header packets

Track Index

track_index() {
    length                              v
    'c'                                 u(1)
    num_sb_blocks                       v
    for (i = 0; i < count; i++) {
        size                            v
        num_chunks                      v
    }
}
length  
Length in bytes of track index.
num_sb_blocks 
Number of entries in following table.
size  
Size of a block.
num_chunks  
Number of chunks in block.

"SB" Block

Note
for some .viv files (like the ones used in the polish VOD service iplex) the key used to encrypt SB blocks is different from the track key, it is received from the server at runtime in an unknown form. It can however be recovered from the .viv file with a simple known-plaintext attack. We know that the first bytes after the track index will begin with "SB" followed by the size of the first SB block (available in the track index). By XOR-ing the expected first four bytes with the encrypted 4 bytes from the viv file, we recover the "secret" SB key.
sb_block() {
    "SB"                                a(2)
    size                                v
    junk                                d(1)
    start_chunk                         v
    do {
        chunk_size                      v
        flag                            u(1)
    } while (chunk_size != 0)
}
size 
Total size of this block.

The chunks for which flag is 0 have the following structure:

chunk_1() {
    video_length                        v
    audio_length                        v
    video_data                          d(video_length)
    do {
        start_of_packet[i]              v
        uncompressed_size[i]            v
    } while(end_of_packet != 0)

    for(int i=0;i<n_packets;i++)
        audio_data[i]                   d(start_of_packet[i+1] - start_of_packet[i])

}
video_data 
VP6-encoded video packet
audio_data 
multiple Vorbis(or speex?)-encoded audio packets
uncompressed_size 
the size (in bytes) of raw PCM samples produced from this audio packet after decompression

The chunks for which flag is 1 have the following structure:

chunk_1() {
    length                              v
    video_data                          d(length)
}
video_data 
VP6-encoded video packet

It seems that always the first chunk of a SB block is a keyframe, the others are not. A more reliable method to find the keyframes is to test the MSB of the first byte of a VP6-encoded packet - the bit is set for non-keyframes and cleared for keyframes.

Tools

A decrypter and parser is available from git://git.mansr.com/vividas (gitweb http://git.mansr.com/?p=vividas).

FFmpeg demuxer based on the above code is available here.

External Resources

Extended company and product description is available on Wikipedia.

"Vividas Licenses TrueMotion VP7 for Mass Distribution of High Quality Video" January 13th, 2005

Vividas - Open Source Acknowledgement