1 // FlvHandler.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "../../../C/CpuArch.h"
8
9 #include "../../Common/ComTry.h"
10 #include "../../Common/MyBuffer.h"
11 #include "../../Common/MyString.h"
12
13 #include "../../Windows/PropVariant.h"
14
15 #include "../Common/InBuffer.h"
16 #include "../Common/ProgressUtils.h"
17 #include "../Common/RegisterArc.h"
18 #include "../Common/StreamObjects.h"
19 #include "../Common/StreamUtils.h"
20
21 #define GetBe24(p) ( \
22 ((UInt32)((const Byte *)(p))[0] << 16) | \
23 ((UInt32)((const Byte *)(p))[1] << 8) | \
24 ((const Byte *)(p))[2] )
25
26 // #define Get16(p) GetBe16(p)
27 #define Get24(p) GetBe24(p)
28 #define Get32(p) GetBe32(p)
29
30 namespace NArchive {
31 namespace NFlv {
32
33 // static const UInt32 kFileSizeMax = (UInt32)1 << 30;
34 static const UInt32 kNumChunksMax = (UInt32)1 << 23;
35
36 static const UInt32 kTagHeaderSize = 11;
37
38 static const Byte kFlag_Video = 1;
39 static const Byte kFlag_Audio = 4;
40
41 static const Byte kType_Audio = 8;
42 static const Byte kType_Video = 9;
43 static const Byte kType_Meta = 18;
44 static const unsigned kNumTypes = 19;
45
46 struct CItem
47 {
48 CByteBuffer Data;
49 Byte Type;
50 };
51
52 struct CItem2
53 {
54 Byte Type;
55 Byte SubType;
56 Byte Props;
57 bool SameSubTypes;
58 unsigned NumChunks;
59 size_t Size;
60
61 CReferenceBuf *BufSpec;
62 CMyComPtr<IUnknown> RefBuf;
63
IsAudioNArchive::NFlv::CItem264 bool IsAudio() const { return Type == kType_Audio; }
65 };
66
67
68 Z7_CLASS_IMP_CHandler_IInArchive_1(
69 IInArchiveGetStream
70 )
71 CMyComPtr<IInStream> _stream;
72 CObjectVector<CItem2> _items2;
73 CByteBuffer _metadata;
74 bool _isRaw;
75 UInt64 _phySize;
76
77 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
78 // AString GetComment();
79 };
80
81 static const Byte kProps[] =
82 {
83 kpidSize,
84 kpidNumBlocks,
85 kpidComment
86 };
87
88 IMP_IInArchive_Props
89 IMP_IInArchive_ArcProps_NO_Table
90
91 static const char * const g_AudioTypes[16] =
92 {
93 "pcm"
94 , "adpcm"
95 , "mp3"
96 , "pcm_le"
97 , "nellymoser16"
98 , "nellymoser8"
99 , "nellymoser"
100 , "g711a"
101 , "g711m"
102 , "audio9"
103 , "aac"
104 , "speex"
105 , "audio12"
106 , "audio13"
107 , "mp3"
108 , "audio15"
109 };
110
111 static const char * const g_VideoTypes[16] =
112 {
113 "video0"
114 , "jpeg"
115 , "h263"
116 , "screen"
117 , "vp6"
118 , "vp6alpha"
119 , "screen2"
120 , "avc"
121 , "video8"
122 , "video9"
123 , "video10"
124 , "video11"
125 , "video12"
126 , "video13"
127 , "video14"
128 , "video15"
129 };
130
131 static const char * const g_Rates[4] =
132 {
133 "5.5 kHz"
134 , "11 kHz"
135 , "22 kHz"
136 , "44 kHz"
137 };
138
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))139 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
140 {
141 NWindows::NCOM::CPropVariant prop;
142 const CItem2 &item = _items2[index];
143 switch (propID)
144 {
145 case kpidExtension:
146 prop = _isRaw ?
147 (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) :
148 (item.IsAudio() ? "audio.flv" : "video.flv");
149 break;
150 case kpidSize:
151 case kpidPackSize:
152 prop = (UInt64)item.Size;
153 break;
154 case kpidNumBlocks: prop = (UInt32)item.NumChunks; break;
155 case kpidComment:
156 {
157 char sz[64];
158 char *s = MyStpCpy(sz, (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) );
159 if (item.IsAudio())
160 {
161 *s++ = ' ';
162 s = MyStpCpy(s, g_Rates[(item.Props >> 2) & 3]);
163 s = MyStpCpy(s, (item.Props & 2) ? " 16-bit" : " 8-bit");
164 s = MyStpCpy(s, (item.Props & 1) ? " stereo" : " mono");
165 }
166 prop = sz;
167 break;
168 }
169 }
170 prop.Detach(value);
171 return S_OK;
172 }
173
174 /*
175 AString CHandler::GetComment()
176 {
177 const Byte *p = _metadata;
178 size_t size = _metadata.Size();
179 AString res;
180 if (size > 0)
181 {
182 p++;
183 size--;
184 for (;;)
185 {
186 if (size < 2)
187 break;
188 int len = Get16(p);
189 p += 2;
190 size -= 2;
191 if (len == 0 || (size_t)len > size)
192 break;
193 {
194 AString temp;
195 temp.SetFrom_CalcLen((const char *)p, len);
196 if (!res.IsEmpty())
197 res += '\n';
198 res += temp;
199 }
200 p += len;
201 size -= len;
202 if (size < 1)
203 break;
204 Byte type = *p++;
205 size--;
206 bool ok = false;
207 switch (type)
208 {
209 case 0:
210 {
211 if (size < 8)
212 break;
213 ok = true;
214 Byte reverse[8];
215 for (int i = 0; i < 8; i++)
216 {
217 bool little_endian = 1;
218 if (little_endian)
219 reverse[i] = p[7 - i];
220 else
221 reverse[i] = p[i];
222 }
223 double d = *(double *)reverse;
224 char temp[32];
225 sprintf(temp, " = %.3f", d);
226 res += temp;
227 p += 8;
228 size -= 8;
229 break;
230 }
231 case 8:
232 {
233 if (size < 4)
234 break;
235 ok = true;
236 // UInt32 numItems = Get32(p);
237 p += 4;
238 size -= 4;
239 break;
240 }
241 }
242 if (!ok)
243 break;
244 }
245 }
246 return res;
247 }
248 */
249
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))250 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
251 {
252 // COM_TRY_BEGIN
253 NWindows::NCOM::CPropVariant prop;
254 switch (propID)
255 {
256 // case kpidComment: prop = GetComment(); break;
257 case kpidPhySize: prop = (UInt64)_phySize; break;
258 case kpidIsNotArcType: prop = true; break;
259 }
260 prop.Detach(value);
261 return S_OK;
262 // COM_TRY_END
263 }
264
Open2(IInStream * stream,IArchiveOpenCallback * callback)265 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
266 {
267 const UInt32 kHeaderSize = 13;
268 Byte header[kHeaderSize];
269 RINOK(ReadStream_FALSE(stream, header, kHeaderSize))
270 if (header[0] != 'F' ||
271 header[1] != 'L' ||
272 header[2] != 'V' ||
273 header[3] != 1 ||
274 (header[4] & 0xFA) != 0)
275 return S_FALSE;
276 UInt64 offset = Get32(header + 5);
277 if (offset != 9 || Get32(header + 9) != 0)
278 return S_FALSE;
279 offset = kHeaderSize;
280
281 CInBuffer inBuf;
282 if (!inBuf.Create(1 << 15))
283 return E_OUTOFMEMORY;
284 inBuf.SetStream(stream);
285
286 CObjectVector<CItem> items;
287 int lasts[kNumTypes];
288 unsigned i;
289 for (i = 0; i < kNumTypes; i++)
290 lasts[i] = -1;
291
292 _phySize = offset;
293 for (;;)
294 {
295 Byte buf[kTagHeaderSize];
296 CItem item;
297 if (inBuf.ReadBytes(buf, kTagHeaderSize) != kTagHeaderSize)
298 break;
299 item.Type = buf[0];
300 UInt32 size = Get24(buf + 1);
301 if (size < 1)
302 break;
303 // item.Time = Get24(buf + 4);
304 // item.Time |= (UInt32)buf[7] << 24;
305 if (Get24(buf + 8) != 0) // streamID
306 break;
307
308 UInt32 curSize = kTagHeaderSize + size + 4;
309 item.Data.Alloc(curSize);
310 memcpy(item.Data, buf, kTagHeaderSize);
311 if (inBuf.ReadBytes(item.Data + kTagHeaderSize, size) != size)
312 break;
313 if (inBuf.ReadBytes(item.Data + kTagHeaderSize + size, 4) != 4)
314 break;
315
316 if (Get32(item.Data + kTagHeaderSize + size) != kTagHeaderSize + size)
317 break;
318
319 offset += curSize;
320
321 // printf("\noffset = %6X type = %2d time = %6d size = %6d", (UInt32)offset, item.Type, item.Time, item.Size);
322
323 if (item.Type == kType_Meta)
324 {
325 // _metadata = item.Buf;
326 }
327 else
328 {
329 if (item.Type != kType_Audio && item.Type != kType_Video)
330 break;
331 if (items.Size() >= kNumChunksMax)
332 return S_FALSE;
333 Byte firstByte = item.Data[kTagHeaderSize];
334 Byte subType, props;
335 if (item.Type == kType_Audio)
336 {
337 subType = (Byte)(firstByte >> 4);
338 props = (Byte)(firstByte & 0xF);
339 }
340 else
341 {
342 subType = (Byte)(firstByte & 0xF);
343 props = (Byte)(firstByte >> 4);
344 }
345 int last = lasts[item.Type];
346 if (last < 0)
347 {
348 CItem2 item2;
349 item2.RefBuf = item2.BufSpec = new CReferenceBuf;
350 item2.Size = curSize;
351 item2.Type = item.Type;
352 item2.SubType = subType;
353 item2.Props = props;
354 item2.NumChunks = 1;
355 item2.SameSubTypes = true;
356 lasts[item.Type] = (int)_items2.Add(item2);
357 }
358 else
359 {
360 CItem2 &item2 = _items2[last];
361 if (subType != item2.SubType)
362 item2.SameSubTypes = false;
363 item2.Size += curSize;
364 item2.NumChunks++;
365 }
366 items.Add(item);
367 }
368 _phySize = offset;
369 if (callback && (items.Size() & 0xFF) == 0)
370 {
371 RINOK(callback->SetCompleted(NULL, &offset))
372 }
373 }
374 if (items.IsEmpty())
375 return S_FALSE;
376
377 _isRaw = (_items2.Size() == 1);
378 for (i = 0; i < _items2.Size(); i++)
379 {
380 CItem2 &item2 = _items2[i];
381 CByteBuffer &itemBuf = item2.BufSpec->Buf;
382 if (_isRaw)
383 {
384 if (!item2.SameSubTypes)
385 return S_FALSE;
386 itemBuf.Alloc((size_t)item2.Size - (size_t)(kTagHeaderSize + 4 + 1) * item2.NumChunks);
387 item2.Size = 0;
388 }
389 else
390 {
391 itemBuf.Alloc(kHeaderSize + (size_t)item2.Size);
392 memcpy(itemBuf, header, kHeaderSize);
393 itemBuf[4] = item2.IsAudio() ? kFlag_Audio : kFlag_Video;
394 item2.Size = kHeaderSize;
395 }
396 }
397
398 for (i = 0; i < items.Size(); i++)
399 {
400 const CItem &item = items[i];
401 CItem2 &item2 = _items2[lasts[item.Type]];
402 size_t size = item.Data.Size();
403 const Byte *src = item.Data;
404 if (_isRaw)
405 {
406 src += kTagHeaderSize + 1;
407 size -= (kTagHeaderSize + 4 + 1);
408 }
409 if (size != 0)
410 {
411 memcpy(item2.BufSpec->Buf + item2.Size, src, size);
412 item2.Size += size;
413 }
414 }
415 return S_OK;
416 }
417
Z7_COM7F_IMF(CHandler::Open (IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback))418 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback))
419 {
420 COM_TRY_BEGIN
421 Close();
422 HRESULT res;
423 try
424 {
425 res = Open2(inStream, callback);
426 if (res == S_OK)
427 _stream = inStream;
428 }
429 catch(...) { res = S_FALSE; }
430 if (res != S_OK)
431 {
432 Close();
433 return S_FALSE;
434 }
435 return S_OK;
436 COM_TRY_END
437 }
438
Z7_COM7F_IMF(CHandler::Close ())439 Z7_COM7F_IMF(CHandler::Close())
440 {
441 _phySize = 0;
442 _stream.Release();
443 _items2.Clear();
444 // _metadata.SetCapacity(0);
445 return S_OK;
446 }
447
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))448 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
449 {
450 *numItems = _items2.Size();
451 return S_OK;
452 }
453
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))454 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
455 Int32 testMode, IArchiveExtractCallback *extractCallback))
456 {
457 COM_TRY_BEGIN
458 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
459 if (allFilesMode)
460 numItems = _items2.Size();
461 if (numItems == 0)
462 return S_OK;
463 UInt64 totalSize = 0;
464 UInt32 i;
465 for (i = 0; i < numItems; i++)
466 totalSize += _items2[allFilesMode ? i : indices[i]].Size;
467 extractCallback->SetTotal(totalSize);
468
469 totalSize = 0;
470
471 CLocalProgress *lps = new CLocalProgress;
472 CMyComPtr<ICompressProgressInfo> progress = lps;
473 lps->Init(extractCallback, false);
474
475 for (i = 0; i < numItems; i++)
476 {
477 lps->InSize = lps->OutSize = totalSize;
478 RINOK(lps->SetCur())
479 CMyComPtr<ISequentialOutStream> outStream;
480 const Int32 askMode = testMode ?
481 NExtract::NAskMode::kTest :
482 NExtract::NAskMode::kExtract;
483 const UInt32 index = allFilesMode ? i : indices[i];
484 const CItem2 &item = _items2[index];
485 RINOK(extractCallback->GetStream(index, &outStream, askMode))
486 totalSize += item.Size;
487 if (!testMode && !outStream)
488 continue;
489 RINOK(extractCallback->PrepareOperation(askMode))
490 if (outStream)
491 {
492 RINOK(WriteStream(outStream, item.BufSpec->Buf, item.BufSpec->Buf.Size()))
493 }
494 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
495 }
496 return S_OK;
497 COM_TRY_END
498 }
499
Z7_COM7F_IMF(CHandler::GetStream (UInt32 index,ISequentialInStream ** stream))500 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
501 {
502 COM_TRY_BEGIN
503 *stream = NULL;
504 CBufInStream *streamSpec = new CBufInStream;
505 CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
506 streamSpec->Init(_items2[index].BufSpec);
507 *stream = streamTemp.Detach();
508 return S_OK;
509 COM_TRY_END
510 }
511
512 static const Byte k_Signature[] = { 'F', 'L', 'V', 1, };
513
514 REGISTER_ARC_I(
515 "FLV", "flv", NULL, 0xD6,
516 k_Signature,
517 0,
518 0,
519 NULL)
520
521 }}
522