Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1543 lines
40 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include "Types.h"
namespace Jpeg2000
{
//-------------------------------------------------------------------------------------------------------------------------------
// Данные функции предназначены для чтения Motion JPEG 2000 (MJ2)
//-------------------------------------------------------------------------------------------------------------------------------
static bool Mj2_ReadBoxHeader(Mj2_Box* pBox, CReader * pStream)
{
if (!pBox)
return false;
pBox->nInitPos = pStream->Tell();
pBox->nLength = pStream->Read(4);
pBox->nType = pStream->Read(4);
if (1 == pBox->nLength)
{
if (0 != pStream->Read(4))
{
Event_Message(EVT_ERROR, "Error: Cannot handle box sizes higher than 2^32\n");
return false;
}
pBox->nLength = pStream->Read(4);
if (0 == pBox->nLength)
pBox->nLength = pStream->GetLeftSize() + 12;
}
else if (0 == pBox->nLength)
{
int nBytesLeft = pStream->GetLeftSize();
if (0 == nBytesLeft)
return false;
pBox->nLength = nBytesLeft + 8;
}
return true;
}
static bool Mj2_ReadJP(CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (oBox.nType != MJ2_JP)
{
Event_Message(EVT_ERROR, "Error: Expected JP Marker\n");
return false;
}
if (0x0d0a870a != pStream->Read(4)) // read the 0x0d0a870a required in a JP box
{
Event_Message(EVT_ERROR, "Error with JP Marker\n");
return false;
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with JP Box size \n");
return false;
}
return true;
}
static bool Mj2_ReadFTYP(Mj2_Movie* pMovie, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_FTYP != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected FTYP Marker\n");
return false;
}
pMovie->unBrand = pStream->Read(4); // BR
pMovie->unMinVersion = pStream->Read(4); // MinV
pMovie->nCompListLength = (oBox.nLength - 16) / 4;
pMovie->pCompList = (unsigned int*)Malloc(pMovie->nCompListLength * sizeof(unsigned int));
for (int nIndex = pMovie->nCompListLength - 1; nIndex > -1; nIndex--)
{
pMovie->pCompList[nIndex] = pStream->Read(4); // CLi
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with FTYP Box\n");
return false;
}
return true;
}
static bool Mj2_ReadMDAT(Mj2_Movie* pMovie, CReader * pStream, Image** ppImage, Mj2_Box oBox)
{
if (MJ2_MDAT != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected FTYP Marker\n");
return false;
}
int nStartPos = pStream->Tell();
//>>>>
// Достаем первую картинку в потоке MDAT
Jp2Box oTempBox;
Jp2_ReadBoxHeader(pMovie->pCodecInfo, pStream, &oTempBox);
do
{
if (JP2_JP2C != oTempBox.nType)
{
pStream->Skip(oTempBox.nLength - 8);
if (pStream->GetLeftSize() < 0)
return false;
if (!Jp2_ReadBoxHeader(pMovie->pCodecInfo, pStream, &oTempBox))
return false;
}
} while (JP2_JP2C != oTempBox.nType);
int nJ2kCodestreamOffset = pStream->Tell();
int nJ2kCodestreamLength = oTempBox.nLength - 8;
// Декодируем J2K
*ppImage = J2k_Decode(pMovie->pJ2k, pStream);
if (!*ppImage)
{
Event_Message(EVT_ERROR, "Failed to decode J2K image\n");
return false;
}
//<<<<<<
int nReaded = pStream->Tell() - nStartPos;
pStream->Skip(oBox.nLength - nReaded - 8);
if (pStream->GetLeftSize() < 0)
return false;
return true;
}
static bool Mj2_ReadMVHD(Mj2_Movie* pMovie, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_MVHD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected MVHD Marker\n");
return false;
}
if (0 != pStream->Read(4)) // Version = 0, flags = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in MVHD box\n");
}
// TO DO: Здесь в зависимости от версии разное число байт должно читаться
// см. fcd15444-3.pdf стр.15
pMovie->unCreationTime = pStream->Read(4); // Creation Time
pMovie->unModificationTime = pStream->Read(4); // Modification Time
pMovie->nTimescale = pStream->Read(4); // Timescale
pMovie->unDuration = pStream->Read(4); // Duration
pMovie->nRate = pStream->Read(4); // Rate
pMovie->nVolume = pStream->Read(2); // Volume
pStream->Skip(10); // const bit(16) reserved = 0 + const unsigned int(32)[2] reserved = 0
pMovie->anTransMatrix[0] = pStream->Read(4); // Transformation matrix
pMovie->anTransMatrix[1] = pStream->Read(4); //
pMovie->anTransMatrix[2] = pStream->Read(4); //
pMovie->anTransMatrix[3] = pStream->Read(4); //
pMovie->anTransMatrix[4] = pStream->Read(4); //
pMovie->anTransMatrix[5] = pStream->Read(4); //
pMovie->anTransMatrix[6] = pStream->Read(4); //
pMovie->anTransMatrix[7] = pStream->Read(4); //
pMovie->anTransMatrix[8] = pStream->Read(4); //
pStream->Skip(24); // bit(32)[6] pre-defined = 0;
pMovie->nNextTrackId = pStream->Read(4); // ID of Next track to be added
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with MVHD Box Size\n");
return false;
}
return true;
}
static bool Mj2_ReadTKHD(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_TKHD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected TKHD Marker\n");
return false;
}
// TO DO: Здесь в зависимости от версии разное число байт должно читаться
// см. fcd15444-3.pdf стр.16
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in TKHD box\n");
return false;
}
int nFlag = pStream->Read(3);
if (!(1 == nFlag || 2 == nFlag || 3 == nFlag || 4 == nFlag)) // nFlags = 1, 2, 3 или 4
{
Event_Message(EVT_ERROR, "Error with flag in TKHD box: Expected flag 1,2,3 or 4\n");
return false;
}
pTrack->unCreationTime = pStream->Read(4); // Creation Time
pTrack->unModificationTime = pStream->Read(4); // Modification Time
pTrack->nTrackID = pStream->Read(4); // Track ID
pStream->Skip(4); // const unsigned int(32) reserved = 0;
pTrack->nDuration = pStream->Read(4); // Duration
pStream->Skip(8); // const unsigned int(32)[2] reserved = 0;
pTrack->nLayer = pStream->Read(2); // Layer
pStream->Read(2); // int(16) pre-defined = 0;
pTrack->nVolume = pStream->Read(2); // Volume
pStream->Skip(2); // const unsigned int(16) reserved = 0;
pTrack->anTransMatrix[0] = pStream->Read(4); // Transformation matrix for track */
pTrack->anTransMatrix[1] = pStream->Read(4); //
pTrack->anTransMatrix[2] = pStream->Read(4); //
pTrack->anTransMatrix[3] = pStream->Read(4); //
pTrack->anTransMatrix[4] = pStream->Read(4); //
pTrack->anTransMatrix[5] = pStream->Read(4); //
pTrack->anTransMatrix[6] = pStream->Read(4); //
pTrack->anTransMatrix[7] = pStream->Read(4); //
pTrack->anTransMatrix[8] = pStream->Read(4); //
pTrack->nVisualWidth = pStream->Read(4); // Image Visual Width
pTrack->nVisualHeight = pStream->Read(4); // Image Visual Height
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with TKHD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadMDHD(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (!(MJ2_MHDR == oBox.nType || MJ2_MDHD == oBox.nType)) // Kakadu writes MHDR instead of MDHD
{
Event_Message(EVT_ERROR, "Error: Expected MDHD Marker\n");
return 1;
}
// TO DO: Сделать поодержку Version = 1
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in MDHD box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in MDHD box. Expected flag 0\n");
return false;
}
pTrack->unCreationTime = pStream->Read(4); // Creation Time
pTrack->unModificationTime = pStream->Read(4); // Modification Time
pTrack->nTimescale = pStream->Read(4); // Timescale
pTrack->nDuration = pStream->Read(4); // Duration
pTrack->nLanguage = pStream->Read(2); // Language
pStream->Skip(2); // unsigned int(16) pre-defined = 0;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with MDHD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadHDLR(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_HDLR != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected HDLR Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in HDLR box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in HDLR box. Expected flag 0\n");
return false;
}
pStream->Skip(4); // unsigned int(32) pre-defined = 0;
pTrack->nHandlerType = pStream->Read(4); // handler-type
pStream->Skip(12); // const unsigned int(32)[3] reserved = 0;
pTrack->nNameSize = oBox.nLength - 32;
pTrack->sName = (char*)Malloc(pTrack->nNameSize * sizeof(char));
for (int nIndex = 0; nIndex < pTrack->nNameSize; nIndex++)
{
pTrack->sName[nIndex] = pStream->Read(1); // Name
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with HDLR Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadVMHD(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_VMHD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected VMHD Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in VMHD box\n");
return false;
}
if (1 != pStream->Read(3)) // Flags = 1
{
Event_Message(EVT_ERROR, "Error with flag in VMHD box. Expected flag 1\n");
return false;
}
pTrack->nTrackType = 0;
pTrack->nGraphicsMode = pStream->Read(2); // graphicsmode
pTrack->anOpColor[0] = pStream->Read(2); // opcolor
pTrack->anOpColor[1] = pStream->Read(2); //
pTrack->anOpColor[2] = pStream->Read(2); //
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with VMHD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadSMHD(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_SMHD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected SMHD Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in SMHD box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in SMHD box. Expected flag 0\n");
return false;
}
pTrack->nTrackType = 1;
pTrack->nBalance = pStream->Read(2);
// Init variables to zero to avoid problems when freeeing memory
// The values will possibly be overidded when decoding the track structure
pTrack->nNumBr = 0;
pTrack->nNumUrl = 0;
pTrack->nNumUrn = 0;
pTrack->unNumChunks = 0;
pTrack->nNumTimeToSample = 0;
pTrack->nNumSamplesToChunk = 0;
pTrack->unNumSamples = 0;
pStream->Skip(2); // Reserved
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with SMHD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadHMHD(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_HMHD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected HMHD Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in HMHD box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in HMHD box. Expected flag 0\n");
return false;
}
pTrack->nTrackType = 2;
pTrack->nMaxPDUsize = pStream->Read(2); // maxPDUsize
pTrack->nAvgPDUsize = pStream->Read(2); // avgPDUsize
pTrack->nMaxBitrate = pStream->Read(4); // maxbitrate
pTrack->nAvgBitrate = pStream->Read(4); // avgbitrate
pTrack->nSlidingAvgBitrate = pStream->Read(4); // slidingavgbitrate
// Init variables to zero to avoid problems when freeeing memory
// The values will possibly be overidded when decoding the track structure
pTrack->nNumBr = 0;
pTrack->nNumUrl = 0;
pTrack->nNumUrn = 0;
pTrack->unNumChunks = 0;
pTrack->nNumTimeToSample = 0;
pTrack->nNumSamplesToChunk = 0;
pTrack->unNumSamples = 0;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with HMHD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadURL(Mj2_TrackParams* pTrack, CReader * pStream, int nUrlNum)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_URL != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected URL Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in URL box\n");
return false;
}
if (1 != pStream->Read(3)) // Если flags = 1, то медиа данные в файле
{
// TO DO: Сделать нормальное чтение строк
pTrack->pUrl[nUrlNum].anLocation[0] = pStream->Read(4);
pTrack->pUrl[nUrlNum].anLocation[1] = pStream->Read(4);
pTrack->pUrl[nUrlNum].anLocation[2] = pStream->Read(4);
pTrack->pUrl[nUrlNum].anLocation[3] = pStream->Read(4);
}
else
{
pTrack->nNumUrl--;
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with URL Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadURN(Mj2_TrackParams* pTrack, CReader * pStream, int nUrnNum)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_URN != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected URN Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in URN box\n");
return false;
}
if (1 != pStream->Read(3)) // Если flags = 1, то медиа данные в файле
{
// TO DO: Сделать нормальное чтение строк
pTrack->pUrn[nUrnNum].anName[0] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anName[1] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anName[2] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anName[3] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anLocation[0] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anLocation[1] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anLocation[2] = pStream->Read(4);
pTrack->pUrn[nUrnNum].anLocation[3] = pStream->Read(4);
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with URN Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadDREF(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_DREF != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected DREF Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in DREF box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in DREF box. Expected flag 0\n");
return 1;
}
int nEntryCount = pStream->Read(4); // entry-count
pTrack->nNumUrl = 0;
pTrack->nNumUrn = 0;
for (int nIndex = 0; nIndex < nEntryCount; nIndex++)
{
pStream->Skip(4);
int nMarker = pStream->Read(4);
if (MJ2_URL == nMarker)
{
pStream->Skip(-8);
pTrack->nNumUrl++;
if (!Mj2_ReadURL(pTrack, pStream, pTrack->nNumUrl))
return false;
}
else if (MJ2_URN == nMarker)
{
pStream->Skip(-8);
pTrack->nNumUrn++;
if (!Mj2_ReadURN(pTrack, pStream, pTrack->nNumUrn))
return false;
}
else
{
Event_Message(EVT_ERROR, "Error with in DREF box. Expected URN or URL box\n");
return false;
}
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with DREF Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadDINF(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_DINF != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected DINF Marker\n");
return false;
}
if (!Mj2_ReadDREF(pTrack, pStream))
return false;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with DINF Box size\n");
return false;
}
return true;
}
static void Mj2_DecompatTTS(Mj2_TrackParams* pTrack)
{
pTrack->unNumSamples = 0;
for (int nIndex = 0; nIndex < pTrack->nNumTimeToSample; nIndex++)
{
pTrack->unNumSamples += pTrack->pTimeToSample[nIndex].nSampleCount;
}
pTrack->pSample = (Mj2_Sample*)Malloc(pTrack->unNumSamples * sizeof(Mj2_Sample));
for (int nIndexTTS = 0; nIndexTTS < pTrack->nNumTimeToSample; nIndexTTS++)
{
for (int nIndexSample = 0; nIndexSample < pTrack->pTimeToSample[nIndexTTS].nSampleCount; nIndexSample++)
{
pTrack->pSample[nIndexSample].unSampleDelta = pTrack->pTimeToSample[nIndexTTS].nSampleDelta;
}
}
}
static void Mj2_DecompatSTSC(Mj2_TrackParams* pTrack)
{
if (1 == pTrack->nNumSamplesToChunk)
{
pTrack->unNumChunks = (unsigned int)ceil((double)pTrack->unNumSamples / (double)pTrack->pSampleToChunk[0].nSamplesPerChunk);
pTrack->pChunk = (Mj2_Chunk*)Malloc(pTrack->unNumChunks * sizeof(Mj2_Chunk));
for (unsigned int unIndex = 0; unIndex < pTrack->unNumChunks; unIndex++)
{
pTrack->pChunk[unIndex].nNumSamples = pTrack->pSampleToChunk[0].nSamplesPerChunk;
}
}
else
{
pTrack->pChunk = (Mj2_Chunk*)Malloc(pTrack->unNumChunks * sizeof(Mj2_Chunk));
pTrack->unNumChunks = 0;
int nSampleNum = 0;
for (int nIndexSTC = 0; nIndexSTC < pTrack->nNumSamplesToChunk - 1; nIndexSTC++)
{
for (int nIndexChunk = pTrack->pSampleToChunk[nIndexSTC].nFirstChunk - 1; nIndexChunk < pTrack->pSampleToChunk[nIndexSTC + 1].nFirstChunk - 1; nIndexChunk++)
{
pTrack->pChunk[nIndexChunk].nNumSamples = pTrack->pSampleToChunk[nIndexSTC].nSamplesPerChunk;
pTrack->unNumChunks++;
nSampleNum += pTrack->pChunk[nIndexChunk].nNumSamples;
}
}
long unNumChunksOld = pTrack->unNumChunks;
pTrack->unNumChunks += (int)(pTrack->unNumSamples - nSampleNum) / pTrack->pSampleToChunk[pTrack->nNumSamplesToChunk - 1].nSamplesPerChunk;
for (unsigned int unIndex = pTrack->pSampleToChunk[pTrack->nNumSamplesToChunk - 1].nFirstChunk - 1; unIndex < pTrack->unNumChunks; unIndex++)
{
pTrack->pChunk[unIndex].nNumSamples = pTrack->pSampleToChunk[pTrack->nNumSamplesToChunk - 1].nSamplesPerChunk;
}
Mj2_Chunk* pChunk_new = (Mj2_Chunk*)Malloc(pTrack->unNumChunks * sizeof(Mj2_Chunk));
if (pChunk_new)
{
memcpy(pChunk_new, pTrack->pChunk, unNumChunksOld * sizeof(Mj2_Chunk));
Free(pTrack->pChunk);
pTrack->pChunk = pChunk_new;
}
else
{
}
}
}
static void Mj2_DecompatSTCO(Mj2_TrackParams* pTrack)
{
for (unsigned int unChunk = 0; unChunk < pTrack->unNumChunks; unChunk++)
{
int nIntraChunkOffset = 0;
int nSample2 = 0;
for (int nSample = 0; nSample < pTrack->pChunk[unChunk].nNumSamples; nSample++)
{
pTrack->pSample[nSample2].unOffset = nIntraChunkOffset + pTrack->pChunk[unChunk].nOffset;
nIntraChunkOffset += pTrack->pSample[nSample2].unSampleSize;
nSample2++;
}
}
}
static bool Mj2_ReadSTTS(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STTS != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STTS Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in STTS box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in STTS box. Expected flag 0\n");
return false;
}
pTrack->nNumTimeToSample = pStream->Read(4); // entry-count
pTrack->pTimeToSample = (Mj2_TimeToSample*)Malloc(pTrack->nNumTimeToSample * sizeof(Mj2_TimeToSample));
for (int nIndex = 0; nIndex < pTrack->nNumTimeToSample; nIndex++)
{
pTrack->pTimeToSample[nIndex].nSampleCount = pStream->Read(4); // sample-count
pTrack->pTimeToSample[nIndex].nSampleDelta = pStream->Read(4); // sample-delta
}
Mj2_DecompatTTS(pTrack);
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STTS Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadSTSZ(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STSZ != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STSZ Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in STSZ box\n ");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in STSZ box. Expected flag 0\n ");
return false;
}
int nSampleSize = pStream->Read(4); // SampleSize
if (0 != nSampleSize) // У всех самплов одинаковый размер
{
pTrack->unSameSampleSize = 1;
for (unsigned int unIndex = 0; unIndex < pTrack->unNumSamples; unIndex++)
{
pTrack->pSample[unIndex].unSampleSize = nSampleSize;
}
pStream->Skip(4); // sample-count
}
else
{
pTrack->unSameSampleSize = 0;
if (pTrack->unNumSamples != pStream->Read(4)) // sample-count
{
Event_Message(EVT_ERROR, "Error in STSZ box. Expected that sample-count is number of samples in track\n ");
return false;
}
for (unsigned int unIndex = 0; unIndex < pTrack->unNumSamples; unIndex++)
{
pTrack->pSample[unIndex].unSampleSize = pStream->Read(4); // entry-size
}
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STSZ Box size\n ");
return false;
}
return true;
}
static bool Mj2_ReadSTSC(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STSC != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STSC Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in STSC box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in STSC box. Expected flag 0\n");
return false;
}
pTrack->nNumSamplesToChunk = pStream->Read(4); // entry-count
pTrack->pSampleToChunk = (Mj2_SampleToChunk*)Malloc(pTrack->nNumSamplesToChunk * sizeof(Mj2_SampleToChunk));
for (int nIndex = 0; nIndex < pTrack->nNumSamplesToChunk; nIndex++)
{
pTrack->pSampleToChunk[nIndex].nFirstChunk = pStream->Read(4); // first-chunk
pTrack->pSampleToChunk[nIndex].nSamplesPerChunk = pStream->Read(4); // samples-per-chunk
pTrack->pSampleToChunk[nIndex].nSampleDescriptionIndex = pStream->Read(4); // sample-description-index
}
Mj2_DecompatSTSC(pTrack); // decompact sample to chunk box
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STSC Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadSTCO(Mj2_TrackParams* pTrack, CReader * pStream)
{
// TO DO: Сделать чтение 'co64'
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STCO != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STCO Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in STCO box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in STCO box. Expected flag 0\n");
return false;
}
if (pStream->Read(4) != pTrack->unNumChunks) // entry-count
{
Event_Message(EVT_ERROR, "Error in STCO box: expecting same amount of entry-count as chunks \n");
return false;
}
else
{
for (unsigned int unIndex = 0; unIndex < pTrack->unNumChunks; unIndex++)
{
pTrack->pChunk[unIndex].nOffset = pStream->Read(4); // chunk-offset
}
}
Mj2_DecompatSTCO(pTrack);
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STCO Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadFIEL(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_FIEL != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected FIEL Marker\n");
return false;
}
pTrack->unFieldCount = pStream->Read(1);
pTrack->unFieldOrder = pStream->Read(1);
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with FIEL Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadJP2P(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_JP2P != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected JP2P Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in JP2P box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in JP2P box. Expected flag 0\n");
return false;
}
pTrack->nNumBr = (oBox.nLength - 12) / 4;
pTrack->pBr = (unsigned int*)Malloc(pTrack->nNumBr * sizeof(unsigned int));
for (int nIndex = 0; nIndex < pTrack->nNumBr; nIndex++)
{
pTrack->pBr[nIndex] = pStream->Read(4);
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with JP2P Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadJP2X(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_JP2X != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected JP2X Marker\n");
return false;
}
pTrack->unNumJp2x = (oBox.nLength - 8);
pTrack->pJp2xData = (unsigned char*)Malloc(pTrack->unNumJp2x * sizeof(unsigned char));
for (unsigned int unIndex = 0; unIndex < pTrack->unNumJp2x; unIndex++)
{
pTrack->pJp2xData[unIndex] = pStream->Read(1);
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with JP2X Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadJSUB(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_JSUB != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected JSUB Marker\n");
return false;
}
pTrack->unHsub = pStream->Read(1);
pTrack->unVsub = pStream->Read(1);
pTrack->unHoff = pStream->Read(1);
pTrack->unVoff = pStream->Read(1);
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with JSUB Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadORFO(Mj2_TrackParams* pTrack, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_ORFO != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected ORFO Marker\n");
return false;
}
pTrack->unOr_FieldCount = pStream->Read(1);
pTrack->unOr_FieldOrder = pStream->Read(1);
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with ORFO Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadSMJ2(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_MJ2 != oBox.nType)
{
Event_Message(EVT_ERROR, "Error in SMJ2 box: Expected MJ2 Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in MJP2 box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in MJP2 box. Expected flag 0\n");
return false;
}
pStream->Skip(4);
pStream->Skip(2); // Pre-defined
pStream->Skip(2); // Reserved
pStream->Skip(4); // Pre-defined
pStream->Skip(4); // Pre-defined
pStream->Skip(4); // Pre-defined
pTrack->nWidth = pStream->Read(2); // Width
pTrack->nHeight = pStream->Read(2); // Height
pTrack->nHorResolution = pStream->Read(4); // Horizontal resolution
pTrack->nVerResolution = pStream->Read(4); // Vertical resolution
pStream->Skip(4); // Reserved
pStream->Skip(2); // Pre-defined = 1
pTrack->anCompressorName[0] = pStream->Read(4); // Compressor Name
pTrack->anCompressorName[1] = pStream->Read(4); //
pTrack->anCompressorName[2] = pStream->Read(4); //
pTrack->anCompressorName[3] = pStream->Read(4); //
pTrack->anCompressorName[4] = pStream->Read(4); //
pTrack->anCompressorName[5] = pStream->Read(4); //
pTrack->anCompressorName[6] = pStream->Read(4); //
pTrack->anCompressorName[7] = pStream->Read(4); //
pTrack->nDepth = pStream->Read(2); // Depth
pStream->Skip(2); // Pre-defined = -1
pTrack->unNumJp2x = 0;
pTrack->unFieldCount = 1;
pTrack->unFieldOrder = 0;
pTrack->unOr_FieldCount = 1;
pTrack->unOr_FieldOrder = 0;
if (!Jp2_ReadJP2H(&pTrack->oJp2, pStream))
{
Event_Message(EVT_ERROR, "Error reading JP2H Box\n");
return false;
}
pTrack->oJp2.pComponents = (Jp2Component*)Malloc(pTrack->oJp2.nComponentsCount * sizeof(Jp2Component));
pTrack->oJp2.pCompList = (unsigned int*)Malloc(sizeof(unsigned int));
pTrack->nNumBr = 0;
pTrack->unNumJp2x = 0;
for (int nIndex = 0; pStream->Tell() - oBox.nInitPos < oBox.nLength; nIndex++)
{
Mj2_Box oBox2;
Mj2_ReadBoxHeader(&oBox2, pStream);
pStream->Seek(oBox2.nInitPos);
switch (oBox2.nType)
{
case MJ2_FIEL:
if (!Mj2_ReadFIEL(pTrack, pStream))
return false;
break;
case MJ2_JP2P:
if (!Mj2_ReadJP2P(pTrack, pStream))
return false;
break;
case MJ2_JP2X:
if (!Mj2_ReadJP2X(pTrack, pStream))
return false;
break;
case MJ2_JSUB:
if (!Mj2_ReadJSUB(pTrack, pStream))
return false;
break;
case MJ2_ORFO:
if (!Mj2_ReadORFO(pTrack, pStream))
return false;
break;
default:
Event_Message(EVT_ERROR, "Error with MJP2 Box size\n");
return false;
}
}
return true;
}
static bool Mj2_ReadSTSD(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STSD != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STSD Marker\n");
return false;
}
if (0 != pStream->Read(1)) // Version = 0
{
Event_Message(EVT_ERROR, "Error: Only Version 0 handled in STSD box\n");
return false;
}
if (0 != pStream->Read(3)) // Flags = 0
{
Event_Message(EVT_ERROR, "Error with flag in STSD box. Expected flag 0\n");
return false;
}
int nEntryCount = pStream->Read(4);
if (0 == pTrack->nTrackType)
{
for (int nIndex = 0; nIndex < nEntryCount; nIndex++)
{
if (!Mj2_ReadSMJ2(pTrack, pImage, pStream))
return false;
}
}
else if (1 == pTrack->nTrackType)
{
// TO DO: Релизовать
int nSkipLen = pStream->Read(4);
pStream->Skip(nSkipLen - 4);
}
else if (2 == pTrack->nTrackType)
{
// TO DO: Реализовать
int nSkipLen = pStream->Read(4);
pStream->Skip(nSkipLen - 4);
}
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STSD Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadSTBL(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_STBL != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected STBL Marker\n");
return false;
}
if (!Mj2_ReadSTSD(pTrack, pImage, pStream))
return false;
if (!Mj2_ReadSTTS(pTrack, pStream))
return false;
if (!Mj2_ReadSTSC(pTrack, pStream))
return false;
if (!Mj2_ReadSTSZ(pTrack, pStream))
return false;
if (!Mj2_ReadSTCO(pTrack, pStream))
return false;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with STBL Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadMINF(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_MINF != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected MINF Marker\n");
return false;
}
pStream->Skip(4);
unsigned int unBoxType = pStream->Read(4);
pStream->Skip(-8);
if (MJ2_VMHD == unBoxType)
{
if (!Mj2_ReadVMHD(pTrack, pStream))
return false;
}
else if (MJ2_SMHD == unBoxType)
{
if (!Mj2_ReadSMHD(pTrack, pStream))
return false;
}
else if (MJ2_HMHD == unBoxType)
{
if (!Mj2_ReadHMHD(pTrack, pStream))
return false;
}
else
{
Event_Message(EVT_ERROR, "Error in MINF box expected vmhd, smhd or hmhd\n");
return false;
}
if (!Mj2_ReadDINF(pTrack, pStream))
return false;
if (!Mj2_ReadSTBL(pTrack, pImage, pStream))
return false;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with MINF Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadMDIA(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_MDIA != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected MDIA Marker\n");
return false;
}
if (!Mj2_ReadMDHD(pTrack, pStream))
return false;
if (!Mj2_ReadHDLR(pTrack, pStream))
return false;
if (!Mj2_ReadMINF(pTrack, pImage, pStream))
return false;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with MDIA Box size\n");
return false;
}
return true;
}
static bool Mj2_ReadTRAK(Mj2_TrackParams* pTrack, Image* pImage, CReader * pStream)
{
Mj2_Box oBox;
Mj2_ReadBoxHeader(&oBox, pStream);
if (MJ2_TRAK != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected TRAK Marker\n");
return false;
}
if (!Mj2_ReadTKHD(pTrack, pStream))
return false;
if (!Mj2_ReadMDIA(pTrack, pImage, pStream))
return false;
if (pStream->Tell() - oBox.nInitPos != oBox.nLength)
{
Event_Message(EVT_ERROR, "Error with TRAK Box\n");
return false;
}
return true;
}
static bool Mj2_ReadMOOV(Mj2_Movie* pMovie, CReader * pStream, Image* pImage, Mj2_Box oBox)
{
if (MJ2_MOOV != oBox.nType)
{
Event_Message(EVT_ERROR, "Error: Expected FTYP Marker\n");
return false;
}
if (!Mj2_ReadMVHD(pMovie, pStream))
return false;
pMovie->pTrack = (Mj2_TrackParams*)Malloc((pMovie->nNextTrackId - 1) * sizeof(Mj2_TrackParams));
Mj2_Box oBox2;
for (unsigned int unIndex = 0; pStream->Tell() - oBox.nInitPos < oBox.nLength; unIndex++)
{
Mj2_TrackParams* pTrack = &pMovie->pTrack[unIndex];
pTrack->pCodecInfo = pMovie->pCodecInfo;
Mj2_ReadBoxHeader(&oBox2, pStream);
if (MJ2_TRAK == oBox2.nType)
{
pStream->Seek(oBox2.nInitPos);
if (!Mj2_ReadTRAK(pTrack, pImage, pStream))
return false;
if (0 == pTrack->nTrackType)
pMovie->nNumVtk++;
else if (1 == pTrack->nTrackType)
pMovie->nNumStk++;
else if (2 == pTrack->nTrackType)
pMovie->nNumHtk++;
}
else if (MJ2_MVEX == oBox2.nType)
{
pStream->Seek(oBox2.nInitPos);
pStream->Skip(oBox2.nLength);
unIndex--;
}
else
{
Event_Message(EVT_ERROR, "Error with MOOV Box: Expected TRAK or MVEX box\n");
return false;
}
}
return true;
}
static bool Mj2_ReadStruct(Mj2_Movie* pMovie, CReader * pStream, Image **ppImage)
{
if (!Mj2_ReadJP(pStream))
return false;
if (!Mj2_ReadFTYP(pMovie, pStream))
return false;
Mj2_Box oBox;
oBox.nType = 0;
Mj2_ReadBoxHeader(&oBox, pStream);
while (MJ2_MOOV != oBox.nType)
{
switch (oBox.nType)
{
case MJ2_MDAT:
// TO DO: Связать данные в MDAT с данными MOOV
if (!Mj2_ReadMDAT(pMovie, pStream, ppImage, oBox))
return false;
break;
case MJ2_MOOF:
case MJ2_FREE:
case MJ2_SKIP:
pStream->Skip(oBox.nLength);
if (pStream->GetLeftSize() < 0)
return false;
break;
default:
Event_Message(EVT_ERROR, "Unknown box in MJ2 stream\n");
pStream->Skip(oBox.nLength);
if (pStream->GetLeftSize() < 0)
return false;
break;
}
if (!Mj2_ReadBoxHeader(&oBox, pStream))
return false;
}
Image oImage;
if (!Mj2_ReadMOOV(pMovie, pStream, &oImage, oBox))
return false;
return true;
}
//-------------------------------------------------------------------------------------------------------------------------------
// Декодирование потока Mj2
//-------------------------------------------------------------------------------------------------------------------------------
void Mj2_DestroyDecompress(Mj2_Movie* pMovie)
{
if (pMovie)
{
Mj2_TrackParams* pTrack = NULL;
if (pMovie->pCodecInfo->pJ2k)
J2k_DestroyCompress(pMovie->pJ2k);
//if ( pMovie->nCompListLength != 0 )
Free(pMovie->pCompList);
for (int nIndex = 0; nIndex < pMovie->nNumVtk + pMovie->nNumStk + pMovie->nNumHtk; nIndex++)
{
pTrack = &pMovie->pTrack[nIndex];
//if ( pTrack->nNameSize != 0 )
Free(pTrack->sName);
if (pTrack->nTrackType == 0)
{
Free(pTrack->oJp2.pComponents);
Free(pTrack->oJp2.pCompList);
Free(pTrack->pJp2xData);
}
//if ( pTrack->nNumUrl != 0 )
Free(pTrack->pUrl);
//if ( pTrack->nNumUrn != 0 )
Free(pTrack->pUrn);
//if ( pTrack->nNumBr != 0 )
Free(pTrack->pBr);
//if ( pTrack->nNumTimeToSample != 0 )
Free(pTrack->pTimeToSample);
//if ( pTrack->unNumChunks != 0 )
Free(pTrack->pChunk);
//if ( pTrack->nNumSamplesToChunk != 0 )
Free(pTrack->pSampleToChunk);
//if ( pTrack->unNumSamples != 0 )
Free(pTrack->pSample);
Free(pTrack);
}
Free(pMovie);
}
}
Mj2_Movie* Mj2_CreateDecompress(PCommon pCodecInfo)
{
Mj2_Movie* pMj2 = (Mj2_Movie*)Malloc(sizeof(Mj2_Movie));
if (pMj2)
{
pMj2->pCodecInfo = (PCommon)pCodecInfo;
pMj2->pJ2k = J2k_CreateDecompress(pCodecInfo);
if (NULL == pMj2->pJ2k)
{
pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY;
Mj2_DestroyDecompress(pMj2);
return NULL;
}
}
else
{
pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY;
}
return pMj2;
}
void Mj2_SetupDecoder(Mj2_Movie* pMovie, DecoderParams *pParameters)
{
pMovie->nNumVtk = 0;
pMovie->nNumStk = 0;
pMovie->nNumHtk = 0;
J2k_SetupDecoder((J2kCodestream*)pMovie->pCodecInfo->pJ2k, pParameters);
}
Image* Mj2_Decode(Mj2_Movie* pMovie, CReader * pStream)
{
if (!pMovie || !pStream)
{
return NULL;
}
PCommon pCodecInfo = pMovie->pCodecInfo;
Image *pImage = NULL;
// Декодируем JP2
if (!Mj2_ReadStruct(pMovie, pStream, &pImage))
{
Event_Message(EVT_ERROR, "Failed to decode jp2 structure\n");
return NULL;
}
return pImage;
}
}