1 /*
2 * probe_ogg.c
3 *
4 * Copyright (C) Tilmann Bitterberg, July 2002
5 * Based heavily on code by Moritz Bunkus for ogminfo from
6 * http://www.bunkus.org/videotools/ogmtools/index.html
7 *
8 * This file is part of transcode, a video stream processing tool
9 *
10 * transcode is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * transcode is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU Make; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 #include "transcode.h"
27 #include "tcinfo.h"
28 #include "ioaux.h"
29 #include "tc.h"
30 #include "libtc/libtc.h"
31 #include "libtc/ratiocodes.h"
32
33 #include <sys/mman.h>
34
35 #if (HAVE_OGG && HAVE_VORBIS)
36
37 #include <ogg/ogg.h>
38 #include <vorbis/codec.h>
39
40 #ifdef HAVE_THEORA
41 #include <theora/theora.h>
42 #endif
43
44 #include "ogmstreams.h"
45
46 #define MAX_AUDIO_TRACKS 255
47 #define MAX_VIDEO_TRACKS 255
48 #define BLOCK_SIZE 4096
49
50 //#define OGM_DEBUG
51
52 struct demux_t {
53 int serial;
54 int fd;
55 int vorbis;
56 ogg_stream_state state;
57 };
58
59 enum { none, Vorbis, Theora, DirectShow, StreamHeader };
60
ogm_packet_type(ogg_packet pack)61 static int ogm_packet_type (ogg_packet pack)
62 {
63 if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "vorbis", 6))
64 return Vorbis;
65 else if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "theora", 6))
66 return Theora;
67 else if ((pack.bytes >= 142) &&
68 !strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg", 35) )
69 return DirectShow;
70 else if (((*pack.packet & OGM_PACKET_TYPE_BITS ) == OGM_PACKET_TYPE_HEADER) &&
71 (pack.bytes >= (int)sizeof(ogm_stream_header) + 1))
72 return StreamHeader;
73
74 return none;
75 }
76
probe_ogg(info_t * ipipe)77 void probe_ogg(info_t *ipipe)
78 {
79 ogg_sync_state sync;
80 ogg_page page;
81 ogg_packet pack;
82 char *buf;
83 int nread, np, sno, nvtracks = 0, natracks = 0, i, idx;
84 //int endofstream = 0, k, n;
85 struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS];
86 int fdin = -1;
87 char vid_codec[5];
88 ogm_stream_header *sth;
89
90 fdin = ipipe->fd_in;
91
92 if (fdin == -1) {
93 tc_log_error(__FILE__, "Could not open file.");
94 goto ogg_out;
95 }
96
97 ipipe->probe_info->magic=TC_MAGIC_OGG;
98
99 memset(streams, 0, sizeof(streams));
100 for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++)
101 streams[i].serial = -1;
102
103 ogg_sync_init(&sync);
104
105 while (1) {
106 np = ogg_sync_pageseek(&sync, &page);
107 if (np < 0) {
108 tc_log_error(__FILE__, "ogg_sync_pageseek failed");
109 goto ogg_out;
110 }
111 if (np == 0) {
112 buf = ogg_sync_buffer(&sync, BLOCK_SIZE);
113 if (!buf) {
114 tc_log_error(__FILE__, "ogg_sync_buffer failed");
115 goto ogg_out;
116 }
117
118 if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) {
119 }
120 ogg_sync_wrote(&sync, nread);
121 continue;
122 }
123
124 if (!ogg_page_bos(&page)) {
125 break;
126 } else {
127 ogg_stream_state sstate;
128 vorbis_info *inf = tc_malloc (sizeof(vorbis_info));
129 vorbis_comment *com = tc_malloc (sizeof(vorbis_comment));
130
131 if (!inf || !com) {
132 tc_log_error(__FILE__, "Out of Memory at %d", __LINE__);
133 goto ogg_out;
134 }
135 sno = ogg_page_serialno(&page);
136 if (ogg_stream_init(&sstate, sno)) {
137 tc_log_error(__FILE__, "ogg_stream_init failed");
138 goto ogg_out;
139 }
140 ogg_stream_pagein(&sstate, &page);
141 ogg_stream_packetout(&sstate, &pack);
142
143 switch (ogm_packet_type(pack))
144 {
145 case Vorbis:
146 vorbis_info_init(inf);
147 vorbis_comment_init(com);
148
149 if(vorbis_synthesis_headerin(inf, com, &pack) < 0) {
150 tc_log_warn(__FILE__, "Could not decode vorbis header "
151 "packet - invalid vorbis stream ()");
152 } else {
153 #ifdef OGM_DEBUG
154 tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; "
155 "rate: %ldHz, channels: %d, bitrate %3.2f kb/s",
156 natracks + 1, natracks + nvtracks + 1, inf->rate,
157 inf->channels, (double)inf->bitrate_nominal/1000.0);
158 #endif
159
160 ipipe->probe_info->track[natracks].samplerate = inf->rate;
161 ipipe->probe_info->track[natracks].chan = inf->channels;
162 ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/
163 ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS;
164 ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0;
165
166 ipipe->probe_info->track[natracks].tid=natracks;
167 if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks;
168
169 streams[natracks].serial = sno;
170 streams[natracks].vorbis = 1;
171 ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate));
172 natracks++;
173 }
174 break;
175 #ifdef HAVE_THEORA
176 case Theora:
177 {
178 theora_info ti;
179 theora_comment tc;
180
181 theora_decode_header(&ti, &tc, &pack);
182
183 ipipe->probe_info->width = ti.width;
184 ipipe->probe_info->height = ti.height;
185 ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator;
186 tc_frc_code_from_ratio(&(ipipe->probe_info->frc),
187 ti.fps_numerator, ti.fps_denominator);
188
189 ipipe->probe_info->codec=TC_CODEC_THEORA;
190
191 idx = natracks + MAX_AUDIO_TRACKS;
192
193 streams[idx].serial = sno;
194 ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
195 nvtracks++;
196 break;
197 }
198 #endif
199 case DirectShow:
200 if ((*(int32_t*)(pack.packet+96) == 0x05589f80) &&
201 (pack.bytes >= 184)) {
202 tc_log_warn(__FILE__, "(v%d/%d) Found old video "
203 "header. Not supported.", nvtracks + 1,
204 natracks + nvtracks + 1);
205 } else if (*(int32_t*)pack.packet+96 == 0x05589F81) {
206 tc_log_warn(__FILE__, "(a%d/%d) Found old audio "
207 "header. Not supported.", natracks + 1,
208 natracks + nvtracks + 1);
209 }
210 break;
211 case StreamHeader:
212 sth = (ogm_stream_header *)(pack.packet + 1);
213
214 if (!strncmp(sth->streamtype, "video", 5)) {
215 #ifdef OGM_DEBUG
216 unsigned long codec;
217 codec = (sth->subtype[0] << 24) +
218 (sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3];
219 tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d "
220 "codec: %p (%c%c%c%c)", nvtracks + 1,
221 natracks + nvtracks + 1,
222 (double)10000000 / (double)sth->time_unit,
223 sth->sh.video.width, sth->sh.video.height, (void *)codec,
224 sth->subtype[0], sth->subtype[1], sth->subtype[2],
225 sth->subtype[3]);
226 #endif
227 vid_codec[0] = sth->subtype[0];
228 vid_codec[1] = sth->subtype[1];
229 vid_codec[2] = sth->subtype[2];
230 vid_codec[3] = sth->subtype[3];
231 vid_codec[4] = '\0';
232
233 //ipipe->probe_info->frames = AVI_video_frames(avifile);
234
235 ipipe->probe_info->width = sth->sh.video.width;
236 ipipe->probe_info->height = sth->sh.video.height;
237 ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit;
238 tc_frc_code_from_value(&(ipipe->probe_info->frc),
239 ipipe->probe_info->fps);
240
241 ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten
242
243 if(strlen(vid_codec)==0) {
244 ipipe->probe_info->codec=TC_CODEC_RGB;
245 } else {
246
247 if(strcasecmp(vid_codec,"dvsd")==0)
248 ipipe->probe_info->codec=TC_CODEC_DV;
249
250 if(strcasecmp(vid_codec,"DIV3")==0)
251 ipipe->probe_info->codec=TC_CODEC_DIVX3;
252
253 if(strcasecmp(vid_codec,"DIVX")==0)
254 ipipe->probe_info->codec=TC_CODEC_DIVX4;
255
256 if(strcasecmp(vid_codec,"DX50")==0)
257 ipipe->probe_info->codec=TC_CODEC_DIVX5;
258
259 if(strcasecmp(vid_codec,"XVID")==0)
260 ipipe->probe_info->codec=TC_CODEC_XVID;
261
262 if(strcasecmp(vid_codec,"MJPG")==0)
263 ipipe->probe_info->codec=TC_CODEC_MJPEG;
264 }
265
266 idx = natracks + MAX_AUDIO_TRACKS;
267
268 streams[idx].serial = sno;
269 ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
270 nvtracks++;
271 } else if (!strncmp(sth->streamtype, "audio", 5)) {
272 int codec;
273 char buf[5];
274 ac_memcpy(buf, sth->subtype, 4);
275 buf[4] = 0;
276 codec = strtoul(buf, NULL, 16);
277 #ifdef OGM_DEBUG
278 tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per "
279 "sample: %d channels: %hd samples per second: %ld "
280 "avgbytespersec: %hd blockalign: %d",
281 natracks + 1, natracks + nvtracks + 1,
282 codec, codec,
283 codec == 0x1 ? "PCM" : codec == 55 ? "MP3" :
284 codec == 0x55 ? "MP3" :
285 codec == 0x2000 ? "AC3" : "unknown",
286 sth->bits_per_sample, sth->sh.audio.channels,
287 (long)sth->samples_per_unit,
288 sth->sh.audio.avgbytespersec,
289 sth->sh.audio.blockalign);
290 #endif
291 idx = natracks;
292
293 ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit;
294 ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels;
295 ipipe->probe_info->track[natracks].bits =
296 (sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample;
297 ipipe->probe_info->track[natracks].format = codec;
298 ipipe->probe_info->track[natracks].bitrate = 0;
299
300 ipipe->probe_info->track[natracks].tid=natracks;
301
302
303 if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks;
304
305 streams[idx].serial = sno;
306 ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate));
307 natracks++;
308 } else {
309 tc_log_warn(__FILE__, "(%d) found new header of unknown/"
310 "unsupported type\n", nvtracks + natracks + 1);
311 }
312 break;
313 case none:
314 tc_log_warn(__FILE__, "OGG stream %d is of an unknown type "
315 "(bad header?)", nvtracks + natracks + 1);
316 break;
317 } /* switch type */
318 free(inf);
319 free(com);
320 ogg_stream_clear(&sstate);
321 } /* beginning of page */
322 } /* while (1) */
323 ogg_out:
324 //close(fdin);
325 return;
326 }
327
328 #else // (HAVE_OGG && HAVE_VORBIS)
329
probe_ogg(info_t * ipipe)330 void probe_ogg(info_t *ipipe)
331 {
332 tc_log_error(__FILE__, "No support for Ogg/Vorbis compiled in");
333 ipipe->probe_info->codec=TC_CODEC_UNKNOWN;
334 ipipe->probe_info->magic=TC_MAGIC_UNKNOWN;
335 }
336
337 #endif
338