diff options
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp')
-rw-r--r-- | src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp | 544 |
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) + +}} |