1 /*
2  * oggDump will dump out an ogg file either by packets or by pages
3  *
4  * Copyright (C) 2008 Joern Seger
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21 
22 #ifdef __WIN32
23 #define __GNU_LIBRARY__
24 #include "../win32/getopt_win.h"
25 #endif
26 
27 #include <iostream>
28 #include <map>
29 #include <vector>
30 #include <sstream>
31 #include <fstream>
32 #include <ostream>
33 #include <cstdlib>
34 #include <unistd.h>
35 
36 #include "fileRepository.h"
37 #include "rawMediaPacket.h"
38 #include "oggDecoder.h"
39 #include "oggEncoder.h"
40 #include "oggStreamDecoder.h"
41 #include "oggPacket.h"
42 #include "oggBOSExtractorFactory.h"
43 #include "streamSerializer.h"
44 #include "exception.h"
45 #include "log.h"
46 
47 struct OutputUnit {
48   OggEncoder     encoder;
49   FileRepository repository;
50 };
51 
printHelp(std::string programName)52 void printHelp(std::string programName)
53 {
54   logger.error() << "usage <"<<programName<<"> [options] file" << std::endl;
55   logger.error() << "Options are:\n"
56                  << " -h         : help screen        \n"
57                  << " -g         : dump pages         \n"
58                  << " -p         : dump packets       \n"
59                  << " -l <level> : information depth; default: 5 (most information)\n"
60                  << " -s         : promt for streams to dump\n"
61                  << " -o <file>  : output dump information to a file\n";
62 }
63 
dumpPacketof(std::string & file,uint8 dumpLevel,bool promptForStreams,std::string & outFilename)64 void dumpPacketof(std::string& file, uint8 dumpLevel, bool promptForStreams, std::string& outFilename)
65 {
66   /* open the first file to be read */
67   std::vector<StreamConfig> configList;
68   StreamSerializer serializer;
69 
70   std::ofstream outStream;
71 
72   /* if there is a filename given, write the data to this file */
73   if (!outFilename.empty())
74     outStream.open(outFilename.c_str());
75 
76   if (!serializer.open(file)) {
77     logger.error() << "Can not open file <" << file << ">\n";
78     exit(-1);
79   }
80 
81   /* read the stream configuration */
82   serializer.getStreamConfig(configList);
83 
84   std::vector<uint32> outputStreamNo;
85 
86   for (uint32 i(0); i<configList.size(); ++i) {
87     std::cout << "Config of stream No. "<<i<<"\n"
88               << "StreamType: ";
89     switch (configList[i].type) {
90     case OggType::kate:
91       std::cout << "kate\n";
92       break;
93     case OggType::theora:
94       std::cout << "theora\n";
95       break;
96     case OggType::vorbis:
97       std::cout << "vorbis\n";
98       break;
99     }
100 
101     std::cout << "serial No : 0x"<< std::hex << configList[i].serialNo;
102     if (configList[i].parameter)
103       //std::cout << configList[i].parameter->toString();
104 
105       if (promptForStreams) {
106         std::cout << "Dump this stream? (y/n) \n";
107         char answer;
108         std::cin >> answer;
109 
110         if (answer != 'Y' && answer != 'y')
111           outputStreamNo.push_back(configList[i].streamNo);
112 
113         std::cout << answer << "\n";
114       }
115   }
116 
117   OggPacket packet;
118   double _time;
119   bool print;
120 
121   while (serializer.available()) {
122 
123     _time = serializer.getNextPacket(packet);
124 
125     print = true;
126     for (uint32 i(0); i<outputStreamNo.size(); ++i)
127       if (outputStreamNo[i] == packet->getStreamNo())
128         print = false;
129 
130     if (!print)
131       continue;
132 
133     if (outFilename.empty()) {
134       std::cout << "\nTime: " << _time;
135       std::cout << packet->toString(dumpLevel);
136     } else {
137       outStream << "\nTime: " << _time;
138       outStream << packet->toString(dumpLevel);
139     }
140   }
141 }
142 
oggDumpCmd(int argc,char * argv[])143 int oggDumpCmd(int argc, char* argv[])
144 {
145 
146   /* default values
147    * for the command line arguments */
148 
149   uint8 dumpLevel(5);
150   std::string outFilename("");
151   bool dumpPages(false);
152   bool dumpPackets(false);
153   bool promptForStreams(false);
154 
155   std::string programName(argv[0]);
156 
157   int opt;
158   while ((opt = getopt(argc, argv, "hgpl:so:")) != EOF)
159 
160     switch (opt) {
161 
162     case 'h':
163       printHelp(argv[0]);
164       exit(-1);
165 
166     case 'g':
167       dumpPages = true;
168       break;
169 
170     case 'p':
171       dumpPackets = true;
172       break;
173 
174     case 's':
175       promptForStreams = true;
176       break;
177 
178     case 'o':
179       outFilename = std::string(optarg);
180       break;
181 
182     case 'l':
183       dumpLevel = atoi(optarg); // yes, I know the atoi bug
184       break;
185 
186     }
187 
188   argc -= optind;
189   argv += optind;
190 
191   std::string analysisFile;
192 
193   if (argc == 1)
194     analysisFile = std::string(argv[0]);
195   else {
196     printHelp(programName);
197     exit(-1);
198   }
199 
200   if ((!dumpPages) && (!dumpPackets)) {
201     logger.error() << "Specify whether you want to dump pages, packet or both by -g and/or -p\n";
202     exit(-1);
203   }
204 
205   if (dumpPackets) {
206     dumpPacketof(analysisFile, dumpLevel, promptForStreams, outFilename);
207     return(0);
208   }
209 
210   std::ofstream outStream;
211 
212   /* if there is a filename given, write the data to this file */
213   if (!outFilename.empty())
214     outStream.open(outFilename.c_str());
215 
216   /* open the m_repository
217    in this easy example, it is a simple file */
218   FileRepository repository(analysisFile, MediaUnit::read);
219 
220   OggDecoder oggDecoder;
221   std::map<uint32, OggStreamDecoder> oggStreamDecoderList;
222   std::vector<OggPage> bosPages;
223 
224   /* run through the m_repository until there is no data left */
225   while (!repository.isEndOfFile()) {
226 
227     RawMediaPacket rawDecoderPacket;
228 
229     /* extract a raw data bunch from the file .. */
230     repository >> rawDecoderPacket;
231 
232     /* .. and insert it into the ogg decoder */
233     oggDecoder << rawDecoderPacket;
234 
235     /* are there any complete ogg Pages available ? */
236     while (oggDecoder.isAvailable()) {
237 
238       OggPage oggPage;
239 
240       /* grap the next page */
241       oggDecoder >> oggPage;
242 
243       /* what ID has this page / what stream does this page belongs to */
244       uint32 serialID = oggPage->serialno();
245 
246       if (oggPage->isBOS()) {
247 
248         bool addPage(false);
249 
250         switch (OggBOSExtractorFactory::getStreamType(oggPage)) {
251 
252         case OggType::theora: {
253           std::cout << "Found theora stream with ID= 0x" << std::hex
254                     << serialID << std::dec << std::endl;
255           if (promptForStreams) {
256             std::cout << "Dump this stream? (y/n) \n";
257             char answer;
258             std::cin >> answer;
259             if (answer == 'Y' || answer == 'y')
260               addPage = true;
261             std::cout << answer << "\n";
262           } else
263             addPage = true;
264         }
265         break;
266 
267         case OggType::vorbis: {
268           std::cout << "Found vorbis stream with ID= 0x" << std::hex
269                     << serialID << std::dec << std::endl;
270           if (promptForStreams) {
271             std::cout << "Dump this stream? (y/n) ";
272             char answer;
273             std::cin >> answer;
274             if (answer == 'Y' || answer == 'y')
275               addPage = true;
276             std::cout << answer << "\n";
277           } else
278             addPage = true;
279 
280         }
281         break;
282 
283         case OggType::kate: {
284           std::cout << "Found kate stream with ID= 0x" << std::hex
285                     << serialID << std::dec << std::endl;
286           if (promptForStreams) {
287             std::cout << "Dump this stream? (y/n) ";
288             char answer;
289             std::cin >> answer;
290             if (answer == 'Y' || answer == 'y')
291               addPage = true;
292             std::cout << answer << "\n";
293           } else
294             addPage = true;
295 
296         }
297         break;
298 
299         default: {
300           std::cout << "Found unknown stream with ID= 0x" << std::hex
301                     << serialID << std::dec << std::endl;
302           if (promptForStreams) {
303             std::cout << "Dump this stream? (y/n) \n";
304             char answer;
305             std::cin >> answer;
306             if (answer == 'Y' || answer == 'y')
307               addPage = true;
308             std::cout << answer << "\n";
309           } else
310             addPage = true;
311         }
312         break;
313         }
314         if (addPage) {
315           oggStreamDecoderList[serialID] = OggStreamDecoder();
316           oggStreamDecoderList[serialID] << oggPage;
317           bosPages.push_back(oggPage);
318         }
319 
320       } else {
321 
322         /* does the user want to dump this stream */
323         if (oggStreamDecoderList.find(serialID) != oggStreamDecoderList.end()) {
324 
325           if (dumpPages) {
326 
327             std::string outputString;
328 
329             // are there any bos pages, then toString them first
330             if (!bosPages.empty()) {
331               for (uint32 j(0); j<bosPages.size(); ++j)
332                 outputString += bosPages[j]->toString(dumpLevel);
333               bosPages.clear();
334             }
335 
336             outputString += oggPage->toString(dumpLevel);
337 
338             if (outFilename.empty())
339               std::cout << outputString;
340             else
341               outStream << outputString;
342 
343           }
344 
345         }
346       }
347     }
348   }
349 
350   /* close all files */
351   repository.close();
352   if (!outFilename.empty())
353     outStream.close();
354 
355   return (0);
356 }
357 
main(int argc,char * argv[])358 int main(int argc, char* argv[])
359 {
360   //logger.setLevel(OggLog::LOG_DEBUG);
361   try {
362     return oggDumpCmd(argc, argv);
363   } catch (OggException & e) {
364     logger.error() << "Fatal error: " << e.what() << std::endl;
365     return -1;
366   }
367 }
368 
369