Example #1
0
//! Get the finished hash value
DataChunkPtr HashHMACSHA1::GetHash(void)
{
//FileClose(OutFile);
	// Build a data chunk for the output
	DataChunkPtr Ret = new DataChunk;
	Ret->Resize(20);

	SHA1_Final(Ret->Data, &Context);

	// Hash the inner hash with the outer key
	SHA1_Init(&Context);
	SHA1_Update(&Context, KeyBuffer_o, 64);
	SHA1_Update(&Context, Ret->Data, static_cast<unsigned long>(Ret->Size));
	SHA1_Final(Ret->Data, &Context);

/*
printf("Hash is:");
for(int i=0; i<20; i++)
{
  printf(" %02x", Ret->Data[i]);
}
printf("\n");
*/
	return Ret;
}
Example #2
0
//! Read a "Chunk" from a non-MXF file
DataChunkPtr mxflib::FileReadChunk(FileHandle InFile, size_t Size)
{
	DataChunkPtr Ret = new DataChunk;
	Ret->Resize(Size);

	// Read the data (and shrink chunk to fit)
	size_t Bytes = FileRead(InFile, Ret->Data, Size);
	if(Bytes == static_cast<size_t>(-1)) Bytes = 0;
	Ret->Resize(Bytes);

	return Ret;
}
Example #3
0
//! Set a data chunk from a hex string
DataChunkPtr mxflib::Hex2DataChunk(std::string Hex)
{
	// Build the result chunk
	DataChunkPtr Ret = new DataChunk();

	// Use a granularity of 16 as most hex strings are likely to be 16 or 32 bytes
	// DRAGONS: We may want to revise this later
	Ret->SetGranularity(16);

	// Index the hex string
	char const *p = Hex.c_str();

	int Size = 0;
	int Value = -1;

	// During this loop Value = -1 when no digits of a number are mid-process
	// This stops a double space being regarded as a small zero in between two spaces
	// It also stops a trailing zero being appended to the data if the last character
	// before the terminating byte is not a hex digit.
	do
	{
		int digit;
		if(*p >= '0' && *p <='9') digit = (*p) - '0';
		else if(*p >= 'a' && *p <= 'f') digit = (*p) - 'a' + 10;
		else if(*p >= 'A' && *p <= 'F') digit = (*p) - 'A' + 10;
		else if(Value == -1)
		{
			// Skip second or subsiquent non-digit
			continue;
		}
		else 
		{
			Size++;
			Ret->Resize(Size);
			Ret->Data[Size-1] = Value;

			Value = -1;
			continue;
		}

		if(Value == -1) Value = 0; else Value <<=4;
		Value += digit;

	// Note that the loop test is done in this way to force
	// a final cycle of the loop with *p == 0 to allow the last
	// number to be processed
	} while(*(p++));

	return Ret;
}
Example #4
0
/*  The hashing key is: 
 *  - trunc( HMAC-SHA-1( CipherKey, 0x00112233445566778899aabbccddeeff ) )
 *  Where trunc(x) is the first 128 bits of x
 */
DataChunkPtr BuildHashKey(size_t Size, const UInt8 *CryptoKey)
{
	//! Constant value to be hashed with cypher key to produce the hashing key
	const UInt8 KeyConst[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };

	HashPtr Hasher = new HashHMACSHA1();

	// Hash the constant data with the crypto key
	Hasher->SetKey(Size, CryptoKey);
	Hasher->HashData(16, KeyConst);
	DataChunkPtr Ret = Hasher->GetHash();

	// Truncate the hashed key to 128-bits (16-bytes)
	Ret->Resize(16);

	return Ret;
}
Example #5
0
	/*! DRAGONS: We use the current UTF16String SetString trait to ensure that we always have the correct handling,
	 *           even if the user wants these strings handled differently (i.e. we do it their way!)
	 */
	void SetStringArray(MDObjectPtr &Array, const std::list<std::string> &Strings)
	{
		// Abort if we are send a NULL pointer
		if(!Array) return;

		// Don't bother if we have nothing to do
		if(Strings.empty()) return;

		// Build a working string
		static MDTypePtr ValType = MDType::Find("UTF16String");
		MDObjectPtr Value = ValType ? new MDObject(ValType) : NULL;
		if(!Value)
		{
			error("Can't build UTF16String value required by SetStringArray() - need this type to be defined in the dictionary file\n");
			return;
		}

		// Get a buffer in which to build the final string array, make it quite granular as it will keep growing
		DataChunkPtr Buffer = new DataChunk();
		Buffer->SetGranularity(16 * 1024);

		std::list<std::string>::const_iterator it = Strings.begin();
		while(it != Strings.end())
		{
			Value->SetString(*it);
			DataChunkPtr ThisString = Value->PutData();
			Buffer->Append(ThisString);
			
			// Add terminator if required, i.e. if the sub-string we just added did not end in a zero
			UInt8 *p = &ThisString->Data[ThisString->Size];

			// Terminate if either of the last two bytes in the current buffer is non-zero - or if the string was too short to have a terminator
			if((ThisString->Size) < 2 || ((*(--p) != 0) || (*(--p) != 0)))
			{
				const UInt8 Term[2] = { 0, 0};
				Buffer->Set(2, Term, Buffer->Size);
			}

			it++;
		}

		// Set the value from this buffer
		Array->SetValue(Buffer);
	}
Example #6
0
//! Read raw index table data from this partition's source file
DataChunkPtr mxflib::Partition::ReadIndexChunk(void)
{
	DataChunkPtr Ret;

	// Locate the index table data
	if(!SeekIndex()) return Ret;

	Int64 IndexSize = GetInt64(IndexByteCount_UL);
	if(IndexSize == 0) return Ret;

	// Read the specified number of bytes
	Ret = Object->GetParentFile()->Read(static_cast<size_t>(IndexSize));

	/* Remove any trailing filler */

	// Scan backwards from the end of the index data
	if(Ret->Size >= 16)
	{
		size_t Count = Ret->Size - 15;
		UInt8 *p = &Ret->Data[Count - 1];

		// Do the scan (slightly optimized)
		while(Count--)
		{
			if(*p == 0x06)
			{
				// Do a versionless compare
				if(memcmp(p, KLVFill_UL.GetValue(), 7) == 0)
				{
					if(memcmp(&p[8], &(KLVFill_UL.GetValue()[8]), 8) == 0)
					{
						Ret->Resize(p - Ret->Data);
						break;
					}
				}
			}
			p--;
		}
	}

	return Ret;
}
Example #7
0
/*! If frame or line mapping is used the parameter Count is used to
 *	determine how many items are read. In frame wrapping it is in
 *	units of EditRate, as specified in the call to Use(), which may
 *  not be the frame rate of this essence
 *	\note This is going to take a lot of memory in clip wrapping! 
 */
DataChunkPtr mxflib::WAVE_PCM_EssenceSubParser::Read(FileHandle InFile, UInt32 Stream, UInt64 Count /*=1*/ /*, IndexTablePtr Index */ /*=NULL*/)
{
	// Move to the current position
	if(BytePosition == 0) BytePosition = DataStart;

	FileSeek(InFile, BytePosition);
	
	// Either use the cached value, or scan the stream and find out how many bytes to read
	if((CachedDataSize == static_cast<size_t>(-1)) || (CachedCount != Count)) ReadInternal(InFile, Stream, Count);

	// Record, then clear, the data size
	size_t Bytes = CachedDataSize;
	CachedDataSize = static_cast<size_t>(-1);

	// Make a datachunk with enough space
	DataChunkPtr Ret = new DataChunk(Bytes);

	// Read the data
	size_t BytesRead = static_cast<size_t>(FileRead(InFile, Ret->Data, Bytes));

	// Update the file pointer
	BytePosition = FileTell(InFile);

	// Move the edit unit pointer forward by the number of edit units read
	CurrentPosition += Count;

	// Cope with an early end-of-file
	if(BytesRead < Bytes)
	{
		DataSize = BytePosition - DataStart;
		Ret->Resize(BytesRead);

		// We need to work out where we actually ended
		CurrentPosition = CalcCurrentPosition();
	}

	return Ret; 
}
Example #8
0
/*! The primer will be <b>appended</b> to the DataChunk */
UInt32 Primer::WritePrimer(DataChunkPtr &Buffer)
{
	UInt32 Bytes;

	// Work out the primer value size first (to allow us to pre-allocate)
	UInt64 PrimerLen = UInt64(size()) * 18 + 8;

	// Re-size buffer to the probable final size
	Buffer->ResizeBuffer((UInt32)(Buffer->Size + 16 + 4 + PrimerLen));

	// Lookup the type to get the key - Static so only need to lookup once
	MDOTypePtr PrimerType = MDOType::Find(Primer_UL);
	mxflib_assert(PrimerType);

	Buffer->Append(PrimerType->GetKey());
	Bytes = static_cast<UInt32>(PrimerType->GetKey().Size);

	// Add the length
	DataChunkPtr BER = MakeBER(PrimerLen);
	Buffer->Append(*BER);
	Bytes += static_cast<UInt32>(BER->Size);

	// Add the vector header
	UInt8 Temp[4];
	PutU32(static_cast<UInt32>(size()), Temp);
	Buffer->Append(4, Temp);
	Bytes += 4;

	PutU32(18, Temp);
	Buffer->Append(4, Temp);
	Bytes += 4;

	// Write the primer data
	iterator it = begin();
	while(it != end())
	{
		PutU16((*it).first, Temp);
		Buffer->Append(2, Temp);
		Buffer->Append(16, (*it).second.GetValue());
		it++;
	}

	return Bytes;
}
Example #9
0
/*! \return Pointer to a data chunk holding the next data or a NULL pointer when no more remains
 *	\note If there is more data to come but it is not currently available the return value will be a pointer to an empty data chunk
 *	\note If Size = 0 the object will decide the size of the chunk to return
 *	\note On no account will the returned chunk be larger than MaxSize (if MaxSize > 0)
 */
DataChunkPtr WAVE_PCM_EssenceSubParser::ESP_EssenceSource::GetEssenceData(size_t Size /*=0*/, size_t MaxSize /*=0*/)
{
	WAVE_PCM_EssenceSubParser *pCaller = SmartPtr_Cast(Caller, WAVE_PCM_EssenceSubParser);

	// Allow us to differentiate the first call
	if(!Started)
	{
		Started = true;

		// Move to the selected position
		if(pCaller->BytePosition == 0) pCaller->BytePosition = pCaller->DataStart;
	}

	if(!BytesRemaining)
	{
		// Either use the cached value, or scan the stream and find out how many bytes to read
		if((pCaller->CachedDataSize == static_cast<size_t>(-1)) || (pCaller->CachedCount != RequestedCount)) pCaller->ReadInternal(File, Stream, RequestedCount);

		// Record, then clear, the data size
		BytesRemaining = pCaller->CachedDataSize;
		pCaller->CachedDataSize = static_cast<size_t>(-1);

		// Flag all done when no more to read
		if(BytesRemaining == 0)
		{
			// Undo removing the size by calling SamplesThisEditUnit so that the padding sequence stays corrent
			if(PaddingEnabled) pCaller->PushBackSize();

			AtEndOfData = true;
			return NULL;
		}
	}

	// Decide how many bytes to read this time - start by trying to read them all
	size_t Bytes = BytesRemaining;

	// Hard limit to MaxSize
	if((MaxSize != 0) && (Bytes > MaxSize))
	{
		Bytes = MaxSize;
	}

	// Also limit to Size
	if((Size != 0) && (Bytes > Size))
	{
		Bytes = Size;
	}

	// Remove this number of bytes from the remaining count
	BytesRemaining -= Bytes;

	// Seek to the current position
	FileSeek(File, pCaller->BytePosition);

	// Read the data
	DataChunkPtr Ret = FileReadChunk(File, Bytes);

	// Update the file pointer
	pCaller->BytePosition = FileTell(File);

	// Move the edit unit pointer forward by the number of edit units read (if the last part of a read)
	if(!BytesRemaining)	
	{
		
		// Only do a simple add if not reading the whole clip, and if the read succeeded
		if((pCaller->SelectedWrapping->ThisWrapType==WrappingOption::Frame) 
			&& (Ret->Size == Bytes))
				pCaller->CurrentPosition += RequestedCount;
		// ... otherwise calculate the new position
		else pCaller->CurrentPosition=pCaller->CalcCurrentPosition();
	}

	// If we get too few bytes - and padding has been selected, padd this wrapping unit
	if((Ret->Size < Bytes) && PaddingEnabled)
	{
		size_t OldSize = Ret->Size;
		Ret->Resize(Bytes);
		memset(&Ret->Data[OldSize], 0, Bytes - OldSize);
	}

	return Ret;
}
Example #10
0
/*! \param Offset Offset from the start of the KLV value from which to start reading
 *  \param Size Number of bytes to read, if -1 all available bytes will be read (which could be billions!)
 *  \return The number of bytes read
 */
size_t KLVEObject::ReadDataFrom(Position Offset, size_t Size /*=-1*/)
{
	// Don't decrypt if we have no decryption wrapper
	if(!Decrypt) return Base_ReadDataFrom(Offset, Size);

	// Load the header if required (and if we can!)
	if(!DataLoaded) if(!LoadData()) return 0;

	// Don't bother reading zero bytes or off the end of the value
	if((Size == 0) || (Offset >= ValueLength))
	{
		Data.Resize(0);
		return 0;
	}

	// Load the IV and check the Check value if this is the first read
	if(	CurrentReadOffset == 0)
	{
		if( Base_ReadDataFrom(DataOffset - EncryptionOverhead, EncryptionOverhead) < EncryptionOverhead)
		{
			error("Unable to read Initialization Vector and Check Value in KLVEObject::ReadDataFrom()\n");
			return 0;
		}

		// Update the current hash if we are calculating one
		if(ReadHasher) ReadHasher->HashData(Data);

		// Initialize the decryption engine with the specified Initialization Vector
		Decrypt->SetIV(16, Data.Data, true);

		// Decrypt the check value...
		DataChunkPtr PlainCheck = Decrypt->Decrypt(16, &Data.Data[16]);

		// Encrypt the check value... (Which is "CHUKCHUKCHUKCHUK" who ever said Chuck Harrison has no ego?)
		const UInt8 DefinitivePlainCheck[16] = { 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B };
		if((!PlainCheck) || (memcmp(PlainCheck->Data, DefinitivePlainCheck, PlainCheck->Size) != 0))
		{
			error("Check value did not correctly decrypt in KLVEObject::ReadDataFrom() - is the encryption key correct?\n");
			return 0;
		}
	}

	// If all the requested bytes are encrypted read-and-decrypt
	if(Offset >= PlaintextOffset) 
	{
		// Check if an attempt is being made to random access the encrypted data - and barf if this is so
		if(Offset != CurrentReadOffset)
		{
			error("Attempt to perform random-access reading of an encrypted KLV value field\n");
			return 0;
		}

		size_t Ret = ReadCryptoDataFrom(Offset, Size);

		// Read the AS-DCP footer if we have read the last of the data
		if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) { Ret = 0; Data.Resize(0); }

		return Ret;
	}

	// If all the bytes requested are plaintext use the base read
	if( (Size != static_cast<size_t>(-1)) && ((Offset + Size) < PlaintextOffset) )
	{
		// Check if an attempt is being made to random access the plaintext while hashing - and barf if this is so
		if(ReadHasher && (Offset != CurrentReadOffset))
		{
			error("Attempt to perform random-access reading of an encrypted KLV value field\n");
			return 0;
		}

		size_t Ret = Base_ReadDataFrom(DataOffset + Offset, Size);

		// Update the read pointer (it is possible to random access within the plaintext area)
		CurrentReadOffset = Offset + Ret;

		// Update the current hash if we are calculating one
		if(ReadHasher) ReadHasher->HashData(Data);

		// Read the AS-DCP footer if we have read the last of the data
		if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) { Ret = 0; Data.Resize(0); }

		return Ret;
	}

	/* We will be mixing plaintext and encrypted */

	// Check if an attempt is being made to random access the encrypted data - and barf if this is so
	// DRAGONS: It is possible to re-load the initial IV to allow a "rewind" but this is not implemented
	if(CurrentReadOffset > PlaintextOffset)
	{
		error("Attempt to perform random-access reading of an encrypted KLV value field\n");
		return 0;
	}

	// Determine how many plaintext bytes could be available (maximum)
	Length PlainSize = PlaintextOffset - Offset;

	// Validate the size
	if((sizeof(size_t) < 8) && (PlainSize > 0xffffffff))
	{
		error("Encrypted KLV contains > 4GBytes of plaintext, but this platform can only handle <= 4GByte chunks\n");
		return 0;
	}

	// Try and read them all
	size_t PlainBytes = Base_ReadDataFrom(DataOffset + Offset, static_cast<size_t>(PlainSize));

	// Update the current hash if we are calculating one
	if(ReadHasher) ReadHasher->HashData(Data);

	// If we couldn't read them all then the data ends before any encrypted bytes so we can exit now
	if(PlainBytes < static_cast<size_t>(PlainSize))
	{
		// DRAGONS: We don't read the footer if we ran out of data early... should we issue an error or a warning?
		//# Read the AS-DCP footer if we have read the last of the data
		//# if(CurrentReadOffset >= ValueLength) ReadFooter();

		return PlainBytes;
	}

	/* We have all the plaintext bytes from Offset forwards, now we read all encrypted bytes too */

	// Take the buffer from the current DataChunk to preserve it
	DataChunkPtr PlainData = new DataChunk;
	PlainData->TakeBuffer(Data, true);

	// Work out how many encrypted bytes to read
	size_t EncSize;
	if(Size == static_cast<size_t>(-1)) EncSize = Size; else EncSize = Size - static_cast<size_t>(PlainSize);

	// Read the encrypted bytes
	ReadCryptoDataFrom(PlaintextOffset, EncSize);

	// Append the decrypted data to the plaintext data
	PlainData->Append(Data);

	// Transfer this data to the "current" DataChunk
	Data.TakeBuffer(PlainData);

	// Set the "next" position to just after the end of what we read
	CurrentReadOffset = Offset + Data.Size;

	// Read the AS-DCP footer if we have read the last of the data
	if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) Data.Resize(0);

	// Return the total number of bytes
	return Data.Size;
}
Example #11
0
/*! \return false on error, else true
 *
 *  DRAGONS: Ensure that the data written matches the size given by CalcFooterLength()
 */
bool KLVEObject::WriteFooter(void)
{
	// Don't write anything if we don't need to
	if((!TrackFileID) && (!HasSequenceNumber) && (!WriteHasher)) return true;

	// Make a reasonable sized buffer
	DataChunkPtr Buffer = new DataChunk(64);

	// Make a pointer to walk through the write buffer
	unsigned char *p = Buffer->Data;

	if(!TrackFileID)
	{
		// Write a "missing" TrackFile ID
		p += MakeBER(p, 4, 0);
	}
	else
	{
		// Write the TrackFile ID
		p += MakeBER(p, 4, 16);
		memcpy(p, TrackFileID->GetValue(), 16);
		p += 16;
	}

	if(!HasSequenceNumber)
	{
		// Write a "missing" sequence number
		p += MakeBER(p, 4, 0);
	}
	else
	{
		// Write the sequence number
		p += MakeBER(p, 4, 8);
		PutU64(SequenceNumber, p);
		p += 8;
	}

	// DRAGONS: We don't bother to write a "missing" MIC as this is the last item so can just be omitted
	if(WriteHasher)
	{
		// Write the BER length for the hash - general concensus seems to be that this is included IN the hash!
		p += MakeBER(p, 4, 20);

		// Finish calculating the MIC
		WriteHasher->HashData((int)(p - Buffer->Data), Buffer->Data);

		// Get the hash
		DataChunkPtr Hash = WriteHasher->GetHash();

		// Write the hash
		if(Hash->Size == 20) memcpy(p, Hash->Data, 20);
		else error("Hash for this KLVEObject is not 20 bytes\n");
		p += 20;
	}

	// Resize the buffer to exactly the amount of data we built
	mxflib_assert((UInt32)(p - Buffer->Data) == FooterLength);
	Buffer->Resize((int)(p - Buffer->Data));

	// Write the footer
	Dest.File->Write(Buffer);

	// Return "All OK"
	return true;
}
Example #12
0
void
JPEGLSCodec::compress(const FrameBuffer &frame)
{
	const Box2i dataW = dataWindow();
	
	const int width = (dataW.max.x - dataW.min.x + 1);
	const int height = (dataW.max.y - dataW.min.y + 1);

	const PixelType pixType = (_depth == JPEGLS_8 ? UINT8 :
								_depth == JPEGLS_10 ? UINT10 :
								_depth == JPEGLS_12 ? UINT12 :
								_depth == JPEGLS_16 ? UINT16 :
								UINT8);
	
	const size_t pixSize = PixelSize(pixType);
	const unsigned int bitDepth = PixelBits(pixType);
	const int numChannels = (_channels == JPEGLS_RGBA ? 4 : 3);
	
	const size_t tempPixelSize = (numChannels * pixSize);
	const size_t tempRowbytes = (tempPixelSize * width);
	const size_t tempBufSize = (tempRowbytes * height);
	
	DataChunk dataChunk(tempBufSize);
	
	char *tempBuffer = (char *)dataChunk.Data;
	
	assert(dataW.min.x == 0 && dataW.min.y == 0);
	
	FrameBuffer tempFrameBuffer(dataW);
	
	tempFrameBuffer.insert("R", Slice(pixType, &tempBuffer[0 * pixSize], tempPixelSize, tempRowbytes));
	tempFrameBuffer.insert("G", Slice(pixType, &tempBuffer[1 * pixSize], tempPixelSize, tempRowbytes));
	tempFrameBuffer.insert("B", Slice(pixType, &tempBuffer[2 * pixSize], tempPixelSize, tempRowbytes));
	
	if(_channels == JPEGLS_RGBA)
		tempFrameBuffer.insert("A", Slice(pixType, &tempBuffer[3 * pixSize], tempPixelSize, tempRowbytes));
	
	tempFrameBuffer.copyFromFrame(frame);
	
	
	JlsParameters params = JlsParameters();
	
	params.width = width;
	params.height = height;
	params.bitspersample = bitDepth;
	//params.bytesperline = (tempRowbytes / 3);
	params.components = numChannels;
	params.allowedlossyerror = 0; // always lossless
	params.ilv = ILV_SAMPLE;
	params.colorTransform = COLORXFORM_NONE;
	//params.outputBgr = 0;
	
	/*
	params.custom.MAXVAL = 255;
	params.custom.T1 = 0;
	params.custom.T2 = 0;
	params.custom.T3 = 0;
	params.custom.RESET = 1;
	
	
	params.jfif.Ver = 123;
	params.jfif.units = 0;
	params.jfif.XDensity = 72;
	params.jfif.YDensity = 72;
	params.jfif.Xthumb = 0;
	params.jfif.Ythumb = 0;
	params.jfif.pdataThumbnail = NULL;
	*/
	
	
	ByteStreamInfo inStream = FromByteArray(dataChunk.Data, dataChunk.Size);
	
	DataChunkPtr outDataChunk = new DataChunk(tempBufSize);
	
	
	size_t bytesWritten = 0;
	
	JLS_ERROR err = OK;
	
	do
	{
		ByteStreamInfo outStream = FromByteArray(outDataChunk->Data, outDataChunk->Size);
		
		err = JpegLsEncodeStream(outStream, &bytesWritten, inStream, &params);
		
		if(err == CompressedBufferTooSmall)
		{
			outDataChunk->Resize(2 * outDataChunk->Size, false);
		}
	
	}while(err == CompressedBufferTooSmall);
	
	assert(err != TooMuchCompressedData);
	
	
	if(err == OK)
	{
		assert(bytesWritten > 0);
	
		outDataChunk->Resize(bytesWritten);
		
		storeData(outDataChunk);
	}
	else
		throw MoxMxf::ArgExc("JPEG-LS compression error");
}
Example #13
0
/*! This will attempt to return an entire wrapping unit (e.g. a full frame for frame-wrapping) but will return it in
 *  smaller chunks if this would break the MaxSize limit. If a Size is specified then the chunk returned will end at
 *  the first wrapping unit end encountered before Size. On no account will portions of two or more different wrapping
 *  units be returned together. The mechanism for selecting a type of wrapping (e.g. frame, line or clip) is not 
 *  (currently) part of the common EssenceSource interface.
 *  \return Pointer to a data chunk holding the next data or a NULL pointer when no more remains
 *	\note If there is more data to come but it is not currently available the return value will be a pointer to an empty data chunk
 *	\note If Size = 0 the object will decide the size of the chunk to return
 *	\note On no account will the returned chunk be larger than MaxSize (if MaxSize > 0)
 */
DataChunkPtr ANCVBISource::GetEssenceData(size_t Size /*=0*/, size_t MaxSize /*=0*/)
{
	// Once this read is done we will be in sync with the master stream position
	CurrentPosition = MasterSource->GetCurrentPosition();

	// If we don't yet have any data prepared, prepare some (even if this will be an "empty" chunk)
	if(BufferedData.empty())
	{
		if((!MasterSource) || (MasterSource->EndOfData())) return NULL;

		BufferedData.push_back(BuildChunk());
	}

	/* Handle the simple case first:
	 * - We are allowed to decide how much data to return (one frame)
	 * - We are not already part way through a buffer
	 * - We are permitted to return the whole buffer in one go
	 */
	if(/*(Size == 0) && */(BufferOffset == 0) && ((MaxSize == 0) || (BufferedData.front()->Size <= MaxSize)))
	{
		// We will return the head item
		DataChunkPtr Ret = BufferedData.front();

		// Remove this item from the list of buffers
		BufferedData.pop_front();

		// Return it
		return Ret;
	}

	// First see how many bytes remain
	size_t Bytes = BufferedData.front()->Size - BufferOffset;
	
	// If we can return all these now, do so
	if(Bytes <= MaxSize)
	{
		// Build a new buffer to hold the reduced data
		DataChunkPtr Ret = new DataChunk(Bytes);

		// Copy in the remaining bytes
		Ret->Set(static_cast<UInt32>(Bytes), BufferedData.front()->Data);

		// Remove this item from the list of buffers
		BufferedData.pop_front();

		// Clear the buffer offset as we will start at the beginning of the next chunk
		BufferOffset = 0;

		// Return the data
		return Ret;
	}

	// Build a new buffer to hold the reduced data
	DataChunkPtr Ret = new DataChunk(MaxSize);

	// Copy in as many bytes as permitted
	Ret->Set(static_cast<UInt32>(MaxSize), BufferedData.front()->Data);

	// Update the offset
	BufferOffset -= MaxSize;

	// Return the data
	return Ret;
}
Example #14
0
//! Build the data for this frame in SMPTE-436M format
DataChunkPtr ANCVBISource::BuildChunk(void)
{
	/* Fill lines from line sources */

	ANCVBILineSourceList::iterator LS_it = Sources.begin();
	while(LS_it != Sources.end())
	{
		int LineNumber = (*LS_it)->GetLineNumber();
		if((*LS_it)->GetField() == 2) LineNumber += Field2Offset();

		Lines.insert(ANCLineMap::value_type(
				LineNumber, 
				new ANCLine(LineNumber, 
				(*LS_it)->GetWrappingType(), 
				(*LS_it)->GetSampleCoding(), 
				(*LS_it)->GetLineData(), 
				(*LS_it)->GetDID(), 
				(*LS_it)->GetSDID())));

		LS_it++;
	}


	/* Now build the chunk from line data */

	/* First we handle the special case of no lines this frame (should be quite common) */
	if(Lines.empty())
	{
		// Simply return "Number of Lines = 0"
		DataChunkPtr Ret = new DataChunk(2);
		PutU16(0, Ret->Data);

		return Ret;
	}

	VBILineMap::iterator it = Lines.begin();

	// Guess the buffer size by assuming that all the lines are the same size, then add 2 bytes for the line count
	// DRAGONS: If the line sizes do vary this is a bottle-neck
	// DRAGONS: We will use this as a remaining-bytes counter while writing the data
	size_t BufferSize = ((*it).second->GetFullDataSize() * Lines.size()) + 2;

	// Get a buffer of this size
	DataChunkPtr Ret = new DataChunk(BufferSize);

	// Index the start of the buffer
	UInt8 *pBuffer = Ret->Data;

	// Write in the number of lines
	PutU16(static_cast<UInt16>(Lines.size()), Ret->Data);
	pBuffer += 2;
	BufferSize -= 2;

	while(it != Lines.end())
	{
		// Get the number of bytes required to add this line to the buffer
		size_t RequiredBytes = (*it).second->GetFullDataSize();

		// If we don't have enough space we must increase the buffer size - can only happen if lines differ in size
		if(RequiredBytes > BufferSize)
		{
			// Work out how far through the buffer we currently are
			size_t CurrentPos = pBuffer - Ret->Data;

			// Make the buffer big enough for this line
			Ret->Resize(static_cast<UInt32>(CurrentPos + RequiredBytes));

			// Flag that we now have just enough bytes left
			BufferSize = RequiredBytes;

			// Set the buffer pointer in the (possibly re-allocated) buffer
			pBuffer = &Ret->Data[CurrentPos];
		}

		// Write the data into the buffer (formatted by the VBILine itself)
		(*it).second->WriteData(pBuffer);

		// Update the buffer pointer and bytes-remaining count
		pBuffer += RequiredBytes;
		BufferSize -= RequiredBytes;

		it++;
	}

	// Resize the buffer to the actual number of bytes that we wrote
	Ret->Resize(static_cast<UInt32>(pBuffer - Ret->Data));

	// Clear the list of pending lines
	Lines.clear();

	// Return the finished data
	return Ret;
}