File: thtranscode.ml

package info (click to toggle)
ocaml-xiph 1.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 820 kB
  • sloc: ml: 4,494; ansic: 3,994; makefile: 3
file content (144 lines) | stat: -rw-r--r-- 4,557 bytes parent folder | download
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
(* 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 Ogg.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 dec, 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 settings =
    {
      Encoder.keyframe_frequency = None;
      vp3_compatible = None;
      soft_target = None;
      buffer_delay = None;
      speed = None;
    }
  in
  let comments = [("artitst", "test artist"); ("title", "test title")] in
  let t = Encoder.create info settings comments in
  let s_o_p (h, b) = h ^ b in
  Encoder.encode_header t os;
  out (s_o_p (Ogg.Stream.flush_page 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 ! *)
          match !latest_yuv with Some x -> x | None -> raise Internal_error)
  in
  let s_o_p (h, b) = h ^ b in
  Printf.printf "Starting transcoding loop !\n%!";
  begin
    try
      while true do
        let op = Encoder.encode_page enc os generator in
        let op = s_o_p op in
        out op
      done
    with Ogg.Not_enough_data -> ()
  end;
  List.iter (fun p -> out (s_o_p p)) (Ogg.Stream.terminate os);
  Unix.close fd;
  Gc.full_major ()