ogg subtitle stream format (was: Re: [MPlayer-dev-eng] [Patch] ogg stream selection via -aid and -vid)
Alexander Werth (gmx)
alexander.werth at gmx.de
Mon May 20 14:00:33 CEST 2002
Hello,
Am Mon, 2002-05-20 um 01.28 schrieb Arpi:
>
> > btw, what is still missing is a subtitle demuxer.
>
> do you knwo something about the format they are using?
> spu-like stuff, ascii text or something new?
I attached a delphi file someone sent me. I added a few comments to make
it easier to read.
It does extract subtitles from ogg megia streams.
syntax is:
oggdemux infile.ogm streamid outfile.srt
The good news are that the format is very simple and straight forward.
It's compareable to the ogg vorbis stream format just that it uses
subtitles as payload.
The subtitles itself are uncompressed.
> > --
> > OpenOffice 1.0.0 Evolution 1.0.4 Nautilus 1.1.14 Galeon 1.2.1
> > Gimp 1.2.3 Gnome 1.4 mplayer 0.90pre4 We are almost there.
>
> :)))
>
> btw, what's missing?
> i'm already "there" since 6 years, and since 1 years i didn't miss a
> single app.
>
I don't miss one either but I still can't recommend a linux system for a
typical office environment. But things are comming together and once all
the above programms are sufficently integrated it's worth a try.
(Even so this isn't the topic of the mail.)
--
OpenOffice 1.0.0 Evolution 1.0.4 Nautilus 1.1.14 Galeon 1.2.1
Gimp 1.2.3 Gnome 1.4 mplayer 0.90pre4 We are almost there.
-------------- next part --------------
program Project1;
{$APPTYPE CONSOLE}
{* This is a quick and dirty hack to demonstrate subtitles*}
{* It uses information from http://www.xiph.org/ogg/vorbis/doc/framing.html *}
{* http://tobias.everwicked.com/prog.htm *}
{* And a lot of guessing *}
uses
SysUtils,classes,strutils;
var memo1,memo2:TStringList;
fs:TFileStream;
buffer:array[0..65535] of byte;
counter,c2,c3,c4,lnum,tnum,tnum1,dnum:integer;
tt,p1,p2:string;
label blah;
begin
memo1:=TStringList.Create;
memo2:=TStringList.Create;
{* open file and read in overlapping 60000 byte chunks *}
fs:=TFileStream.Create(ParamStr(1),fmopenread);
c2:=fs.Size div 60000;
for counter:=0 to c2 do
begin
fs.Read(buffer,65536);
{* search a buffer *}
for c3:=0 to 60000 do
begin
{* Search for OggS with the right Stream ID (paramstr(2) and stream_structure_version *}
{* stream serial number
Ogg allows for seperate logical bitstreams to be mixed at page granularity in a physical bitstream. The most common case would be
sequential arrangement, but it is possible to interleave pages for two seperate bitstreams to be decoded concurrently. The serial
number is the means by which pages physical pages are associated with a particular logical stream. Each logical stream must have a
unique serial number within a physical stream:
byte value
14 0xXX LSB
15 0xXX
16 0xXX
17 0xXX MSB
*}
{* byte 5:
byte value
5 bitflags: 0x01: unset = fresh packet
set = continued packet
0x02: unset = not first page of logical bitstream
set = first page of logical bitstream (bos)
0x04: unset = not last page of logical bitstream
set = last page of logical bitstream (eos)
*}
if (buffer[c3] = $4f) and (buffer[c3+1] = $67) and (buffer[c3+2] = $67) and (buffer[c3+3] = $53) and (buffer[c3+4] = $00) and (buffer[c3+14] = strtoint(paramstr(2))) and ((buffer[c3+5] = $00) or (buffer[c3+5] = $04)) then
begin
tt:='';
{* Time Position:
PCM absolute position
(This is packed in the same way the rest of Ogg data is packed; LSb of LSB first. Note that the 'position' data specifies a 'sample'
number (eg, in a CD quality sample is four octets, 16 bits for left and 16 bits for right; in video it would be the frame number). The
position specified is the total samples encoded after including all packets finished on this page (packets begun on this page but
continuing on to thenext page do not count). The rationale here is that the position specified in the frame header of the last page
tells how long the PCM data coded by the bitstream is. A truncated stream will still return the proper number of samples that can
be decoded fully.
A special value of '-1' (in two's complement) indicates that no packets finish on this page.
byte value
6 0xXX LSB
7 0xXX
8 0xXX
9 0xXX
10 0xXX
11 0xXX
12 0xXX
13 0xXX MSB *}
memo2.add(inttostr(buffer[c3+9]*256*256*256+buffer[c3+8]*256*256+buffer[c3+7]*256+buffer[c3+6]));
{* The number of page segments is in byte 0x26 and is ignored *}
{* segment_table (containing packet lacing values)
The lacing values for each packet segment physically appearing in this page are listed in contiguous order.
byte value
27 0x00-0xff (0-255)
[...]
n 0x00-0xff (0-255, n=page_segments+26)
Total page size is calculated directly from the known header size and lacing values in the segment table. Packet data segments
follow immediately after the header.
*}
{* So in buffer[c3+27] we find the page size. *}
{* The content of the first three bytes is unknown and probably a subtitle headder. The first was always 88 for me. The ther two vary.*}
for c4:=3 to buffer[c3+27] do
begin
tt:=tt+char(buffer[c3+28+c4]);
end;
memo2.text:=memo2.Text+tt;
{* EOS ? *}
if (buffer[c3+5] = $04) then
begin
goto blah;
end;
end;
end;
fs.Position:=fs.Position-5536;
end;
blah:
{* done reading sub info *}
fs.Free;
{* Clean up a little *}
counter:=0;
while counter < memo2.Count do
begin
if (length(memo2[counter]) < 3) then
begin
memo2.Delete(counter);
end;
inc(counter);
end;
{* Unfortunatly the pattern matching at the start does also apply to the stream setup packet. *}
for counter:=0 to 9 do
Writeln(memo2[counter]);
Writeln('How many bad lines are there?');
Readln(tt);
c4:=strtoint(tt);
for counter:=1 to c4 do
memo2.Delete(0);
memo2.Add('0');
{* Convert to .srt format *}
lnum:=0;
while memo2.Count > 1 do
begin
dnum:=1;
inc(lnum);
memo1.Add(inttostr(lnum));
try
inc(dnum);
tnum:=strtoint(memo2[2]);
except on EConvertError do
begin
try
inc(dnum);
tnum:=strtoint(memo2[3]);
except on EConvertError do
begin
inc(dnum);
tnum:=strtoint(memo2[4]);
end;
end;
end;
end;
tnum1:=StrToInt(memo2[0]);
{* convert time *}
p1:=IfThen(length(inttostr(tnum1 div 3600000))=1,'0','')+inttostr(tnum1 div 3600000)+':'+IfThen(length(inttostr((tnum1 mod 3600000) div 60000))=1,'0','')+inttostr((tnum1 mod 3600000) div 60000)+':'+ifthen(length(inttostr(((tnum1 mod 3600000) mod 60000) div 1000))=1,'0','')+inttostr(((tnum1 mod 3600000) mod 60000) div 1000)+','+ifthen(length(inttostr(((tnum1 mod 3600000) mod 60000) mod 1000))=1,'00','')+ifthen(length(inttostr(((tnum1 mod 3600000) mod 60000) mod 1000))=2,'0','')+inttostr(((tnum1 mod 3600000) mod 60000) mod 1000);
p2:=IfThen(length(inttostr(tnum div 3600000))=1,'0','')+inttostr(tnum div 3600000)+':'+IfThen(length(inttostr((tnum mod 3600000) div 60000))=1,'0','')+inttostr((tnum mod 3600000) div 60000)+':'+ifthen(length(inttostr(((tnum mod 3600000) mod 60000) div 1000))=1,'0','')+inttostr(((tnum mod 3600000) mod 60000) div 1000)+','+ifthen(length(inttostr(((tnum mod 3600000) mod 60000) mod 1000))=1,'00','')+ifthen(length(inttostr(((tnum mod 3600000) mod 60000) mod 1000))=2,'0','')+inttostr(((tnum mod 3600000) mod 60000) mod 1000);
memo1.Add(p1+' --> '+p2);
memo1.Add(memo2[1]);
if dnum > 2 then memo1.Add(memo2[2]);
if dnum > 3 then memo1.Add(memo2[3]);
memo1.Add('');
while dnum+1 > 0 do
begin
dec(dnum);
memo2.Delete(0);
end;
end;
memo1.SaveToFile(ParamStr(3));
memo1.Free;
memo2.Free;
end.
More information about the MPlayer-dev-eng
mailing list