/*
 *  Copyright (C) 2005 Gabest
 *  http://www.gabest.org
 *
 *  This file format is free to use as long as its specification is not
 *  modified or extended without the permission of Gabest. Suggestions
 *  can be sent in email or posted to the forum at sf.net/projects/guliverkli.
 *
 */

-----------------------
The .dsm file structure
-----------------------

FileInfo + Header Packets + Samples + Footer Packets

Header & Footer Packets:
- Required: MediaType
- Optional: StreamInfo, Chapters, SyncPoints, Resource

Notes:
- SyncPoints is optional because seeking can be performed simply by searching for packet syncpoints and their timestamps.
- This layout is fine for streaming. On connection send everything up to Sample packets, build the graph at the other end, then the rest when playing. (TODO: introduce NewSegment packet, to support seeking over network)
- The resolution of timestamp and duration is 100ns.
- Strings are zero terminated utf-8 strings.
- Date format string: "%Y-%m-%d %H:%M:%S". (GMT)
- Byte order is big-endian

Definitions
-----------

DSMF_VERSION    = 0x01

DSMSW           = 44534D53 ("DSMS")
DSMSW_SIZE      = 4

DSMP_FILEINFO   = 0
DSMP_STREAMINFO = 1
DSMP_MEDIATYPE  = 2
DSMP_CHAPTERS   = 3
DSMP_SAMPLE     = 4
DSMP_SYNCPOINTS = 5
DSMP_RESOURCE   = 6

Packet
------

DSMSW (DSMSW_SIZE bytes) (DirectShow Media SyncWord)
DSMP_* (5 bits)
data size length (3 bits -> 1-8 bytes)
data size (1-8 bytes)

[... data ...]

FileInfo : extends Packet (DSMP_FILEINFO)
-----------------------------------------

version (1 byte, DSMF_VERSION)

... repeated n times ...

id (4 bytes, alphanum)
string

... repeated n times ...

Notes:
- Parsers should not open files with higher "version" than they were compiled for.
- Suggested values of "id":
    "TITL": Title
    "AUTH": Author
    "RTNG": Rating
    "CPYR": Copyright
    "DESC": Description
    "APPL": Application
    "MUXR": Muxer
    "DATE": Encoding date
    ... more to be defined ...

MediaType : extends Packet (DSMP_MEDIATYPE)
-------------------------------------------

stream id (1 byte)

majortype (sizeof(GUID))
subtype (sizeof(GUID))
bFixedSizeSamples (1 bit)
bTemporalCompression (1 bit)
lSampleSize (30 bit)
formattype (sizeof(GUID))

[... format data ...]

Notes:
- Multiple MediaTypes per stream is NOT allowed
- bFixedSizeSamples, bTemporalCompression, lSampleSize are only there to preserve compatibility with dshow's media type structure, they aren't playing a role in anything really.

StreamInfo : extends Packet (DSMP_STREAMINFO)
---------------------------------------------

stream id (1 byte)

... repeated n times ...

id (4 bytes, alphanum)
string

... repeated n times ...

Notes:
- Suggested values of "id":
    "NAME": Stream name
    "SGRP": Stream group (groupped streams can be useful if the splitter is able to group and switch between them, but it's not a strict requirement towards dsm splitters)
    "LANG": Language code (ISO 639-2)
    "DESC": Description
    ... more to be defined ...

Chapters : extends Packet (DSMP_CHAPTERS)
-----------------------------------------

... repeated n times ...

timestamp delta sign (1 bit, <0?)
timestamp delta length (3 bits -> 0-7 bytes)
reserved (4 bits)
timestamp delta (0-7 bytes)
string

... repeated n times ...

Notes:
- "timestamp delta" holds the difference to the previous value, starts at 0.

Sample : extends Packet (DSMP_SAMPLE)
-------------------------------------

stream id (1 byte)

syncpoint (1 bit)
timestamp sign (1 bit, <0?)
timestamp length (3 bits -> 0-7 bytes)
duration length (3 bits -> 0-7 bytes)

timestamp (0-7 bytes)
duration (0-7 bytes)

[... data ...]

Notes:
- sign == 1 && timestamp length == 0 -> timestamp and duration is unknown (but for syncpoints it cannot be unknown!)
- sign == 0 && timestamp length == 0 -> simply means the value is stored on zero bytes and its value is zero too.
- timestamps of syncpoints must be strictly in increasing order.
- timestamps can be negative (to allow cutting a file at anywhere, preroll samples may need to be saved)

SyncPoints : extends Packet (DSMP_SYNCPOINTS)
---------------------------------------------

... repeated n times ...

timestamp delta sign (1 bit, <0?)
timestamp delta length (3 bits -> 0-7 bytes)
file position delta length (3 bits -> 0-7 bytes)
reserved (1 bit)

timestamp delta (0-7 bytes)
file position delta (0-7 bytes)

... repeated n times ...

Notes:
- "timestamp delta" / "file position delta" holds the difference to the previous value, both start at 0.

The algorithm of SyncPoints generation
--------------------------------------

First example:

stream 1: 1,5,8 (video)
stream 2: 2,3,6,7,9 (audio)
stream 3: 4 (subtitle)

1 ----|               1->2   1      +2        -> 1 (t 1, fp 1)
      |---- 2         2->3   1,2    +3 -2     -> 1
      |---- 3         3->4   1,3    +4        -> 1
    +-|-- 4 (start)   4->5   1,3,4  +5 -1     -> 1
5 --|-|               5->6   3,4,5  +6 -3     -> 3 (t 5, fp 3)
    | |---- 6         6->7   4,5,6  +7 -6     -> 4 (t 6, fp 4)
    | |---- 7         7->8   4,5,7  +8 -7 -4  -> 4
    +-|-- 4 (stop)
      |---- 8         8->9   5,8    +9 -5     -> 5 (t 8, fp 5)
9 ----|               9->10  8,9   +10 -8     -> 8 (t 9, fp 8)
      |---- 10        10->   9,10             -> 9 (t 10, fp 9)

Notice how it is the values of the first and last elements of the queue are used.

In the end it represents the following: (timestamp ranges mapped to file positions)

1->5:  [1]
5->6:  [3]
6->8:  [4]
8->9:  [5]
9->10: [8]
10->:  [9]

Example usage:

Seeking to 7 would mean we need to start decoding at the file position of 4, which
makes sure we hit at least one syncpoint from every stream (4,5,7 and 6 too, but 6
can be skipped) until we reach 7.

---

Second example:

This is going to be a bit more complicated case. (I hope you like my ascii art :)

stream 1: 1,4,5,6,7 (video)
stream 2: 2,3 (subtitle)

1 -----|              1->2   1      +2        -> 1 (t 1, fp 1)
     +-|--- 2 (start) 2->3   1,2   (+3 NOT!)  -> 1
   +-|-|- 3 (start)   3->4   1,2    +4 -1     -> 1
4 -|-|-|              4->5   2,4    +5 -4     -> 2 (t 4, fp 2)
5 -|-|-|              5->6   2,5    +6 -5 -2  -> 2
   +-|-|- 3 (stop)
     +-|--- 2 (stop)
6 -----|              6->7   6      +7 -6     -> 6 (t 6, fp 6)
7 -----|              7->    7                -> 7 (t 7, fp 7)

The problem with subtitles that they are discontinous, overlapped and can totally hide
other syncpoints, just like 2 hides 4, 5 and even 3 fully (which requires special handling,
see "NOT!"). That means such a subtitle, when it is too long, can influence seeking time
by a lot. It might be wise and worth limiting the duration of samples to a couple of minutes,
possibly sacrificing a bit of correctness by it. Splitters can also choose to ignore the
suggested seek position, when it falls too far from the required, and go on searching the
stream for syncpoints themselves.

Resource : extends Packet (DSMP_RESOURCE)
-----------------------------------------

compression type (2 bits, 0: none, 1: gzip, 2-3: reserved)
reserved (6 bits)
name string
desc string
mime string

[... data ...]
