Media Kit: Add MPEG2 video stream decoder test.

- This test currently fails and thus showing that there is something wrong in
  the Media Kit.
- This test was first developed and tested on Haiku R1 Alpha 4.1 where
  every thing was hacked together to make this test succeed, thus showing that
  the current Media Kit state of affair has some issues and not this test :)
- The test comes with documentation (@see mpeg2_decoder.test.cpp).

Signed-off-by: Colin Günther <[email protected]>
This commit is contained in:
Colin Günther
2014-07-06 13:51:31 +00:00
parent d71c5a1ebf
commit c5954368ed
4 changed files with 337 additions and 0 deletions
+1
View File
@@ -18,6 +18,7 @@ SimpleTest mediaDescriptions :
: media ;
SubInclude HAIKU_TOP src tests kits media media_decoder ;
SubInclude HAIKU_TOP src tests kits media mpeg2_decoder_test ;
SubInclude HAIKU_TOP src tests kits media notificationtest ;
SubInclude HAIKU_TOP src tests kits media nodetest ;
SubInclude HAIKU_TOP src tests kits media playwav ;
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,17 @@
SubDir HAIKU_TOP src tests kits media mpeg2_decoder_test ;
local ffmpegHeaders = [ BuildFeatureAttribute ffmpeg : headers : path ] ;
UseHeaders $(ffmpegHeaders) ;
UseHeaders [ FDirName $(ffmpegHeaders) libavcodec ] ;
UseHeaders [ FDirName $(ffmpegHeaders) libavformat ] ;
UseHeaders [ FDirName $(ffmpegHeaders) libavutil ] ;
SubDirC++Flags -D__STDC_CONSTANT_MACROS -Wno-deprecated-declarations ;
SimpleTest mpeg2_decoder_test :
mpeg2_decoder_test.cpp
: be media translation $(TARGET_LIBSUPC++)
;
Includes [ FGristFiles mpeg2_decoder_test.cpp ]
: [ BuildFeatureAttribute ffmpeg : headers ] ;
@@ -0,0 +1,319 @@
/*
* Copyright 2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Colin Günther, [email protected]
*/
/*! Tests video stream decoding functionality of the FFMPEG decoder plugin.
This test is designed with testing the dvb media-addon video decoding
capability in mind. Thus we are restricting this test to MPEG2-Video.
The test requires a MPEG2 test file at the same directory you start the
test from. Normally there is a test file included at the same location
this source file is located if not have a look at the git history.
Successful completion of this test results in a series of PNG image
files created at the same location you start the test from.
The originally included test file results in 84 PNG images,
representing a movie sequence with the actress Anne Hathaway.
This test file has the following properties:
- The first frames cannot be decoded, due to missing I-Frames
This is by intention, and helps identifying possible bugs in the
FFMPEG decoder plugin regarding decoding of video streams, where
the stream may start in the middle of a group of pictures (GoP).
- encoded_video.output.first_active = 0
- encoded_video.output.last_active = 575
- encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT
- encoded_video.output.display.format = B_YUV420
- encoded_video.output.display.line_width = 720
- encoded_video.output.display.line_count = 576
In any way, there -MUST- be no need to properly initialize those video
properties for this test to succeed. To put it in other terms: The
FFMPEG decoder plugin should determine those properties by its own and
decode the video accordingly.
*/
#include <stdlib.h>
#include <string.h>
#include <AppKit.h>
#include <InterfaceKit.h>
#include <MediaKit.h>
#include <SupportKit.h>
#include <TranslationKit.h>
#include <media/Buffer.h>
#include <media/BufferGroup.h>
#include <media/MediaDecoder.h>
#include <storage/File.h>
#include <support/Errors.h>
extern "C" {
#include "avcodec.h"
#ifdef DEBUG
// Needed to fix debug build, otherwise the linker complains about
// "undefined reference to `ff_log2_tab'"
const uint8_t ff_log2_tab[256] = {0};
#endif
} // extern "C"
const char* kTestVideoFilename = "./AVCodecTestMpeg2VideoStreamRaw";
class FileDecoder : public BMediaDecoder {
private:
BFile* sourceFile;
public:
FileDecoder(BFile* file) : BMediaDecoder() {
sourceFile = file;
}
protected:
virtual status_t GetNextChunk(const void **chunkData, size_t* chunkLen,
media_header* mh) {
static const uint kReadSizeInBytes = 4096;
memset(mh, 0, sizeof(media_header));
void* fileData = malloc(kReadSizeInBytes);
ssize_t readLength = this->sourceFile->Read(fileData,
kReadSizeInBytes);
if (readLength < 0)
return B_ERROR;
if (readLength == 0)
return B_LAST_BUFFER_ERROR;
*chunkData = fileData;
*chunkLen = readLength;
return B_OK;
}
};
media_format* CreateMpeg2MediaFormat();
media_format CreateRawMediaFormat();
void WriteBufferToImageFileAndRecycleIt(BBuffer* buffer);
BBitmap* CreateBitmapUsingMediaFormat(media_format mediaFormat);
status_t SaveBitmapAtPathUsingFormat(BBitmap* bitmap, BString* filePath,
uint32 bitmapStorageFormat);
int
main(int argc, char* argv[])
{
BApplication app("application/x-vnd.mpeg2-decoder-test");
BFile* mpeg2EncodedFile = new BFile(kTestVideoFilename, O_RDONLY);
BMediaDecoder* mpeg2Decoder = new FileDecoder(mpeg2EncodedFile);
media_format* mpeg2MediaFormat = CreateMpeg2MediaFormat();
mpeg2Decoder->SetTo(mpeg2MediaFormat);
status_t settingMpeg2DecoderStatus = mpeg2Decoder->InitCheck();
if (settingMpeg2DecoderStatus < B_OK)
exit(1);
media_format rawMediaFormat = CreateRawMediaFormat();
status_t settingMpeg2DecoderOutputStatus
= mpeg2Decoder->SetOutputFormat(&rawMediaFormat);
if (settingMpeg2DecoderOutputStatus < B_OK)
exit(2);
static const uint32 kVideoBufferRequestTimeout = 20000;
uint32 videoBufferSizeInBytesMax = 720 * 576 * 4;
BBufferGroup* rawVideoFramesGroup
= new BBufferGroup(videoBufferSizeInBytesMax, 4);
BBuffer* rawVideoFrame = NULL;
int64 rawVideoFrameCount = 0;
media_header mh;
while (true) {
rawVideoFrame = rawVideoFramesGroup->RequestBuffer(
videoBufferSizeInBytesMax, kVideoBufferRequestTimeout);
status_t decodingVideoFrameStatus = mpeg2Decoder->Decode(
rawVideoFrame->Data(), &rawVideoFrameCount, &mh, NULL);
if (decodingVideoFrameStatus < B_OK) {
rawVideoFrame->Recycle();
break;
}
WriteBufferToImageFileAndRecycleIt(rawVideoFrame);
}
// Cleaning up
rawVideoFramesGroup->ReclaimAllBuffers();
}
/*! The caller takes ownership of the returned media_format value.
Thus the caller needs to free the returned value.
The returned value may be NULL, when there was an error.
*/
media_format*
CreateMpeg2MediaFormat()
{
// The following code is mainly copy 'n' paste from src/add-ons/media/
// media-add-ons/dvb/MediaFormat.cpp:GetHeaderFormatMpegVideo()
status_t status;
media_format_description desc;
desc.family = B_MISC_FORMAT_FAMILY;
desc.u.misc.file_format = 'ffmp';
desc.u.misc.codec = CODEC_ID_MPEG2VIDEO;
static media_format* kFailedToCreateMpeg2MediaFormat = NULL;
BMediaFormats formats;
status = formats.InitCheck();
if (status < B_OK) {
printf("formats.InitCheck failed, error %lu\n", status);
return kFailedToCreateMpeg2MediaFormat;
}
media_format* mpeg2MediaFormat
= static_cast<media_format*>(malloc(sizeof(media_format)));
memset(mpeg2MediaFormat, 0, sizeof(media_format));
status = formats.GetFormatFor(desc, mpeg2MediaFormat);
if (status) {
printf("formats.GetFormatFor failed, error %lu\n", status);
free(mpeg2MediaFormat);
return kFailedToCreateMpeg2MediaFormat;
}
// The following code block can be removed, once the ffmpeg addon can
// determine the codec output parameters from the encoded data.
mpeg2MediaFormat->u.encoded_video.output.first_active = 0;
mpeg2MediaFormat->u.encoded_video.output.last_active = 575;
mpeg2MediaFormat->u.encoded_video.output.orientation
= B_VIDEO_TOP_LEFT_RIGHT;
mpeg2MediaFormat->u.encoded_video.output.display.format = B_YUV420;
mpeg2MediaFormat->u.encoded_video.output.display.line_width = 720;
mpeg2MediaFormat->u.encoded_video.output.display.line_count = 576;
return mpeg2MediaFormat;
}
media_format
CreateRawMediaFormat()
{
media_format rawMediaFormat;
memset(&rawMediaFormat, 0, sizeof(media_format));
rawMediaFormat.type = B_MEDIA_RAW_VIDEO;
rawMediaFormat.u.raw_video.display.format = B_RGB32;
rawMediaFormat.u.raw_video.display.line_width = 720;
rawMediaFormat.u.raw_video.display.line_count = 576;
rawMediaFormat.u.raw_video.last_active
= rawMediaFormat.u.raw_video.display.line_count - 1;
rawMediaFormat.u.raw_video.display.bytes_per_row
= rawMediaFormat.u.raw_video.display.line_width * 4;
rawMediaFormat.u.raw_video.field_rate = 0; // wildcard
rawMediaFormat.u.raw_video.interlace = 1;
rawMediaFormat.u.raw_video.first_active = 0;
rawMediaFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
rawMediaFormat.u.raw_video.pixel_width_aspect = 1;
rawMediaFormat.u.raw_video.pixel_height_aspect = 1;
rawMediaFormat.u.raw_video.display.pixel_offset = 0;
rawMediaFormat.u.raw_video.display.line_offset = 0;
rawMediaFormat.u.raw_video.display.flags = 0;
return rawMediaFormat;
}
void
WriteBufferToImageFileAndRecycleIt(BBuffer* buffer)
{
static BBitmap* image = NULL;
if (image == NULL) {
// Lazy initialization
image = CreateBitmapUsingMediaFormat(CreateRawMediaFormat());
}
if (image == NULL) {
// Failed to create the image, needed for converting the buffer
buffer->Recycle();
return;
}
memcpy(image->Bits(), buffer->Data(), image->BitsLength());
buffer->Recycle();
static int32 imageSavedCounter = 0;
static const char* kImageFileNameTemplate = "./mpeg2TestImage%d.png";
BString imageFileName;
imageFileName.SetToFormat(kImageFileNameTemplate, imageSavedCounter);
status_t savingBitmapStatus = SaveBitmapAtPathUsingFormat(image,
&imageFileName, B_PNG_FORMAT);
if (savingBitmapStatus >= B_OK)
imageSavedCounter++;
}
BBitmap*
CreateBitmapUsingMediaFormat(media_format mediaFormat)
{
const uint32 kNoFlags = 0;
BBitmap* creatingBitmapFailed = NULL;
float imageWidth = mediaFormat.u.raw_video.display.line_width;
float imageHeight = mediaFormat.u.raw_video.display.line_count;
BRect imageFrame(0, 0, imageWidth - 1, imageHeight - 1);
color_space imageColorSpace = mediaFormat.u.raw_video.display.format;
BBitmap* bitmap = NULL;
bitmap = new BBitmap(imageFrame, kNoFlags, imageColorSpace);
if (bitmap == NULL)
return bitmap;
if (bitmap->InitCheck() < B_OK) {
delete bitmap;
return creatingBitmapFailed;
}
if (bitmap->IsValid() == false) {
delete bitmap;
return creatingBitmapFailed;
}
return bitmap;
}
status_t
SaveBitmapAtPathUsingFormat(BBitmap* bitmap, BString* filePath,
uint32 bitmapStorageFormat)
{
BFile file(*filePath, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
status_t creatingFileStatus = file.InitCheck();
if (creatingFileStatus < B_OK)
return creatingFileStatus;
BBitmapStream bitmapStream(bitmap);
static BTranslatorRoster* translatorRoster = NULL;
if (translatorRoster == NULL)
translatorRoster = BTranslatorRoster::Default();
status_t writingBitmapToFileStatus = translatorRoster->Translate(
&bitmapStream, NULL, NULL, &file, bitmapStorageFormat,
B_TRANSLATOR_BITMAP);
bitmapStream.DetachBitmap(&bitmap);
return writingBitmapToFileStatus;
}