VideoCodec/Modules/Pipeline/Pipeline.cpp
#include "Pipeline.h"
using namespace std;
namespace VideoCodec
{
Pipeline::Pipeline() : Module(PIPELINE_MODULE_ID)
{
}
Pipeline::~Pipeline()
{
}
ConfigurationStatus Pipeline::Configure(ConfigurationElement* configurationElement)
{
if (configurationElement != NULL && configurationElement->GetValue("type") != NULL)
{
if (*configurationElement->GetValue("type")->GetString() == string("simpleFrames"))
this->type = SIMPLE_FRAMES_PIPELINE;
else if (*configurationElement->GetValue("type")->GetString() == string("encodeFramesToFile"))
this->type = ENCODE_FRAMES_TO_FILE;
else if (*configurationElement->GetValue("type")->GetString() == string("decodeFileToFrames"))
this->type = DECODE_FILE_TO_FRAMES;
else if (*configurationElement->GetValue("type")->GetString() == string("encodeFramesToFileWithPrediction"))
this->type = ENCODE_FRAMES_TO_FILE_WITH_PREDICTION;
this->diagnosticOutputResiduals = (configurationElement->GetValue("diagnosticOutputResiduals") != NULL && *(configurationElement->GetValue("diagnosticOutputResiduals")->GetString()) == "true");
this->diagnosticShowVectors = (configurationElement->GetValue("diagnosticShowVectors") != NULL && *(configurationElement->GetValue("diagnosticShowVectors")->GetString()) == "true");
this->diagnosticPerformanceTimingInLoop = (configurationElement->GetValue("diagnosticPerformanceTimingInLoop") != NULL && *(configurationElement->GetValue("diagnosticPerformanceTimingInLoop")->GetString()) == "true");
this->diagnosticPerformanceTimingPerLoop = (configurationElement->GetValue("diagnosticPerformanceTimingPerLoop") != NULL && *(configurationElement->GetValue("diagnosticPerformanceTimingPerLoop")->GetString()) == "true");
this->diagnosticPerformanceTimingGlobal = (configurationElement->GetValue("diagnosticPerformanceTimingGlobal") != NULL && *(configurationElement->GetValue("diagnosticPerformanceTimingGlobal")->GetString()) == "true");
this->diagnosticPerformanceBits = (configurationElement->GetValue("diagnosticPerformanceBits") != NULL && *(configurationElement->GetValue("diagnosticPerformanceBits")->GetString()) == "true");
this->mv4 = (configurationElement->GetValue("mv4") != NULL && *(configurationElement->GetValue("mv4")->GetString()) == "true");
return MODULE_CONFIGURATION_OK;
}
else
{
this->type = INVALID_PIPELINE;
this->diagnosticOutputResiduals = false;
this->diagnosticPerformanceTimingInLoop = false;
this->diagnosticPerformanceTimingPerLoop = false;
this->diagnosticPerformanceTimingGlobal = false;
return MODULE_CONFIGURATION_ERROR;
}
}
void Pipeline::Run(Codec* codec)
{
// Based on the choice of coding path, get a set of modules and pass a PictureInfo instance through.
if (this->type == SIMPLE_FRAMES_PIPELINE)
{
// The simple frames pipeline takes a set of image files, and prepares them for coding.
// The frames are then decompressed and output to individual frame files.
// Retrieve configured modules from the encoder.
FrameInputModule* input = (FrameInputModule*)codec->GetModule(std::string(FRAME_INPUT_MODULE_ID));
FrameOutputModule* output = (FrameOutputModule*)codec->GetModule(std::string(FRAME_OUTPUT_MODULE_ID));
ColourSpaceConverter* yuvConverter = (ColourSpaceConverter*)codec->GetModule(std::string(COLOUR_SPACE_CONVERTER_MODULE_ID));
MacroblockHandler* macroblockHandler = (MacroblockHandler*)codec->GetModule(std::string(MACROBLOCK_HANDLER_MODULE_ID));
AANDCT* dct = (AANDCT*)codec->GetModule(std::string(AAN_DCT_MODULE_ID));
DCTCoefficientQuantizer* dctCoefficientQuantizer = (DCTCoefficientQuantizer*)codec->GetModule(std::string(DCT_COEFFICIENT_QUANTIZER_MODULE_ID));
// Check that the required modules are non-NULL.
if (input == NULL || output == NULL ||
yuvConverter == NULL || macroblockHandler == NULL || dct == NULL || dctCoefficientQuantizer == NULL)
{
Logger::LogMessage("One of the modules required by this pipeline could not be retrieved.");
return;
}
int framesProcessedCount = 0;
while (input->HasFrames())
{
// Get scan order pixel data in RGB format.
PictureInfo* thisFrame = input->FetchNextFrame();
thisFrame->SetFrameType(FRAME_TYPE_INTRA);
// Convert to YUV.
yuvConverter->ConvertRGBToYUVRepresentation(thisFrame, codec);
// Change the pixel layout from scan order to macroblock layout.
macroblockHandler->ConvertScanToMacroblockLayout(thisFrame);
// Perform a discrete cosine transform to compress entropy in the top left of each block.
dct->PictureDCT(thisFrame);
// Quantize the coefficients in place.
dctCoefficientQuantizer->QuantizeDCTCoefficients(thisFrame, false);
// Dequantize the coefficients in place.
dctCoefficientQuantizer->DequantizeDCTCoefficients(thisFrame, false);
// Perform an inverse discrete cosine transform to get back to the normal spatial representation for each block.
dct->PictureIDCT(thisFrame);
// Change the pixel layout back to the scan order.
macroblockHandler->ConvertMacroblockToScanLayout(thisFrame);
// Go back to the RGB colour space.
yuvConverter->ConvertYUVToRGBRepresentation(thisFrame);
// Write out the data.
output->WriteImage(thisFrame, framesProcessedCount);
// Clean up the picture information.
delete thisFrame;
// Processing of this frame is complete.
Logger::LogMessage("Processed a frame.");
framesProcessedCount++;
}
}
else if (this->type == ENCODE_FRAMES_TO_FILE || this->type == ENCODE_FRAMES_TO_FILE_WITH_PREDICTION)
{
this->encodePath(codec);
}
else if (this->type == DECODE_FILE_TO_FRAMES)
{
this->decodePath(codec);
}
else
{
Logger::LogMessage("Unsupported pipeline selected.");
}
}
void Pipeline::codeFrame(PictureInfo* thisFrame, FrameDataBitLength codedLength, StreamFormatter* formatter, HuffmanEntropyEncoder* entropyEncoder, DCTCoefficientQuantizationMethod quantizationMethod, UnsignedByte quantizationScale, bool mv4)
{
// Output the frame header data.
formatter->OutputFrameHeader(thisFrame->GetFrameType() == FRAME_TYPE_INTER, quantizationMethod, quantizationScale);
// Build the VLC stream.
BinaryStreamBuilder* streamBuilder = new BinaryStreamBuilder();
streamBuilder->AllocateStream(codedLength);
entropyEncoder->EncodeFrame(thisFrame, streamBuilder, mv4);
// Finish writing to the stream by aligning with the next byte boundary, then get the data.
streamBuilder->AlignStream();
// Output the data into the file stream.
unsigned char* bitStream = streamBuilder->GetStream();
formatter->OutputFrame(bitStream, streamBuilder->GetWrittenBitCount() >> 3);
delete [] bitStream;
delete streamBuilder;
}
void Pipeline::encodePath(Codec* codec)
{
// If this is false, all frames will be key-frames.
bool prediction = this->type == ENCODE_FRAMES_TO_FILE_WITH_PREDICTION;
// Retrieve configured modules from the encoder.
FrameInputModule* input = (FrameInputModule*)codec->GetModule(std::string(FRAME_INPUT_MODULE_ID));
StreamOutputModule* output = (StreamOutputModule*)codec->GetModule(std::string(STREAM_OUTPUT_MODULE_ID));
StreamFormatter* formatter = (StreamFormatter*)codec->GetModule(std::string(STREAM_FORMATTER_MODULE_ID));
ColourSpaceConverter* yuvConverter = (ColourSpaceConverter*)codec->GetModule(std::string(COLOUR_SPACE_CONVERTER_MODULE_ID));
MacroblockHandler* macroblockHandler = (MacroblockHandler*)codec->GetModule(std::string(MACROBLOCK_HANDLER_MODULE_ID));
AANDCT* dct = (AANDCT*)codec->GetModule(std::string(AAN_DCT_MODULE_ID));
DCTCoefficientQuantizer* dctCoefficientQuantizer = (DCTCoefficientQuantizer*)codec->GetModule(std::string(DCT_COEFFICIENT_QUANTIZER_MODULE_ID));
HuffmanEntropyEncoder* entropyEncoder = (HuffmanEntropyEncoder*)codec->GetModule(std::string(HUFFMAN_ENTROPY_ENCODER_MODULE_ID));
ProgressInformation* progress = (ProgressInformation*)codec->GetModule(std::string(PROGRESS_INFORMATION_MODULE_ID));
External* external = (External*)codec->GetModule(std::string(EXTERNAL_MODULE_ID));
// For predictive frames:
GOPManager* gopManager = NULL;
FrameDifference* frameDifference = NULL;
MotionEstimation* motionEstimation = NULL;
RateControl* rateControl = NULL;
if (prediction)
{
gopManager = (GOPManager*)codec->GetModule(std::string(GOP_MANAGER_MODULE_ID));
frameDifference = (FrameDifference*)codec->GetModule(std::string(FRAME_DIFFERENCE_MODULE_ID));
motionEstimation = (MotionEstimation*)codec->GetModule(std::string(MOTION_ESTIMATION_MODULE_ID));
rateControl = (RateControl*)codec->GetModule(std::string(RATE_CONTROL_MODULE_ID));
if (this->mv4)
motionEstimation->EnableMV4();
}
// Check that the required modules are non-NULL.
if (input == NULL || output == NULL ||
yuvConverter == NULL || macroblockHandler == NULL || dct == NULL || dctCoefficientQuantizer == NULL || entropyEncoder == NULL ||
progress == NULL || external == NULL ||
(prediction && (gopManager == NULL || frameDifference == NULL || motionEstimation == NULL || rateControl == NULL)))
{
Logger::LogMessage("One of the modules required by this pipeline was not specified. Make sure that it is enabled at the command line.");
return;
}
// To write the data to the stream:
// 1. Open the stream output file and write the format header.
// 2. Write a stream header based on the first frame's dimensions.
// 3. Write frame headers then frame data, for each frame in the stream.
//
// The GOPManager holds a reference frame which consists of byte-based pixel data in scan order.
// Open the output stream for writing. Attach the stream to the stream formatter.
output->OpenFile();
formatter->SetOutputModule(output);
// Write the file format information header.
formatter->OutputFormatHeader();
// Keep a reference frame in unsigned byte format.
PictureInfo* reference = NULL;
// Performance details in a map. Each integer value has an associated string and integer. startTimes are also stored for performance timers.
std::map<pair<const char*, FrameCount>, int64_t> performanceDetails;
int64_t startTimes[10];
startTimes[0] = startTimes[1] = startTimes[2] = startTimes[3] = 0;
// Keep track of how many frames we've processed, and the number of bits encoded (will only be valid when performance bit counting is enabled).
FrameCount framesProcessedCount = 0;
int64_t performanceStreamBits = 0;
// Set up global timing.
if (this->diagnosticPerformanceTimingGlobal)
PERFORMANCE_TIMING_BEFORE(1);
while (input->HasFrames())
{
// The current frame.
PictureInfo* thisFrame = input->FetchNextFrame();
// Configure this frame based on the input frame and the status of prediction/the reference frame.
thisFrame->SetFrameType(!prediction || reference == NULL || (gopManager->RequireIFrame()) ? FRAME_TYPE_INTRA : FRAME_TYPE_INVALID);
thisFrame->SetBlockColumns(thisFrame->GetPixelsX() >> 4);
thisFrame->SetBlockRows(thisFrame->GetPixelsY() >> 4);
// Output the stream header if this is the first frame.
if (framesProcessedCount == 0)
formatter->OutputStreamHeader(input->GetTotalFrameCount(), thisFrame->GetBlockColumns(), thisFrame->GetBlockRows(), this->mv4, motionEstimation->UseSubPixel());
// Frame timing.
if (this->diagnosticPerformanceTimingPerLoop)
{
PERFORMANCE_TIMING_BEFORE(2);
}
// Convert to YUV.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
yuvConverter->ConvertRGBToYUVRepresentation(thisFrame, codec);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Colour space conversion",framesProcessedCount);
if (thisFrame->GetFrameType() == FRAME_TYPE_INTRA)
{
// This frame is encoded as a key-frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertScanToMacroblockLayout(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: To macroblock layout",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureDCT(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Whole picture DCT",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->QuantizeDCTCoefficients(thisFrame, false);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Coefficient quantization",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
FrameDataBitLength codedLength = entropyEncoder->GetFrameCodedLength(thisFrame, mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Entropy coding length only",framesProcessedCount);
PERFORMANCE_BITS((int64_t)codedLength, "Required I-frame: Bits coded", framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
this->codeFrame(thisFrame, codedLength, formatter, entropyEncoder, dctCoefficientQuantizer->GetIntraQuantizationMethod(), dctCoefficientQuantizer->GetIntraQuantizationScale(), mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Entropy coding",framesProcessedCount);
// Compute the new reference frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->DequantizeDCTCoefficients(thisFrame, false);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Coefficient dequantization for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureIDCT(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: Whole picture IDCT for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertMacroblockToScanLayout(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Required I-frame: To scan layout for reference",framesProcessedCount);
// We no longer require the old reference. Set the reference to be this frame.
if (reference != NULL)
delete reference;
reference = thisFrame;
gopManager->Reset();
Logger::LogMessage("I");
}
else
{
// Prepare the residual frame.
PictureInfo* residual = thisFrame->Clone();
thisFrame->SetFrameType(FRAME_TYPE_INTRA);
residual->SetFrameType(FRAME_TYPE_INTER);
// Motion estimation for the reference frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
motionEstimation->CalculateMotionVectors(reference, residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Motion estimation",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
motionEstimation->ApplyMotionVectors(reference, residual->GetMVDisplacementsX(), residual->GetMVDisplacementsY());
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Motion compensation",framesProcessedCount);
#ifdef PIPELINE_DIAGNOSTICS
// To show a vector field representation of motion vectors
if (this->diagnosticShowVectors)
{
Logger::LogMessage("Diagnostics: Outputting motion vectors. (If nothing apppears, please use a smaller frame size.)");
external->ShowVectorField(residual->GetMVDisplacementsX(), residual->GetMVDisplacementsY(), thisFrame->GetBlockColumns(), thisFrame->GetBlockRows(), 8);
}
#endif
// Prepare a residual frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
frameDifference->SubtractFrame(residual, reference);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Frame subtraction",framesProcessedCount);
// Work out whether to code a P frame, if rate control is enabled.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
bool codeP = !(rateControl->IsEnabled() && rateControl->ShouldEncodeI(residual));
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Rate control",framesProcessedCount);
FrameDataBitLength residualLength = 0;
FrameDataBitLength intraLength = 0;
// Transform the residual frame.
if (!rateControl->IsEnabled() || codeP)
{
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertSignedScanToSignedMacroblockLayout(residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Scan to macroblock layout",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureDCT(residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Whole frame DCT",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->QuantizeDCTCoefficients(residual, true);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Coefficient quantization",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
residualLength = entropyEncoder->GetFrameCodedLength(residual, this->mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Residual coding length only",framesProcessedCount);
}
// Transform the intra frame.
if (!rateControl->IsEnabled() || !codeP)
{
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertScanToMacroblockLayout(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Scan to macroblock layout (intra)",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureDCT(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Whole frame DCT (intra)",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->QuantizeDCTCoefficients(thisFrame, false);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Coefficient quantization (intra)",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
intraLength = entropyEncoder->GetFrameCodedLength(thisFrame, this->mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Mode decision: Intra coding length only",framesProcessedCount);
}
// Output whichever is smaller.
if ((!rateControl->IsEnabled()) && intraLength <= residualLength || !codeP)
{
PERFORMANCE_BITS((int64_t)intraLength, "Decided intra. Bits coded", framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
this->codeFrame(thisFrame, intraLength, formatter, entropyEncoder, dctCoefficientQuantizer->GetIntraQuantizationMethod(), dctCoefficientQuantizer->GetIntraQuantizationScale(), this->mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided intra. Frame coding",framesProcessedCount);
// Compute the new reference frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->DequantizeDCTCoefficients(thisFrame, false);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided intra. Dequantization for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureIDCT(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided intra. Whole frame IDCT for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertMacroblockToScanLayout(thisFrame);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided intra. Macroblock to scan layout for reference",framesProcessedCount);
// We no longer require the old reference. Set the reference to be this frame.
delete reference;
delete residual;
reference = thisFrame;
gopManager->Reset();
Logger::LogMessage("I");
}
else
{
PERFORMANCE_BITS((int64_t)residualLength, "Decided inter. Bits coded", framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
this->codeFrame(residual, residualLength, formatter, entropyEncoder, dctCoefficientQuantizer->GetInterQuantizationMethod(), dctCoefficientQuantizer->GetInterQuantizationScale(), this->mv4);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided inter. Frame coding",framesProcessedCount);
// Compute the new reference frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dctCoefficientQuantizer->DequantizeDCTCoefficients(residual, true);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided inter. Dequantization for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
dct->PictureIDCT(residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided inter. Whole frame IDCT for reference",framesProcessedCount);
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
macroblockHandler->ConvertSignedMacroblockToSignedScanLayout(residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided inter. Macroblock to scan layout for reference",framesProcessedCount);
// Add the reference frame. This will update reference to include the residual correction, ready for the next frame.
PERFORMANCE_TIMING_IN_LOOP_BEFORE(3);
frameDifference->AddFrame(reference, residual);
PERFORMANCE_TIMING_IN_LOOP_AFTER(3,"Decided inter. Adding residual error for reference",framesProcessedCount);
// The intra version is no longer necessary.
delete thisFrame;
delete residual;
Logger::LogMessage("P");
// Increment the number of frames in this GOP.
gopManager->IncrementInterCount();
}
}
if (this->diagnosticPerformanceTimingPerLoop)
PERFORMANCE_TIMING_AFTER(2,"Encoding time",framesProcessedCount);
// Processing of this frame is complete.
Logger::LogMessage("Processed frame %d.", framesProcessedCount);
framesProcessedCount++;
}
if (this->diagnosticPerformanceTimingGlobal)
{
PERFORMANCE_TIMING_AFTER(1,"Encoding process",0);
Logger::LogMessage("Completed performance timings:");
}
// Clean up the reference picture.
if (reference != NULL)
delete reference;
output->CloseFile();
if (this->diagnosticPerformanceBits)
PERFORMANCE_BITS(performanceStreamBits, "Total stream bits", 0);
// Display all performance data.
for (map<pair<const char*, FrameCount>, int64_t>::iterator i = performanceDetails.begin(); i != performanceDetails.end(); i++)
{
Logger::LogMessage("%s %d : %d", (i->first).first, (i->first).second, (int)i->second);
}
}
void Pipeline::decodePath(Codec* codec)
{
// The decode file to frames pipeline takes a binary file which was generated using the
// codec, decodes the file and decompresses the video to individual frames which are output.
// Retrieve configured modules from the encoder.
StreamInputModule* input = (StreamInputModule*)codec->GetModule(std::string(STREAM_INPUT_MODULE_ID));
FrameOutputModule* output = (FrameOutputModule*)codec->GetModule(std::string(FRAME_OUTPUT_MODULE_ID));
StreamParser* parser = (StreamParser*)codec->GetModule(std::string(STREAM_PARSER_MODULE_ID));
ColourSpaceConverter* yuvConverter = (ColourSpaceConverter*)codec->GetModule(std::string(COLOUR_SPACE_CONVERTER_MODULE_ID));
MacroblockHandler* macroblockHandler = (MacroblockHandler*)codec->GetModule(std::string(MACROBLOCK_HANDLER_MODULE_ID));
AANDCT* dct = (AANDCT*)codec->GetModule(std::string(AAN_DCT_MODULE_ID));
DCTCoefficientQuantizer* dctCoefficientQuantizer = (DCTCoefficientQuantizer*)codec->GetModule(std::string(DCT_COEFFICIENT_QUANTIZER_MODULE_ID));
HuffmanEntropyDecoder* entropyDecoder = (HuffmanEntropyDecoder*)codec->GetModule(std::string(HUFFMAN_ENTROPY_DECODER_MODULE_ID));
FrameDifference* frameDifference = (FrameDifference*)codec->GetModule(std::string(FRAME_DIFFERENCE_MODULE_ID));
MotionEstimation* motionEstimation = (MotionEstimation*)codec->GetModule(std::string(MOTION_ESTIMATION_MODULE_ID));
// Check that the required modules are non-NULL.
if (input == NULL || output == NULL ||
parser == NULL || yuvConverter == NULL || macroblockHandler == NULL || dct == NULL || dctCoefficientQuantizer == NULL || entropyDecoder == NULL ||
frameDifference == NULL)
{
Logger::LogMessage("One of the modules required by this pipeline was not specified. Make sure that it is enabled at the command line.");
return;
}
// Open the input stream and attach a stream parser to it.
input->OpenFile();
parser->SetInputModule(input);
// Write the file format information header.
StreamStatus* streamStatus = new StreamStatus;
parser->InputFormatHeader(streamStatus);
if (*streamStatus != STREAM_STATUS_OK)
Logger::LogMessage("Warning: The stream format identifier was incorrect. Is this the correct file type?");
StreamHeader* streamHeader = new StreamHeader;
parser->InputStreamHeader(streamStatus, streamHeader);
if (*streamStatus != STREAM_STATUS_OK)
Logger::LogMessage("Warning: The stream header information could not be read.");
// Update the 4-motion-vector state.
if (streamHeader->mv4)
motionEstimation->EnableMV4();
// Create a binary stream reader to read in the rest of the file data using
// the stream input module.
BinaryStreamReader* binaryReader = new BinaryStreamReader(input);
// Keep track of the last decoded frame so that predictive frames can be decompressed.
PictureInfo* referenceFrame = NULL;
FrameCount framesProcessedCount = 0;
while (framesProcessedCount < streamHeader->frameCount)
{
// Store the current frame as we decode it.
PictureInfo* thisFrame = new PictureInfo();
// Get the header for this frame.
FrameHeader* frameHeader = new FrameHeader;
parser->InputFrameHeader(streamStatus, frameHeader, binaryReader);
if (*streamStatus != STREAM_STATUS_OK)
Logger::LogMessage("Warning: The frame header information could not be read.");
// Configure based on the frame header.
if (frameHeader->pFrame)
{
thisFrame->SetFrameType(FRAME_TYPE_INTER);
Logger::LogMessage("Inter frame");
}
else
{
thisFrame->SetFrameType(FRAME_TYPE_INTRA);
Logger::LogMessage("Intra frame");
}
// The quantization method for this frame is in the frame header.
if (frameHeader->pFrame)
{
dctCoefficientQuantizer->SetInterQuantizationMethod(frameHeader->quantizationMethod);
dctCoefficientQuantizer->SetInterQuantizationScale(frameHeader->quantizationScale);
}
else
{
dctCoefficientQuantizer->SetIntraQuantizationMethod(frameHeader->quantizationMethod);
dctCoefficientQuantizer->SetIntraQuantizationScale(frameHeader->quantizationScale);
}
// Decode this whole frame and align the reader for the next frame.
entropyDecoder->DecodeFrame(binaryReader, streamHeader, frameHeader, thisFrame);
binaryReader->AlignReader();
// Put the frame in the spatial domain.
dctCoefficientQuantizer->DequantizeDCTCoefficients(thisFrame, thisFrame->GetFrameType() == FRAME_TYPE_INTER);
dct->PictureIDCT(thisFrame);
if (thisFrame->GetFrameType() == FRAME_TYPE_INTER)
{
// Add the just-decoded residual to the reference frame.
macroblockHandler->ConvertSignedMacroblockToSignedScanLayout(thisFrame);
// Diagnostics section: Output residual frame data separately.
#ifdef PIPELINE_DIAGNOSTICS
if (this->diagnosticOutputResiduals)
{
// Output the following frames:
// * Current reference frame (normal output previous iteration).
// * Reference with motion vectors applied.
// * Residual data added to blank image.
// * Final image (normal output).
Logger::LogMessage("Diagnostics: Outputting residual.");
PictureInfo* clonedResidual = thisFrame->Clone();
PictureInfo* referenceFrameVectorsApplied = referenceFrame->Clone();
// Appply vectors, and make a residual image.
motionEstimation->ApplyMotionVectors(referenceFrameVectorsApplied, thisFrame->GetMVDisplacementsX(), thisFrame->GetMVDisplacementsY());
frameDifference->DiagnosticResidual(clonedResidual);
// Convert to RGB.
yuvConverter->ConvertYUVToRGBRepresentation(referenceFrameVectorsApplied);
yuvConverter->ConvertYUVToRGBRepresentation(clonedResidual);
// Ouput the frames.
std::stringstream withVectorsName;
std::stringstream residualName;
withVectorsName << "diagnosticsVectorsApplied" << framesProcessedCount << ".png";
residualName << "diagnosticsResidual" << framesProcessedCount << ".png";
output->WriteImage(referenceFrameVectorsApplied, withVectorsName.str());
output->WriteImage(clonedResidual, residualName.str());
delete clonedResidual;
delete referenceFrameVectorsApplied;
}
#endif
motionEstimation->ApplyMotionVectors(referenceFrame, thisFrame->GetMVDisplacementsX(), thisFrame->GetMVDisplacementsY());
frameDifference->AddFrame(referenceFrame, thisFrame);
// Copy the byte pixel data from the reference into the current frame.
thisFrame->SetUnsignedByteData(referenceFrame->CopyUnsignedByteData(), referenceFrame->GetUnsignedByteDataLength());
}
else
{
// Store this frame as the reference frame.
macroblockHandler->ConvertMacroblockToScanLayout(thisFrame);
// Initialize the reference if this is the first frame.
if (framesProcessedCount == 0)
referenceFrame = thisFrame->Clone();
else
referenceFrame->SetUnsignedByteData(thisFrame->CopyUnsignedByteData(), thisFrame->GetUnsignedByteDataLength());
}
// Go back to the RGB colour space.
yuvConverter->ConvertYUVToRGBRepresentation(thisFrame);
// Write out the data.
output->WriteImage(thisFrame, framesProcessedCount);
// Clean up the frame.
delete thisFrame;
// Clean up meta-data.
delete frameHeader;
// Processing of this frame is complete.
Logger::LogMessage("Processed frame %d.", framesProcessedCount);
framesProcessedCount++;
}
// Clear the reference frame.
if (referenceFrame != NULL)
delete referenceFrame;
input->CloseFile();
// Flush any cached images.
output->FlushCachedImages();
delete binaryReader;
delete streamHeader;
delete streamStatus;
}
inline int64_t Pipeline::getTime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}
}