1 // amf.cpp:  AMF (Action Message Format) rpc marshalling, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software Foundation, Inc
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 3 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 #include "GnashSystemNetHeaders.h"
22 #include "buffer.h"
23 #include "log.h"
24 #include "amf.h"
25 #include "amfutf8.h"
26 #include "utility.h"
27 #include "flv.h"
28 
29 #include <boost/detail/endian.hpp>
30 #include <string>
31 #include <vector>
32 #include <cmath>
33 #include <climits>
34 #include <cstdint>
35 
36 using gnash::log_debug;
37 using gnash::log_error;
38 
39 namespace cygnal
40 {
41 
Flv()42 Flv::Flv()
43 {
44 //    GNASH_REPORT_FUNCTION;
45     memcpy(&_header.sig, "FLV", 3);
46     _header.version = 1;
47     _header.type = Flv::FLV_VIDEO | Flv::FLV_AUDIO;
48 //    _header.head_size = 9;
49 
50     memset(&_tag, 0, sizeof(flv_tag_t));
51     _tag.type = Flv::TAG_METADATA;
52     _tag.bodysize[0] = 0x0f;
53     _tag.bodysize[1] = 0x30;
54     _tag.bodysize[2] = 0x00;
55 }
56 
~Flv()57 Flv::~Flv()
58 {
59 //    GNASH_REPORT_FUNCTION;
60 }
61 
62 // Encode the data into a Buffer
63 std::shared_ptr<cygnal::Buffer>
encodeHeader(std::uint8_t type)64 Flv::encodeHeader(std::uint8_t type)
65 {
66 //    GNASH_REPORT_FUNCTION;
67     std::shared_ptr<cygnal::Buffer> buf(new Buffer(sizeof(Flv::flv_header_t)));
68     buf->clear();
69 
70     std::uint8_t version = 0x1;
71     *buf = "FLV";
72     *buf += version;
73 
74     *buf += type;
75 
76     std::uint32_t size = htonl(0x9);
77     buf->append((std::uint8_t *)&size, sizeof(std::uint32_t));
78 
79     return buf;
80 }
81 
82 // Decode a Buffer into a header
83 std::shared_ptr<Flv::flv_header_t>
decodeHeader(std::uint8_t * data)84 Flv::decodeHeader(std::uint8_t *data)
85 {
86 //    GNASH_REPORT_FUNCTION;
87     std::shared_ptr<flv_header_t> header(new flv_header_t);
88     memcpy(header.get(), data, sizeof(flv_header_t));
89 //    std::copy(buf->begin(), buf->begin() + sizeof(flv_header_t), header.get());
90 
91     // test the magic number
92     if (memcmp(header->sig, "FLV", 3) != 0) {
93 	log_error(_("Bad magic number for FLV file!"));
94 	header.reset();
95 	return header;
96     }
97 
98     // Make sure the version is legit, it should alwys be 1
99     if (header->version != 0x1) {
100 	log_error(_("Bad version in FLV header! %d"), _header.version);
101 	header.reset();
102 	return header;
103     }
104 
105     // Make sure the type is set correctly
106     if (((header->type & Flv::FLV_AUDIO) && (header->type & Flv::FLV_VIDEO))
107 	|| (header->type & Flv::FLV_AUDIO) || (header->type & Flv::FLV_VIDEO)) {
108     } else {
109 	log_error(_("Bad FLV file Type: %d"), header->type);
110     }
111 
112     // Be lazy, as head_size is an array of 4 bytes, and not an integer in the data
113     // structure. This is to get around possible padding done to the data structure
114     // done by some compilers.
115     std::uint32_t size = *(reinterpret_cast<std::uint32_t *>(header->head_size));
116     // The header size is big endian
117     swapBytes(header->head_size, sizeof(std::uint32_t));
118 
119     // The header size is always 9, guess it could change some day in the far future, so
120     // we should use it.
121     if (ntohl(size) != 0x9) {
122 	log_error(_("Bad header size in FLV header! %d"), size);
123 	header.reset();
124     }
125 
126     return header;
127 }
128 
129 // Decode a MetaData object, which is after the header, but before all the tags
130 std::shared_ptr<cygnal::Element>
decodeMetaData(std::shared_ptr<cygnal::Buffer> buf)131 Flv::decodeMetaData(std::shared_ptr<cygnal::Buffer> buf)
132 {
133     return decodeMetaData(buf->reference(), buf->size());
134 }
135 
136 std::shared_ptr<cygnal::Element>
decodeMetaData(std::uint8_t * buf,size_t size)137 Flv::decodeMetaData(std::uint8_t *buf, size_t size)
138 {
139 //    GNASH_REPORT_FUNCTION;
140     AMF amf;
141     std::uint8_t *ptr = buf;
142     std::uint8_t *tooFar = ptr + size;
143 
144     // Extract the onMetaData object name
145     // In disk files, I always see the 0x2 type field for
146     // a string, but not always in streaming, at least according to
147     // Gnash's libmedia/FLVParser code. Since this is always
148     if (*ptr == Element::STRING_AMF0) {
149 	ptr++;
150     }
151 
152     std::uint16_t length;
153     length = ntohs((*(std::uint16_t *)ptr) & 0xffff);
154     if (length >= SANE_STR_SIZE) {
155 	log_error(_("%d bytes for a string is over the safe limit of %d"),
156 		  length, SANE_STR_SIZE);
157     }
158     ptr += sizeof(std::uint16_t);
159     std::string name(reinterpret_cast<const char *>(ptr), length);
160     ptr += length;
161 
162     // Extract the properties for this metadata object.
163     _metadata = amf.extractAMF(ptr, tooFar);
164     if (_metadata.get()) {
165     	ptr += amf.totalsize();
166 	    _metadata->setName(name.c_str(), length);
167     }
168 
169     return _metadata;
170 }
171 
172 std::shared_ptr<Flv::flv_audio_t>
decodeAudioData(std::uint8_t byte)173 Flv::decodeAudioData(std::uint8_t byte)
174 {
175 //    GNASH_REPORT_FUNCTION;
176     std::shared_ptr<flv_audio_t> audio(new flv_audio_t);
177 //    memset(audio->reference(), 0, sizeof(flv_audio_t));
178 
179     // Get the sound type
180     if (byte && Flv::AUDIO_STEREO) {
181 	audio->type = Flv::AUDIO_STEREO;
182     } else if (!byte && Flv::AUDIO_STEREO) {
183 	audio->type = Flv::AUDIO_MONO;
184     } else {
185 	log_error(_("Bad FLV Audio Sound Type: %x"), byte + 0);
186     }
187 
188     // Get the sound size
189     if ((byte >> 1) && Flv::AUDIO_16BIT) {
190 	audio->size = Flv::AUDIO_16BIT;
191     } else if (!(byte >> 1) && Flv::AUDIO_16BIT) {
192 	audio->size = Flv::AUDIO_8BIT;
193     } else {
194 	log_error(_("Bad FLV Audio Sound size: %d"), byte >> 1);
195     }
196 
197     // Get the sound rate
198 
199     if ((byte >> 2) && Flv::AUDIO_11KHZ) {
200 	audio->rate = Flv::AUDIO_11KHZ;
201     } else if ((byte >> 2) & Flv::AUDIO_22KHZ) {
202 	audio->rate = Flv::AUDIO_22KHZ;
203     } else if ((byte >> 2) & Flv::AUDIO_44KHZ) {
204 	audio->rate = Flv::AUDIO_44KHZ;
205     } else if ((byte >> 2) == 0) {
206 	audio->rate = Flv::AUDIO_55KHZ;
207     } else {
208 	log_error(_("Bad FLV Audio Sound Rate: %d"), byte >> 2);
209     }
210 
211     // Get the sound format
212     if ((byte >> 4) && Flv::AUDIO_ADPCM) {
213 	audio->format = Flv::AUDIO_ADPCM;
214     } else if ((byte >> 4) && Flv::AUDIO_MP3) {
215 	audio->format = Flv::AUDIO_MP3;
216     } else if ((byte >> 4) && Flv::AUDIO_NELLYMOSER_8KHZ) {
217 	audio->format = Flv::AUDIO_NELLYMOSER_8KHZ;
218     } else if ((byte >> 4) && Flv::AUDIO_NELLYMOSER) {
219 	audio->format = Flv::AUDIO_NELLYMOSER;
220     } else if ((byte >> 4) && Flv::AUDIO_VORBIS) {
221 	audio->format = Flv::AUDIO_VORBIS;
222     } else if (!(byte >> 4) && Flv::AUDIO_ADPCM) {
223 	audio->format = Flv::AUDIO_UNCOMPRESSED;
224     } else {
225 	log_error(_("Bad FLV Audio Sound format: %d"), byte >> 4);
226     }
227 
228     return audio;
229 }
230 
231 std::shared_ptr<Flv::flv_video_t>
decodeVideoData(std::uint8_t byte)232 Flv::decodeVideoData(std::uint8_t byte)
233 {
234 //    GNASH_REPORT_FUNCTION;
235     std::shared_ptr<flv_video_t> video(new flv_video_t);
236 //    memset(video, 0, sizeof(flv_video_t));
237 
238     // Get the codecID codecID
239     if (byte && Flv::VIDEO_H263) {
240 	video->codecID = Flv::VIDEO_H263;
241     } else if (byte && Flv::VIDEO_SCREEN) {
242 	video->codecID = Flv::VIDEO_SCREEN;
243     } else if (byte && Flv::VIDEO_VP6) {
244 	video->codecID = Flv::VIDEO_VP6;
245     } else if (byte && Flv::VIDEO_VP6_ALPHA) {
246 	video->codecID = Flv::VIDEO_VP6_ALPHA;
247     } else if (byte && Flv::VIDEO_SCREEN2) {
248 	video->codecID = Flv::VIDEO_SCREEN2;
249     } else if (byte && Flv::VIDEO_THEORA) {
250 	video->codecID = Flv::VIDEO_THEORA;
251     } else if (byte && Flv::VIDEO_DIRAC) {
252 	video->codecID = Flv::VIDEO_DIRAC;
253     } else if (byte && Flv::VIDEO_SPEEX) {
254 	video->codecID = Flv::VIDEO_SPEEX;
255     } else {
256 	log_error(_("Bad FLV Video Codec CodecID: 0x%x"), byte + 0);
257     }
258 
259     if (byte && Flv::KEYFRAME) {
260 	video->type = Flv::KEYFRAME;
261     } else if (byte && Flv::INTERFRAME) {
262 	video->type = Flv::INTERFRAME;
263     } else if (byte && Flv::DISPOSABLE) {
264 	video->type = Flv::DISPOSABLE;
265     } else {
266 	log_error(_("Bad FLV Video Frame CodecID: 0x%x"), byte + 0);
267     }
268 
269     return video;
270 }
271 
272 // Convert a 24 bit integer to a 32 bit one so we can use it.
273 std::uint32_t
convert24(std::uint8_t * num)274 Flv::convert24(std::uint8_t *num)
275 {
276 //    GNASH_REPORT_FUNCTION;
277     std::uint32_t bodysize = 0;
278 
279 #ifdef BOOST_BIG_ENDIAN
280     bodysize = *(reinterpret_cast<std::uint32_t *>(num)) >> 8;
281 #else
282     bodysize = *(reinterpret_cast<std::uint32_t *>(num)) << 8;
283     bodysize = ntohl(bodysize);
284 #endif
285 
286     return bodysize;
287 }
288 
289 // Decode the tag header
290 std::shared_ptr<Flv::flv_tag_t>
decodeTagHeader(std::uint8_t * buf)291 Flv::decodeTagHeader(std::uint8_t *buf)
292 {
293 //    GNASH_REPORT_FUNCTION;
294     flv_tag_t *data = reinterpret_cast<flv_tag_t *>(buf);
295     std::shared_ptr<flv_tag_t> tag(new flv_tag_t);
296     memcpy(tag.get(), data, sizeof(flv_tag_t));
297 
298 //    std::copy(buf->begin(), buf->end(), tag);
299 
300     // These fields are all 24 bit, big endian integers
301     swapBytes(tag->bodysize, 3);
302     swapBytes(tag->timestamp, 3);
303     swapBytes(tag->streamid, 3);
304 
305     return tag;
306 }
307 
308 std::shared_ptr<cygnal::Element>
findProperty(const std::string & name)309 Flv::findProperty(const std::string &name)
310 {
311     if (_properties.size() > 0) {
312 	std::vector<std::shared_ptr<cygnal::Element> >::iterator ait;
313 //	cerr << "# of Properties in object: " << _properties.size() << endl;
314 	for (ait = _properties.begin(); ait != _properties.end(); ++ait) {
315 	    std::shared_ptr<cygnal::Element> el = (*(ait));
316 	    if (el->getName() == name) {
317 		return el;
318 	    }
319 //	    el->dump();
320 	}
321     }
322     std::shared_ptr<cygnal::Element> el;
323     return el;
324 }
325 
326 void
dump()327 Flv::dump()
328 {
329 //    GNASH_REPORT_FUNCTION;
330     if (_properties.size() > 0) {
331 	std::vector<std::shared_ptr<cygnal::Element> >::iterator ait;
332 	std::cerr << "# of Properties in object: " << _properties.size()
333 	          << std::endl;
334 	for (ait = _properties.begin(); ait != _properties.end(); ++ait) {
335 	    std::shared_ptr<cygnal::Element> el = (*(ait));
336             // an onMetaData packet of an FLV stream only contains number or
337             // boolean bydefault
338             if (el->getType() == Element::NUMBER_AMF0) {
339                 log_debug(_("FLV MetaData: %s: %s"), el->getName(), el->to_number());
340             } else if (el->getType() == Element::BOOLEAN_AMF0) {
341                 log_debug(_("FLV MetaData: %s: %s"), el->getName(), (el->to_bool() ? "true" : "false") );
342             } else {
343                 log_debug(_("FLV MetaData: %s: %s"), el->getName(), el->to_string());
344             }
345 	}
346     } else {
347 	std::cerr << "No properties" << std::endl;
348     }
349 }
350 
351 } // end of amf namespace
352 
353 // local Variables:
354 // mode: C++
355 // indent-tabs-mode: t
356 // End:
357