File: thtranscode.ml

package info (click to toggle)
ocaml-theora 0.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, stretch
  • size: 444 kB
  • ctags: 226
  • sloc: ansic: 589; ml: 459; makefile: 81; sh: 3
file content (153 lines) | stat: -rw-r--r-- 4,446 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(* Shamelessly inspired of http://theora.org/doc/libtheora-1.0beta1/ *)

exception No_theora

open Theora

let infile = ref "input.ogg"
let outfile = ref "output.ogg"
let debug = ref false

let quality = ref 40

let () =
  Arg.parse
    [
      "-d", Arg.Set debug, "Show debugging messages";
      "-o", Arg.Set_string outfile, "Output file";
      "-q", Arg.Set_int quality, "Quality of the compression";
      "-i", Arg.Set_string infile, "Input file";
    ]
    ignore
    "thranscode [options]"

let in_init () =
  let sync,fd = Ogg.Sync.create_from_file !infile in
  let rec fill os =
    let page = Ogg.Sync.read sync in
    try
      (* We drop pages which are not for us.. *)
      if Ogg.Page.serialno page =
         Ogg.Stream.serialno os
      then
        Ogg.Stream.put_page os page ;
    with
       | Ogg.Bad_data -> fill os (* Do not care about page that are not for us.. *)
  in
  (** Test wether the stream is theora *)
  let test_theora () =
    (** Get First page *)
    let page = Ogg.Sync.read sync in
    (** Check wether this is a b_o_s *)
    if not (Ogg.Page.bos page) then raise No_theora ;
    (** Create a stream with this ID *)
    let serial = Ogg.Page.serialno page in
    Printf.printf "Testing stream %nx\n" serial ;
    let os = Ogg.Stream.create ~serial () in
    Ogg.Stream.put_page os page ;
    let packet = Ogg.Stream.get_packet os in
    (** Test header. Do not catch anything, first page should be sufficient *)
    if not (Decoder.check packet) then
      raise Not_found;
    Printf.printf "Got a theora stream !\n" ;
    let dec = Decoder.create () in
    (** Decode headers *)
    let rec f packet =
      try
        Decoder.headerin dec packet
      with
        | Not_enough_data ->
          let rec g () =
             try
               let packet = Ogg.Stream.get_packet os in
               f packet
             with
               | Ogg.Not_enough_data -> fill os; g ()
          in
          g ()
    in
    let info,vendor,comments = f packet in
    serial,os,dec,info,vendor,comments
  in
  (** Now find a theora stream *)
  let rec init () =
    try
      test_theora ()
    with
      | Not_found ->
         ( Printf.printf "This stream was not theora..\n";
           init () )
      | No_theora ->
         ( Printf.printf "No theora stream was found..\n%!";
           raise No_theora )
  in
  let serial,os,t,info,vendor,comments = init () in
  Printf.printf
     "Ogg logical stream %nx is Theora %dx%d %.02f fps video\n"
     serial info.frame_width info.frame_height
     ((float_of_int info.fps_numerator) /. (float_of_int info.fps_denominator)) ;
  Printf.printf "Encoded frame content is %dx%d with %dx%d offset\n"
     info.picture_width info.picture_height info.picture_x
     info.picture_y ;
  Printf.printf "YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d\n"
     info.frame_width info.frame_height info.fps_numerator
     info.fps_denominator 'p'
     info.aspect_numerator info.aspect_denominator ;
  Printf.printf "Vendor: %s\n" vendor ;
  List.iter (fun (x,y) -> Printf.printf "%s: %s\n" x y) comments ;
  flush_all ();
  t,os,fill,info,fd

let out_init info =
  let oc = open_out !outfile in
  let out s = output_string oc s; flush oc in
  let os = Ogg.Stream.create () in
  let comments = ["artitst", "test artist"; "title", "test title"] in
  let t = Encoder.create info comments in
    Encoder.encode_header t os;
    out (Ogg.Stream.flush os);
    t,os,out

let () =
  let dec,is,fill,info,fd = in_init () in
  let info =
  {
    info with
      target_bitrate = 0;
      quality = !quality;
  }
  in
  let enc,os,out = out_init info in
  let latest_yuv = ref None in
  let rec generator () =
    try
      let yuv = Decoder.get_yuv dec is in
      latest_yuv := Some yuv ;
      yuv
    with
      | Ogg.Not_enough_data when not (Ogg.Stream.eos is) ->
         (fill is; generator ())
      | Duplicate_frame ->
         (* Got a duplicate frame, sending previous one ! *)
         begin
          match !latest_yuv with
            | Some x -> x
            | None   -> raise Internal_error
         end
  in
  Printf.printf "Starting transcoding loop !\n%!";
  begin
   try
    while true do
      let op = Encoder.encode_page enc os generator in
      let s_o_p (h,b) = h ^ b in
      let op = s_o_p op in
        out op
    done
   with
     | Ogg.Not_enough_data -> ()
  end ;
  Encoder.eos enc os;
  out (Ogg.Stream.flush os);
  Unix.close fd;
  Gc.full_major ()