#include "MediaTrackVideoSupplier.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <MediaTrack.h>
#include "ColorSpaceToString.h"
using std::nothrow;
#define DEBUG_DECODED_FRAME 0
#if DEBUG_DECODED_FRAME
# include <Bitmap.h>
# include <BitmapStream.h>
# include <File.h>
# include <TranslatorRoster.h>
#endif
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
int32 trackIndex, status_t& initStatus)
:
VideoTrackSupplier(),
fVideoTrack(track),
fPerformanceTime(0),
fDuration(0),
fCurrentFrame(0),
fTrackIndex(trackIndex)
{
if (!fVideoTrack) {
printf("MediaTrackVideoSupplier() - no video track\n");
return;
}
initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0);
fDuration = fVideoTrack->Duration();
}
MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
{
}
const media_format&
MediaTrackVideoSupplier::Format() const
{
return fFormat;
}
status_t
MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const
{
if (!fVideoTrack)
return B_NO_INIT;
return fVideoTrack->EncodedFormat(format);
}
status_t
MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
{
if (!fVideoTrack)
return B_NO_INIT;
return fVideoTrack->GetCodecInfo(info);
}
status_t
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
const media_raw_video_format& format, bool& wasCached)
{
if (!fVideoTrack)
return B_NO_INIT;
if (!buffer)
return B_BAD_VALUE;
if (fVideoTrack->CountFrames() < 2) {
static int already = false;
if (already) {
wasCached = true;
return B_OK;
}
already = true;
}
status_t ret = B_OK;
if (format.display.format
!= fFormat.u.raw_video.display.format
|| fFormat.u.raw_video.display.bytes_per_row
!= format.display.bytes_per_row) {
ret = _SwitchFormat(format.display.format,
format.display.bytes_per_row);
if (ret < B_OK) {
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
"unable to switch media format: %s\n", strerror(ret));
return ret;
}
}
int64 frameCount = 1;
media_header mediaHeader;
ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
if (ret < B_OK && ret != B_LAST_BUFFER_ERROR) {
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
"error while reading frame of track: %s\n", strerror(ret));
} else
fPerformanceTime = mediaHeader.start_time;
fCurrentFrame = fVideoTrack->CurrentFrame();
if (performanceTime)
*performanceTime = fPerformanceTime;
#if DEBUG_DECODED_FRAME
if (modifiers() & B_SHIFT_KEY) {
BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
BTranslatorRoster* roster = BTranslatorRoster::Default();
BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
BBitmapStream bitmapStream(bitmap);
roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
bitmapStream.DetachBitmap(&bitmap);
delete bitmap;
}
#endif
if (ret == B_LAST_BUFFER_ERROR && fVideoTrack->CountFrames() < 2)
return B_OK;
return ret;
}
status_t
MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame)
{
if (!fVideoTrack)
return B_NO_INIT;
if (fVideoTrack->CountFrames() < 2)
return B_OK;
status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
return ret;
}
status_t
MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
{
if (!fVideoTrack)
return B_NO_INIT;
if (fVideoTrack->CountFrames() < 2)
return B_OK;
bigtime_t _performanceTime = *performanceTime;
status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (ret < B_OK)
return ret;
ret = fVideoTrack->SeekToTime(performanceTime);
if (ret == B_OK) {
if (_performanceTime != *performanceTime) {
printf("seeked by time: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME
"\n", _performanceTime, *performanceTime);
}
fPerformanceTime = *performanceTime;
fCurrentFrame = fVideoTrack->CurrentFrame();
}
return ret;
}
status_t
MediaTrackVideoSupplier::SeekToFrame(int64* frame)
{
if (!fVideoTrack)
return B_NO_INIT;
if (fVideoTrack->CountFrames() < 2)
return B_OK;
int64 wantFrame = *frame;
if (wantFrame == fCurrentFrame)
return B_OK;
status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (ret != B_OK)
return ret;
if (wantFrame > *frame) {
int64 nextWantFrame = wantFrame + 1;
if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
if (nextWantFrame == wantFrame) {
wantFrame++;
*frame = wantFrame;
}
}
}
if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
*frame = fCurrentFrame;
return B_OK;
}
ret = fVideoTrack->SeekToFrame(frame);
if (ret != B_OK)
return ret;
fCurrentFrame = *frame;
fPerformanceTime = fVideoTrack->CurrentTime();
return ret;
}
BRect
MediaTrackVideoSupplier::Bounds() const
{
return BRect(0, 0, fFormat.u.raw_video.display.line_width - 1,
fFormat.u.raw_video.display.line_count - 1);
}
color_space
MediaTrackVideoSupplier::ColorSpace() const
{
return fFormat.u.raw_video.display.format;
}
uint32
MediaTrackVideoSupplier::BytesPerRow() const
{
return fFormat.u.raw_video.display.bytes_per_row;
}
status_t
MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
{
fFormat.Clear();
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
return ret;
}
uint32 width = fFormat.u.encoded_video.output.display.line_width;
uint32 height = fFormat.u.encoded_video.output.display.line_count;
if (format == B_NO_COLOR_SPACE) {
format = fFormat.u.encoded_video.output.display.format;
if (format == B_NO_COLOR_SPACE) {
format = B_YCbCr422;
} else {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"preferred color space: %s\n",
color_space_to_string(format));
}
}
uint32 minBytesPerRow;
if (format == B_YCbCr422)
minBytesPerRow = ((width * 2 + 3) / 4) * 4;
else
minBytesPerRow = width * 4;
bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
ret = _SetDecodedFormat(width, height, format, bytesPerRow);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
strerror(ret));
format = B_RGB32;
bytesPerRow = max_c(bytesPerRow, width * 4);
ret = _SetDecodedFormat(width, height, format, bytesPerRow);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"fVideoTrack->DecodedFormat(): %s - giving up\n",
strerror(ret));
return ret;
}
}
if (fFormat.u.raw_video.display.format != format) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
" codec changed colorspace of decoded format (%s -> %s)!\n",
color_space_to_string(format),
color_space_to_string(fFormat.u.raw_video.display.format));
format = fFormat.u.raw_video.display.format;
if (format == B_YCbCr422)
minBytesPerRow = ((width * 2 + 3) / 4) * 4;
else
minBytesPerRow = width * 4;
if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
}
}
if (fFormat.u.raw_video.last_active != height - 1) {
printf("should skip %" B_PRId32 " lines at bottom!\n",
(height - 1) - fFormat.u.raw_video.last_active);
}
return ret;
}
status_t
MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
color_space format, uint32 bytesPerRow)
{
fFormat.Clear();
fFormat.u.raw_video.display.format = format;
fFormat.u.raw_video.display.line_width = width;
fFormat.u.raw_video.display.line_count = height;
fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
return fVideoTrack->DecodedFormat(&fFormat);
}