/* * probe_ogg.c * * Copyright (C) Tilmann Bitterberg, July 2002 * Based heavily on code by Moritz Bunkus for ogminfo from * http://www.bunkus.org/videotools/ogmtools/index.html * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "transcode.h" #include "tcinfo.h" #include "ioaux.h" #include "tc.h" #include "libtc/libtc.h" #include "libtc/ratiocodes.h" #include #if (HAVE_OGG && HAVE_VORBIS) #include #include #ifdef HAVE_THEORA #include #endif #include "ogmstreams.h" #define MAX_AUDIO_TRACKS 255 #define MAX_VIDEO_TRACKS 255 #define BLOCK_SIZE 4096 //#define OGM_DEBUG struct demux_t { int serial; int fd; int vorbis; ogg_stream_state state; }; enum { none, Vorbis, Theora, DirectShow, StreamHeader }; static int ogm_packet_type (ogg_packet pack) { if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "vorbis", 6)) return Vorbis; else if ((pack.bytes >= 7) && ! strncmp(&pack.packet[1], "theora", 6)) return Theora; else if ((pack.bytes >= 142) && !strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg", 35) ) return DirectShow; else if (((*pack.packet & OGM_PACKET_TYPE_BITS ) == OGM_PACKET_TYPE_HEADER) && (pack.bytes >= (int)sizeof(ogm_stream_header) + 1)) return StreamHeader; return none; } void probe_ogg(info_t *ipipe) { ogg_sync_state sync; ogg_page page; ogg_packet pack; char *buf; int nread, np, sno, nvtracks = 0, natracks = 0, i, idx; //int endofstream = 0, k, n; struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS]; int fdin = -1; char vid_codec[5]; ogm_stream_header *sth; fdin = ipipe->fd_in; if (fdin == -1) { tc_log_error(__FILE__, "Could not open file."); goto ogg_out; } ipipe->probe_info->magic=TC_MAGIC_OGG; memset(streams, 0, sizeof(streams)); for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++) streams[i].serial = -1; ogg_sync_init(&sync); while (1) { np = ogg_sync_pageseek(&sync, &page); if (np < 0) { tc_log_error(__FILE__, "ogg_sync_pageseek failed"); goto ogg_out; } if (np == 0) { buf = ogg_sync_buffer(&sync, BLOCK_SIZE); if (!buf) { tc_log_error(__FILE__, "ogg_sync_buffer failed"); goto ogg_out; } if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) { } ogg_sync_wrote(&sync, nread); continue; } if (!ogg_page_bos(&page)) { break; } else { ogg_stream_state sstate; vorbis_info *inf = tc_malloc (sizeof(vorbis_info)); vorbis_comment *com = tc_malloc (sizeof(vorbis_comment)); if (!inf || !com) { tc_log_error(__FILE__, "Out of Memory at %d", __LINE__); goto ogg_out; } sno = ogg_page_serialno(&page); if (ogg_stream_init(&sstate, sno)) { tc_log_error(__FILE__, "ogg_stream_init failed"); goto ogg_out; } ogg_stream_pagein(&sstate, &page); ogg_stream_packetout(&sstate, &pack); switch (ogm_packet_type(pack)) { case Vorbis: vorbis_info_init(inf); vorbis_comment_init(com); if(vorbis_synthesis_headerin(inf, com, &pack) < 0) { tc_log_warn(__FILE__, "Could not decode vorbis header " "packet - invalid vorbis stream ()"); } else { #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; " "rate: %ldHz, channels: %d, bitrate %3.2f kb/s", natracks + 1, natracks + nvtracks + 1, inf->rate, inf->channels, (double)inf->bitrate_nominal/1000.0); #endif ipipe->probe_info->track[natracks].samplerate = inf->rate; ipipe->probe_info->track[natracks].chan = inf->channels; ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/ ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS; ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[natracks].serial = sno; streams[natracks].vorbis = 1; ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate)); natracks++; } break; #ifdef HAVE_THEORA case Theora: { theora_info ti; theora_comment tc; theora_decode_header(&ti, &tc, &pack); ipipe->probe_info->width = ti.width; ipipe->probe_info->height = ti.height; ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator; tc_frc_code_from_ratio(&(ipipe->probe_info->frc), ti.fps_numerator, ti.fps_denominator); ipipe->probe_info->codec=TC_CODEC_THEORA; idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; break; } #endif case DirectShow: if ((*(int32_t*)(pack.packet+96) == 0x05589f80) && (pack.bytes >= 184)) { tc_log_warn(__FILE__, "(v%d/%d) Found old video " "header. Not supported.", nvtracks + 1, natracks + nvtracks + 1); } else if (*(int32_t*)pack.packet+96 == 0x05589F81) { tc_log_warn(__FILE__, "(a%d/%d) Found old audio " "header. Not supported.", natracks + 1, natracks + nvtracks + 1); } break; case StreamHeader: sth = (ogm_stream_header *)(pack.packet + 1); if (!strncmp(sth->streamtype, "video", 5)) { #ifdef OGM_DEBUG unsigned long codec; codec = (sth->subtype[0] << 24) + (sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3]; tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d " "codec: %p (%c%c%c%c)", nvtracks + 1, natracks + nvtracks + 1, (double)10000000 / (double)sth->time_unit, sth->sh.video.width, sth->sh.video.height, (void *)codec, sth->subtype[0], sth->subtype[1], sth->subtype[2], sth->subtype[3]); #endif vid_codec[0] = sth->subtype[0]; vid_codec[1] = sth->subtype[1]; vid_codec[2] = sth->subtype[2]; vid_codec[3] = sth->subtype[3]; vid_codec[4] = '\0'; //ipipe->probe_info->frames = AVI_video_frames(avifile); ipipe->probe_info->width = sth->sh.video.width; ipipe->probe_info->height = sth->sh.video.height; ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit; tc_frc_code_from_value(&(ipipe->probe_info->frc), ipipe->probe_info->fps); ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten if(strlen(vid_codec)==0) { ipipe->probe_info->codec=TC_CODEC_RGB; } else { if(strcasecmp(vid_codec,"dvsd")==0) ipipe->probe_info->codec=TC_CODEC_DV; if(strcasecmp(vid_codec,"DIV3")==0) ipipe->probe_info->codec=TC_CODEC_DIVX3; if(strcasecmp(vid_codec,"DIVX")==0) ipipe->probe_info->codec=TC_CODEC_DIVX4; if(strcasecmp(vid_codec,"DX50")==0) ipipe->probe_info->codec=TC_CODEC_DIVX5; if(strcasecmp(vid_codec,"XVID")==0) ipipe->probe_info->codec=TC_CODEC_XVID; if(strcasecmp(vid_codec,"MJPG")==0) ipipe->probe_info->codec=TC_CODEC_MJPEG; } idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; } else if (!strncmp(sth->streamtype, "audio", 5)) { int codec; char buf[5]; ac_memcpy(buf, sth->subtype, 4); buf[4] = 0; codec = strtoul(buf, NULL, 16); #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per " "sample: %d channels: %hd samples per second: %ld " "avgbytespersec: %hd blockalign: %d", natracks + 1, natracks + nvtracks + 1, codec, codec, codec == 0x1 ? "PCM" : codec == 55 ? "MP3" : codec == 0x55 ? "MP3" : codec == 0x2000 ? "AC3" : "unknown", sth->bits_per_sample, sth->sh.audio.channels, (long)sth->samples_per_unit, sth->sh.audio.avgbytespersec, sth->sh.audio.blockalign); #endif idx = natracks; ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit; ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels; ipipe->probe_info->track[natracks].bits = (sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample; ipipe->probe_info->track[natracks].format = codec; ipipe->probe_info->track[natracks].bitrate = 0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); natracks++; } else { tc_log_warn(__FILE__, "(%d) found new header of unknown/" "unsupported type\n", nvtracks + natracks + 1); } break; case none: tc_log_warn(__FILE__, "OGG stream %d is of an unknown type " "(bad header?)", nvtracks + natracks + 1); break; } /* switch type */ free(inf); free(com); ogg_stream_clear(&sstate); } /* beginning of page */ } /* while (1) */ ogg_out: //close(fdin); return; } #else // (HAVE_OGG && HAVE_VORBIS) void probe_ogg(info_t *ipipe) { tc_log_error(__FILE__, "No support for Ogg/Vorbis compiled in"); ipipe->probe_info->codec=TC_CODEC_UNKNOWN; ipipe->probe_info->magic=TC_MAGIC_UNKNOWN; } #endif