Files
DocumentServer-v-9.2.0/core/DesktopEditor/raster/Jp2/Mj2.h
T
Yajbir Singh f1b860b25c
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++

#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;
}
}