summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp')
-rw-r--r--src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp544
1 files changed, 544 insertions, 0 deletions
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp
new file mode 100644
index 000000000..a22c29e30
--- /dev/null
+++ b/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp
@@ -0,0 +1,544 @@
+// FlvHandler.cpp
+
+#include "StdAfx.h"
+
+#include "../../../C/CpuArch.h"
+
+#include "Common/Buffer.h"
+#include "Common/ComTry.h"
+// #include "Common/Defs.h"
+#include "Common/MyString.h"
+
+#include "Windows/PropVariant.h"
+
+#include "../Common/ProgressUtils.h"
+#include "../Common/RegisterArc.h"
+#include "../Common/StreamObjects.h"
+#include "../Common/StreamUtils.h"
+
+#define GetBe24(p) ( \
+ ((UInt32)((const Byte *)(p))[0] << 16) | \
+ ((UInt32)((const Byte *)(p))[1] << 8) | \
+ ((const Byte *)(p))[2] )
+
+#define Get16(p) GetBe16(p)
+#define Get24(p) GetBe24(p)
+#define Get32(p) GetBe32(p)
+
+namespace NArchive {
+namespace NFlv {
+
+static const UInt32 kFileSizeMax = (UInt32)1 << 30;
+static const int kNumChunksMax = (UInt32)1 << 23;
+
+const UInt32 kTagHeaderSize = 11;
+
+static const Byte kFlag_Video = 1;
+static const Byte kFlag_Audio = 4;
+
+static const Byte kType_Audio = 8;
+static const Byte kType_Video = 9;
+static const Byte kType_Meta = 18;
+static const int kNumTypes = 19;
+
+struct CItem
+{
+ UInt32 Offset;
+ UInt32 Size;
+ // UInt32 Time;
+ Byte Type;
+};
+
+struct CItem2
+{
+ Byte Type;
+ Byte SubType;
+ Byte Props;
+ bool SameSubTypes;
+ int NumChunks;
+ size_t Size;
+
+ CReferenceBuf *BufSpec;
+ CMyComPtr<IUnknown> RefBuf;
+
+ bool IsAudio() const { return Type == kType_Audio; }
+};
+
+class CHandler:
+ public IInArchive,
+ public IInArchiveGetStream,
+ public CMyUnknownImp
+{
+ int _isRaw;
+ CMyComPtr<IInStream> _stream;
+ CObjectVector<CItem2> _items2;
+ // CByteBuffer _metadata;
+ HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
+ AString GetComment();
+public:
+ MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
+ INTERFACE_IInArchive(;)
+ STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
+};
+
+STATPROPSTG kProps[] =
+{
+ { NULL, kpidSize, VT_UI8},
+ { NULL, kpidNumBlocks, VT_UI4},
+ { NULL, kpidComment, VT_BSTR}
+};
+
+/*
+STATPROPSTG kArcProps[] =
+{
+ { NULL, kpidComment, VT_BSTR}
+};
+*/
+
+IMP_IInArchive_Props
+IMP_IInArchive_ArcProps_NO
+
+static const char *g_AudioTypes[16] =
+{
+ "pcm",
+ "adpcm",
+ "mp3",
+ "pcm_le",
+ "nellymoser16",
+ "nellymoser8",
+ "nellymoser",
+ "g711a",
+ "g711m",
+ "audio9",
+ "aac",
+ "speex",
+ "audio12",
+ "audio13",
+ "mp3",
+ "audio15"
+};
+
+static const char *g_VideoTypes[16] =
+{
+ "video0",
+ "jpeg",
+ "h263",
+ "screen",
+ "vp6",
+ "vp6alpha",
+ "screen2",
+ "avc",
+ "video8",
+ "video9",
+ "video10",
+ "video11",
+ "video12",
+ "video13",
+ "video14",
+ "video15"
+};
+
+static const char *g_Rates[4] =
+{
+ "5.5 kHz",
+ "11 kHz",
+ "22 kHz",
+ "44 kHz"
+};
+
+static void MyStrCat(char *d, const char *s)
+{
+ MyStringCopy(d + MyStringLen(d), s);
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+ NWindows::NCOM::CPropVariant prop;
+ const CItem2 &item = _items2[index];
+ switch(propID)
+ {
+ case kpidExtension:
+ prop = _isRaw ?
+ (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) :
+ (item.IsAudio() ? "audio.flv" : "video.flv");
+ break;
+ case kpidSize:
+ case kpidPackSize:
+ prop = (UInt64)item.Size;
+ break;
+ case kpidNumBlocks: prop = (UInt32)item.NumChunks; break;
+ case kpidComment:
+ {
+ char sz[64];
+ MyStringCopy(sz, (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) );
+ if (item.IsAudio())
+ {
+ MyStrCat(sz, " ");
+ MyStrCat(sz, g_Rates[(item.Props >> 2) & 3]);
+ MyStrCat(sz, (item.Props & 2) ? " 16-bit" : " 8-bit");
+ MyStrCat(sz, (item.Props & 1) ? " stereo" : " mono");
+ }
+ prop = sz;
+ break;
+ }
+ }
+ prop.Detach(value);
+ return S_OK;
+}
+
+/*
+AString CHandler::GetComment()
+{
+ const Byte *p = _metadata;
+ size_t size = _metadata.GetCapacity();
+ AString res;
+ if (size > 0)
+ {
+ p++;
+ size--;
+ for (;;)
+ {
+ if (size < 2)
+ break;
+ int len = Get16(p);
+ p += 2;
+ size -= 2;
+ if (len == 0 || (size_t)len > size)
+ break;
+ {
+ AString temp;
+ char *sz = temp.GetBuffer(len);
+ memcpy(sz, p, len);
+ sz[len] = 0;
+ temp.ReleaseBuffer();
+ if (!res.IsEmpty())
+ res += '\n';
+ res += temp;
+ }
+ p += len;
+ size -= len;
+ if (size < 1)
+ break;
+ Byte type = *p++;
+ size--;
+ bool ok = false;
+ switch(type)
+ {
+ case 0:
+ {
+ if (size < 8)
+ break;
+ ok = true;
+ Byte reverse[8];
+ for (int i = 0; i < 8; i++)
+ {
+ bool little_endian = 1;
+ if (little_endian)
+ reverse[i] = p[7 - i];
+ else
+ reverse[i] = p[i];
+ }
+ double d = *(double *)reverse;
+ char temp[32];
+ sprintf(temp, " = %.3f", d);
+ res += temp;
+ p += 8;
+ size -= 8;
+ break;
+ }
+ case 8:
+ {
+ if (size < 4)
+ break;
+ ok = true;
+ // UInt32 numItems = Get32(p);
+ p += 4;
+ size -= 4;
+ break;
+ }
+ }
+ if (!ok)
+ break;
+ }
+ }
+ return res;
+}
+
+STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
+{
+ COM_TRY_BEGIN
+ NWindows::NCOM::CPropVariant prop;
+ switch(propID)
+ {
+ case kpidComment: prop = GetComment(); break;
+ }
+ prop.Detach(value);
+ return S_OK;
+ COM_TRY_END
+}
+*/
+
+HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
+{
+ CRecordVector<CItem> items;
+
+ const UInt32 kHeaderSize = 13;
+ Byte header[kHeaderSize];
+ RINOK(ReadStream_FALSE(stream, header, kHeaderSize));
+ if (header[0] != 'F' ||
+ header[1] != 'L' ||
+ header[2] != 'V' ||
+ header[3] != 1 ||
+ (header[4] & 0xFA) != 0)
+ return S_FALSE;
+ UInt32 offset = Get32(header + 5);
+ if (offset != 9 || Get32(header + 9) != 0)
+ return S_FALSE;
+ offset += 4;
+
+ CByteBuffer inBuf;
+ size_t fileSize;
+ {
+ UInt64 fileSize64;
+ RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize64));
+ if (fileSize64 > kFileSizeMax)
+ return S_FALSE;
+
+ if (callback)
+ RINOK(callback->SetTotal(NULL, &fileSize64))
+
+ RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
+ fileSize = (size_t)fileSize64;
+ inBuf.SetCapacity(fileSize);
+ for (size_t pos = 0; pos < fileSize;)
+ {
+ UInt64 offset64 = pos;
+ if (callback)
+ RINOK(callback->SetCompleted(NULL, &offset64))
+ size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20));
+ RINOK(ReadStream_FALSE(stream, inBuf + pos, rem));
+ pos += rem;
+ }
+ }
+
+ int lasts[kNumTypes];
+ int i;
+ for (i = 0; i < kNumTypes; i++)
+ lasts[i] = -1;
+
+ while (offset < fileSize)
+ {
+ CItem item;
+ item.Offset = offset;
+ const Byte *buf = inBuf + offset;
+ offset += kTagHeaderSize;
+ if (offset > fileSize)
+ return S_FALSE;
+
+ item.Type = buf[0];
+ UInt32 size = Get24(buf + 1);
+ if (size < 1)
+ return S_FALSE;
+ // item.Time = Get24(buf + 4);
+ // item.Time |= (UInt32)buf[7] << 24;
+ if (Get24(buf + 8) != 0) // streamID
+ return S_FALSE;
+
+ UInt32 curSize = kTagHeaderSize + size + 4;
+ item.Size = curSize;
+
+ offset += curSize - kTagHeaderSize;
+ if (offset > fileSize)
+ return S_FALSE;
+
+ if (Get32(buf + kTagHeaderSize + size) != kTagHeaderSize + size)
+ return S_FALSE;
+
+ // printf("\noffset = %6X type = %2d time = %6d size = %6d", (UInt32)offset, item.Type, item.Time, item.Size);
+
+ if (item.Type == kType_Meta)
+ {
+ // _metadata = item.Buf;
+ }
+ else
+ {
+ if (item.Type != kType_Audio && item.Type != kType_Video)
+ return S_FALSE;
+ if (items.Size() >= kNumChunksMax)
+ return S_FALSE;
+ Byte firstByte = buf[kTagHeaderSize];
+ Byte subType, props;
+ if (item.Type == kType_Audio)
+ {
+ subType = firstByte >> 4;
+ props = firstByte & 0xF;
+ }
+ else
+ {
+ subType = firstByte & 0xF;
+ props = firstByte >> 4;
+ }
+ int last = lasts[item.Type];
+ if (last < 0)
+ {
+ CItem2 item2;
+ item2.RefBuf = item2.BufSpec = new CReferenceBuf;
+ item2.Size = curSize;
+ item2.Type = item.Type;
+ item2.SubType = subType;
+ item2.Props = props;
+ item2.NumChunks = 1;
+ item2.SameSubTypes = true;
+ lasts[item.Type] = _items2.Add(item2);
+ }
+ else
+ {
+ CItem2 &item2 = _items2[last];
+ if (subType != item2.SubType)
+ item2.SameSubTypes = false;
+ item2.Size += curSize;
+ item2.NumChunks++;
+ }
+ items.Add(item);
+ }
+ }
+
+ _isRaw = (_items2.Size() == 1);
+ for (i = 0; i < _items2.Size(); i++)
+ {
+ CItem2 &item2 = _items2[i];
+ CByteBuffer &itemBuf = item2.BufSpec->Buf;
+ if (_isRaw)
+ {
+ if (!item2.SameSubTypes)
+ return S_FALSE;
+ itemBuf.SetCapacity((size_t)item2.Size - (kTagHeaderSize + 4 + 1) * item2.NumChunks);
+ item2.Size = 0;
+ }
+ else
+ {
+ itemBuf.SetCapacity(kHeaderSize + (size_t)item2.Size);
+ memcpy(itemBuf, header, kHeaderSize);
+ itemBuf[4] = item2.IsAudio() ? kFlag_Audio : kFlag_Video;
+ item2.Size = kHeaderSize;
+ }
+ }
+
+ for (i = 0; i < items.Size(); i++)
+ {
+ const CItem &item = items[i];
+ CItem2 &item2 = _items2[lasts[item.Type]];
+ size_t size = item.Size;
+ const Byte *src = inBuf + item.Offset;
+ if (_isRaw)
+ {
+ src += kTagHeaderSize + 1;
+ size -= (kTagHeaderSize + 4 + 1);
+ }
+ memcpy(item2.BufSpec->Buf + item2.Size, src, size);
+ item2.Size += size;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
+{
+ COM_TRY_BEGIN
+ Close();
+ HRESULT res;
+ try
+ {
+ res = Open2(inStream, callback);
+ if (res == S_OK)
+ _stream = inStream;
+ }
+ catch(...) { res = S_FALSE; }
+ if (res != S_OK)
+ {
+ Close();
+ return S_FALSE;
+ }
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+ _stream.Release();
+ _items2.Clear();
+ // _metadata.SetCapacity(0);
+ return S_OK;
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+ *numItems = _items2.Size();
+ return S_OK;
+}
+
+STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
+ Int32 testMode, IArchiveExtractCallback *extractCallback)
+{
+ COM_TRY_BEGIN
+ bool allFilesMode = (numItems == (UInt32)-1);
+ if (allFilesMode)
+ numItems = _items2.Size();
+ if (numItems == 0)
+ return S_OK;
+ UInt64 totalSize = 0;
+ UInt32 i;
+ for (i = 0; i < numItems; i++)
+ totalSize += _items2[allFilesMode ? i : indices[i]].Size;
+ extractCallback->SetTotal(totalSize);
+
+ totalSize = 0;
+
+ CLocalProgress *lps = new CLocalProgress;
+ CMyComPtr<ICompressProgressInfo> progress = lps;
+ lps->Init(extractCallback, false);
+
+ for (i = 0; i < numItems; i++)
+ {
+ lps->InSize = lps->OutSize = totalSize;
+ RINOK(lps->SetCur());
+ CMyComPtr<ISequentialOutStream> outStream;
+ Int32 askMode = testMode ?
+ NExtract::NAskMode::kTest :
+ NExtract::NAskMode::kExtract;
+ UInt32 index = allFilesMode ? i : indices[i];
+ const CItem2 &item = _items2[index];
+ RINOK(extractCallback->GetStream(index, &outStream, askMode));
+ totalSize += item.Size;
+ if (!testMode && !outStream)
+ continue;
+ RINOK(extractCallback->PrepareOperation(askMode));
+ if (outStream)
+ {
+ RINOK(WriteStream(outStream, item.BufSpec->Buf, item.BufSpec->Buf.GetCapacity()));
+ }
+ RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
+ }
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
+{
+ COM_TRY_BEGIN
+ *stream = 0;
+ CBufInStream *streamSpec = new CBufInStream;
+ CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
+ streamSpec->Init(_items2[index].BufSpec);
+ *stream = streamTemp.Detach();
+ return S_OK;
+ COM_TRY_END
+}
+
+static IInArchive *CreateArc() { return new CHandler; }
+
+static CArcInfo g_ArcInfo =
+ { L"FLV", L"flv", 0, 0xD6, { 'F', 'L', 'V' }, 3, false, CreateArc, 0 };
+
+REGISTER_ARC(Flv)
+
+}}