summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2010-03-02 22:13:23 (UTC)
committer Michael Krelin <hacker@klever.net>2010-03-08 00:14:30 (UTC)
commit02ba82674ce4c5983130a2501169e013b70a95fa (patch) (unidiff)
tree381eead49cedf960378a7d418fe7ac957d704afb
parentce549cc7cc156ff1d6e15bcceb6c6178154af459 (diff)
downloadiii-02ba82674ce4c5983130a2501169e013b70a95fa.zip
iii-02ba82674ce4c5983130a2501169e013b70a95fa.tar.gz
iii-02ba82674ce4c5983130a2501169e013b70a95fa.tar.bz2
introducing iii-extract-riff-chunk utility
for getting the timestamp out of Nikon AVIs Signed-off-by: Michael Krelin <hacker@klever.net>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/iii-extract-riff-chunk.cc246
3 files changed, 251 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
index 425033a..f1c5466 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -9,2 +9,3 @@ soapeyefiService.h
9/iiid 9/iiid
10/COPYING.cc 10/COPYING.cc
11/iii-extract-riff-chunk
diff --git a/src/Makefile.am b/src/Makefile.am
index b5b7d5c..b31bed5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,3 @@
1bin_PROGRAMS=iii-extract-riff-chunk
1sbin_PROGRAMS=iiid 2sbin_PROGRAMS=iiid
2noinst_HEADERS = \ 3noinst_HEADERS = \
@@ -18,4 +19,7 @@ nodist_iiid_SOURCES = \
18iiid_LDADD = ${MODULES_LIBS} ${UUID_LIBS} 19iiid_LDADD = ${MODULES_LIBS} ${UUID_LIBS}
19 20
21iii_extract_riff_chunk_SOURCES = iii-extract-riff-chunk.cc
22nodist_iii_extract_riff_chunk_SOURCES = COPYING.cc
23
20COPYING.cc: ${top_srcdir}/COPYING 24COPYING.cc: ${top_srcdir}/COPYING
21 echo "const char * COPYING = " >$@ || (rm $@;exit 1) 25 echo "const char * COPYING = " >$@ || (rm $@;exit 1)
diff --git a/src/iii-extract-riff-chunk.cc b/src/iii-extract-riff-chunk.cc
new file mode 100644
index 0000000..3a06db2
--- a/dev/null
+++ b/src/iii-extract-riff-chunk.cc
@@ -0,0 +1,246 @@
1/* vim:set sw=8 nosi noai cin cino=:0,l1,g0: */
2#include <stdint.h>
3#include <stdlib.h>
4#include <getopt.h>
5#include <cstring>
6#include <iostream>
7#include <fstream>
8#include <stdexcept>
9#include <cassert>
10#include <list>
11#include <string>
12#include <iterator>
13
14#include "config.h"
15
16#define PHEADER \
17 PACKAGE " Version " VERSION "\n" \
18 "Copyright (c) 2009-2010 Klever Group"
19
20typedef uint32_t fourcc_type;
21enum fourcc_value {
22 fourcc_RIFF = 0x46464952, fourcc_AVI = 0x20495641, fourcc_LIST = 0x5453494c,
23 fourcc_hdrl = 0x6c726468, fourcc_strl = 0x6c727473,
24 fourcc_ncdt = 0x7464636e, fourcc_ncvr = 0x7276636e, fourcc_nctg = 0x6774636e, fourcc_ncth = 0x6874636e
25};
26
27fourcc_type str2fourcc(const char *str) {
28 fourcc_type rv = 0;
29 return *(fourcc_type*)strncpy((char*)&rv,str,sizeof(rv));
30}
31const std::string fourcc2str(fourcc_type fcc) {
32 char rv[sizeof(fcc)+1];
33 *(fourcc_type*)rv = fcc;
34 rv[sizeof(fcc)]=0;
35 return rv;
36}
37
38#pragma pack(1)
39struct riff_sized_head {
40 fourcc_type fourcc;
41 uint32_t size;
42};
43#pragma pack()
44
45class exceptional_success : public std::exception { };
46
47struct riff {
48 struct chunk_type {
49 riff_sized_head head;
50 uint32_t left;
51
52 chunk_type(riff& r) {
53 r.read(&head);
54 left = head.size;
55 }
56 };
57
58 std::istream *in;
59 typedef std::list<chunk_type> chunks_type;
60 chunks_type chunks;
61
62 riff(std::istream& i) : in(&i) { }
63
64 protected:
65
66 int begin_chunk() {
67 chunks.push_back( chunk_type(*this) );
68 return chunks.size();
69 }
70 void end_chunk(int level) {
71 assert(in); assert(chunks.size()==level);
72 std::streamsize o = chunks.back().left;
73 chunks.pop_back();
74 if(!o) return;
75 in->seekg(o,std::ios::cur);
76 for(chunks_type::iterator i=chunks.begin(),ie=chunks.end() ;i!=ie; (i++)->left -= o) ;
77 }
78
79 void read(void *p,size_t n) {
80 assert(in);
81 if( (!chunks.empty()) && chunks.back().left < n)
82 throw std::runtime_error("attempt to read beyond the end of chunk");
83 if(!in->read((char*)p,n))
84 throw std::runtime_error("failed to read()");
85 std::streamsize gc = in->gcount();
86 for(chunks_type::iterator i=chunks.begin(),ie=chunks.end();i!=ie;++i) {
87 i->left -= gc; assert(i->left >= 0);
88 }
89 if(gc!=n) throw std::runtime_error("incomplete read()");
90 }
91
92 template<typename T> void read(T* v) { read(v,sizeof(*v)); }
93
94 friend class scoped_chunk;
95
96};
97
98struct scoped_chunk {
99 riff& rs;
100 int level;
101 riff::chunks_type::reverse_iterator chunk_iterator;
102
103 scoped_chunk(riff& rs_) : rs(rs_), level(rs.begin_chunk()), chunk_iterator(rs.chunks.rbegin()) { }
104 ~scoped_chunk() { rs.end_chunk(level); }
105
106 riff::chunk_type& chunk() { return *chunk_iterator; }
107
108 fourcc_type get_chunk_id() { return chunk().head.fourcc; }
109 bool has(uint32_t bytes=1) { return chunk().left >= bytes; }
110
111 template<typename T> T read() { T rv; rs.read(&rv); return rv; }
112 template<typename T> void read(T& t) { rs.read(&t); }
113 void read(void *p,size_t n) { rs.read(p,n); }
114
115};
116
117struct chunk_path_type : public std::list<std::string> {
118
119 chunk_path_type() { }
120 chunk_path_type(const char *str) {
121 for(const char *p0=str,*p1=strchr(p0,'/');p0;p1=p1?strchr(p0=++p1,'/'):(p0=0))
122 if(p0!=p1)
123 push_back( p1? std::string(p0,p1-p0) : std::string(p0) );
124 }
125
126};
127
128struct chunk_walker {
129 riff& r;
130 chunk_path_type path;
131
132 chunk_walker(riff& r_) : r(r_) { }
133
134 void shoot(scoped_chunk& chunk) {
135 riff::chunk_type& c = chunk.chunk();
136 while(c.left) {
137 static char tmp[512];
138 int r = (c.left<sizeof(tmp)) ? c.left : sizeof(tmp) ;
139 chunk.read(tmp,r); std::cout.write(tmp,r);
140 }
141 if(first) throw exceptional_success();
142 }
143
144 void walk_chunks() {
145 scoped_chunk chunk(r);
146 std::string pc = fourcc2str(chunk.get_chunk_id());
147 bool list = false;
148 switch(chunk.get_chunk_id()) {
149 case fourcc_RIFF: case fourcc_LIST:
150 list = true;
151 pc += '.'; pc += fourcc2str(chunk.read<fourcc_type>());
152 break;
153 }
154 path.push_back(pc);
155 std::pair<chunk_path_type::const_iterator,chunk_path_type::const_iterator>
156 mm = std::mismatch(path.begin(),path.end(),target.begin());
157 bool dive = list;
158 if(mm.first==path.end()) {
159 if(mm.second==target.end()) {
160 shoot(chunk);
161 dive = false;
162 }
163 }else{
164 assert(mm.second!=target.end());
165 dive = false;
166 }
167
168 if(dive) while(chunk.has()) walk_chunks();
169 path.pop_back();
170 }
171
172
173 chunk_path_type target;
174 chunk_walker& set_target(chunk_path_type t) { target = t; return *this; }
175 bool first;
176 chunk_walker& set_oneshot(bool os) { first = true; return *this; }
177};
178
179void usage(char **argv) {
180 std::cerr << PHEADER << std::endl << std::endl
181 << ' ' << argv[0] << " [options] <avi-file> <chunk-path>" << std::endl
182 << std::endl <<
183 " -h, --help,\n"
184 " --usage display this text\n"
185 " -V, --version display version information\n"
186 " -L, --license show license\n"
187 "\n"
188 "Example: " << argv[0] << " DSC_0001.AVI '/RIFF.AVI /LIST.ncdt/nctg' \\\n"
189 "\t\t| dd bs=1 skip=82 count=19 2>/dev/null\n"
190 "Output: YYYY:MM:DD HH:MM:SS (for Nikon-recorded AVI)"
191 << std::endl << std::endl;
192 exit(0);
193}
194
195int main(int argc,char **argv) try {
196
197 bool first = false;
198
199 for(;;) {
200 static struct option opts[] = {
201 { "first", no_argument, 0, '1' },
202 { "help", no_argument, 0, 'h' },
203 { "usage", no_argument, 0, 'h' },
204 { "version", no_argument, 0, 'V' },
205 { "license", no_argument, 0, 'L' },
206 { NULL, 0, 0, 0 }
207 };
208 int c = getopt_long(argc,argv,"1hVL",opts,NULL);
209 if(c==-1) break;
210 switch(c) {
211 case 'h':
212 usage(argv); break;
213 case 'V':
214 std::cerr << VERSION << std::endl;
215 exit(0); break;
216 case 'L':
217 extern const char *COPYING;
218 std::cerr << COPYING << std::endl;
219 exit(0); break;
220 case '1':
221 first = true; break;
222 default:
223 std::cerr << "Huh?" << std::endl;
224 exit(1); break;
225 }
226 }
227
228 if(optind!=(argc-2)) usage(argv);
229
230 std::ifstream i(argv[optind],std::ios::in);
231 if(!i) {
232 std::cerr << "Failed to open file '" << argv[optind] << "'" << std::endl;
233 exit(1);
234 }
235 riff rs(i);
236 chunk_walker(rs)
237 .set_target(chunk_path_type(argv[++optind]))
238 .set_oneshot(first)
239 .walk_chunks();
240 return 0;
241}catch(exceptional_success&) {
242 return 0;
243}catch(std::exception &e) {
244 std::cerr << "oops: " << e.what() << std::endl;
245 return 1;
246}