* Moved a bunch of non-primary interface classes into a new subfolder

"interface"

* Complete reimplementation of the playback engine using Media Nodes:
- Seeking video files does not appear to lockup the playback anymore, but
works on a frame accurate level even for keyframe based streams. There is
currently a problem with certain container formats, the audio track reports
a "Device Seek Error" in certain conditions. In that case audio goes silent,
and can be restarted by going back to the beginnings of the stream.
- Video overlays are now supported.
- It would be possible to connect the output of the MediaPlayer to other
applications or dormant media nodes.

* Known regressions:
- The volume slider has currently no effect anymore.
- Switching the audio track during playback has a known race condition and
can crash the player.
- The new engine is not as "light weight" as the old one. I tagged the
previous implementation in tags/components/mediaplayer-engine-v1. It does
not seem to have any noticable effect though.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25725 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-05-30 18:59:36 +00:00
parent 2ec83efbb2
commit 0fc56ed57b
70 changed files with 9348 additions and 1773 deletions
File diff suppressed because it is too large Load Diff
+110 -161
View File
@@ -2,7 +2,7 @@
* Controller.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
* Copyright (C) 2007 Stephan Aßmus <[email protected]>
* Copyright (C) 2007-2008 Stephan Aßmus <[email protected]> (MIT Ok)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -29,207 +29,156 @@
#include <Locker.h>
#include <String.h>
class AudioSupplier;
#include "NodeManager.h"
class AudioTrackSupplier;
class BBitmap;
class BMediaFile;
class BMediaTrack;
class ProxyAudioSupplier;
class ProxyVideoSupplier;
class SoundOutput;
class VideoSupplier;
class VideoTrackSupplier;
class VideoView;
class Controller {
class Controller : public NodeManager {
public:
class Listener {
public:
Listener();
virtual ~Listener();
Listener();
virtual ~Listener();
virtual void FileFinished();
virtual void FileChanged();
virtual void FileFinished();
virtual void FileChanged();
virtual void VideoTrackChanged(int32 index);
virtual void AudioTrackChanged(int32 index);
virtual void VideoTrackChanged(int32 index);
virtual void AudioTrackChanged(int32 index);
virtual void VideoStatsChanged();
virtual void AudioStatsChanged();
virtual void VideoStatsChanged();
virtual void AudioStatsChanged();
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void VolumeChanged(float volume);
virtual void MutedChanged(bool muted);
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void VolumeChanged(float volume);
virtual void MutedChanged(bool muted);
};
Controller();
virtual ~Controller();
Controller();
virtual ~Controller();
bool Lock();
status_t LockWithTimeout(bigtime_t timeout);
void Unlock();
// PlaybackManager interface
virtual int64 Duration();
status_t SetTo(const entry_ref &ref);
void GetSize(int *width, int *height);
// NodeManager interface
virtual VideoTarget* CreateVideoTarget();
virtual VideoSupplier* CreateVideoSupplier();
virtual AudioSupplier* CreateAudioSupplier();
int AudioTrackCount();
int VideoTrackCount();
// Controller
status_t SetTo(const entry_ref &ref);
void GetSize(int *width, int *height);
int AudioTrackCount();
int VideoTrackCount();
status_t SelectAudioTrack(int n);
status_t SelectVideoTrack(int n);
status_t SelectAudioTrack(int n);
status_t SelectVideoTrack(int n);
void Stop();
void Play();
void Pause();
void TogglePlaying();
void Stop();
void Play();
void Pause();
void TogglePlaying();
bool IsPaused() const;
bool IsStopped() const;
uint32 PlaybackState() const;
uint32 PlaybackState();
bigtime_t Duration();
bigtime_t Position();
bigtime_t TimeDuration();
bigtime_t TimePosition();
void SetVolume(float value);
float Volume() const;
void VolumeUp();
void VolumeDown();
void ToggleMute();
void SetPosition(float value);
virtual void SetVolume(float percent);
float Volume();
void VolumeUp();
void VolumeDown();
void ToggleMute();
void SetPosition(float value);
bool HasFile();
status_t GetFileFormatInfo(
media_file_format* fileFormat);
status_t GetCopyright(BString* copyright);
status_t GetLocation(BString* location);
status_t GetName(BString* name);
status_t GetEncodedVideoFormat(media_format* format);
status_t GetVideoCodecInfo(media_codec_info* info);
status_t GetEncodedAudioFormat(media_format* format);
status_t GetAudioCodecInfo(media_codec_info* info);
bool HasFile();
status_t GetFileFormatInfo(media_file_format* fileFormat);
status_t GetCopyright(BString* copyright);
status_t GetLocation(BString* location);
status_t GetName(BString* name);
status_t GetEncodedVideoFormat(media_format* format);
status_t GetVideoCodecInfo(media_codec_info* info);
status_t GetEncodedAudioFormat(media_format* format);
status_t GetAudioCodecInfo(media_codec_info* info);
// video view
void SetVideoView(VideoView *view);
void SetVideoView(VideoView *view);
bool IsOverlayActive();
bool IsOverlayActive();
bool LockBitmap();
void UnlockBitmap();
BBitmap * Bitmap();
// notification support
bool AddListener(Listener* listener);
void RemoveListener(Listener* listener);
bool AddListener(Listener* listener);
void RemoveListener(Listener* listener);
private:
void _AudioDecodeThread();
void _AudioPlayThread();
uint32 _PlaybackState(int32 playingMode) const;
void _EndOfStreamReached(bool isVideo = false);
void _VideoDecodeThread();
void _VideoPlayThread();
void _NotifyFileChanged() const;
void _NotifyFileFinished() const;
void _NotifyVideoTrackChanged(int32 index) const;
void _NotifyAudioTrackChanged(int32 index) const;
void _StartThreads();
void _StopThreads();
void _NotifyVideoStatsChanged() const;
void _NotifyAudioStatsChanged() const;
void _EndOfStreamReached(bool isVideo = false);
void _UpdatePosition(bigtime_t position,
bool isVideoPosition = false,
bool force = false);
void _NotifyPlaybackStateChanged(uint32 state) const;
void _NotifyPositionChanged(float position) const;
void _NotifyVolumeChanged(float volume) const;
void _NotifyMutedChanged(bool muted) const;
static int32 _VideoDecodeThreadEntry(void *self);
static int32 _VideoPlayThreadEntry(void *self);
static int32 _AudioDecodeThreadEntry(void *self);
static int32 _AudioPlayThreadEntry(void *self);
// overridden from PlaybackManager so that we
// can use our own Listener mechanism
virtual void NotifyPlayModeChanged(int32 mode) const;
virtual void NotifyLoopModeChanged(int32 mode) const;
virtual void NotifyLoopingEnabledChanged(
bool enabled) const;
virtual void NotifyVideoBoundsChanged(BRect bounds) const;
virtual void NotifyFPSChanged(float fps) const;
virtual void NotifyCurrentFrameChanged(int32 frame) const;
virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const;
private:
void _NotifyFileChanged();
void _NotifyFileFinished();
void _NotifyVideoTrackChanged(int32 index);
void _NotifyAudioTrackChanged(int32 index);
void _NotifyVideoStatsChanged();
void _NotifyAudioStatsChanged();
VideoView* fVideoView;
volatile bool fPaused;
volatile bool fStopped;
float fVolume;
bool fMuted;
void _NotifyPlaybackStateChanged();
void _NotifyPositionChanged(float position);
void _NotifyVolumeChanged(float volume);
void _NotifyMutedChanged(bool muted);
entry_ref fRef;
BMediaFile* fMediaFile;
friend class InfoWin;
ProxyVideoSupplier* fVideoSupplier;
ProxyAudioSupplier* fAudioSupplier;
VideoTrackSupplier* fVideoTrackSupplier;
AudioTrackSupplier* fAudioTrackSupplier;
enum {
MAX_AUDIO_BUFFERS = 8,
MAX_VIDEO_BUFFERS = 3,
};
BList fAudioTrackList;
BList fVideoTrackList;
mutable bigtime_t fPosition;
bigtime_t fDuration;
float fVideoFrameRate;
bool fAutoplay;
volatile bool fPauseAtEndOfStream;
volatile bool fSeekToStartAfterPause;
struct buffer_info {
char * buffer;
BBitmap * bitmap;
size_t sizeUsed;
size_t sizeMax;
bigtime_t startTime;
bool formatChanged;
bool endOfStream;
media_format mediaFormat;
};
VideoView * fVideoView;
volatile bool fPaused;
volatile bool fStopped;
float fVolume;
bool fMuted;
entry_ref fRef;
BMediaFile * fMediaFile;
mutable BLocker fDataLock;
VideoSupplier* fVideoSupplier;
AudioSupplier* fAudioSupplier;
BLocker fVideoSupplierLock;
BLocker fAudioSupplierLock;
BList * fAudioTrackList;
BList * fVideoTrackList;
media_format fAudioFormat;
media_format fVideoFormat;
sem_id fAudioDecodeSem;
sem_id fVideoDecodeSem;
sem_id fAudioPlaySem;
sem_id fVideoPlaySem;
sem_id fAudioWaitSem;
sem_id fVideoWaitSem;
thread_id fAudioDecodeThread;
thread_id fVideoDecodeThread;
thread_id fAudioPlayThread;
thread_id fVideoPlayThread;
SoundOutput * fSoundOutput;
volatile bool fSeekAudio;
volatile bool fSeekVideo;
volatile bigtime_t fSeekPosition;
bigtime_t fPosition;
bigtime_t fDuration;
int32 fAudioBufferCount;
int32 fAudioBufferReadIndex;
int32 fAudioBufferWriteIndex;
int32 fVideoBufferCount;
int32 fVideoBufferReadIndex;
int32 fVideoBufferWriteIndex;
buffer_info fAudioBuffer[MAX_AUDIO_BUFFERS];
buffer_info fVideoBuffer[MAX_VIDEO_BUFFERS];
BLocker fTimeSourceLock;
bigtime_t fTimeSourceSysTime;
bigtime_t fTimeSourcePerfTime;
bool fAutoplay;
volatile bool fPauseAtEndOfStream;
volatile bool fSeekToStartAfterPause;
BBitmap * fCurrentBitmap;
BLocker fBitmapLock;
BList fListeners;
BList fListeners;
};
+3
View File
@@ -227,6 +227,9 @@ printf("InfoWin::Update(0x%08lx)\n", which);
fContentsView->SetFontAndColor(be_plain_font, B_FONT_ALL);
// fContentsView->Insert("");
if (!fController->Lock())
return;
fLabelsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kRed);
status_t err;
+40 -15
View File
@@ -2,13 +2,16 @@ SubDir HAIKU_TOP src apps mediaplayer ;
SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
# for BRecentItems
UsePublicHeaders [ FDirName be_apps Tracker ] ;
UsePrivateHeaders shared ;
# source directories
local sourceDirs =
interface
media_node_framework
media_node_framework/audio
media_node_framework/video
playlist
supplier
support
@@ -21,6 +24,33 @@ for sourceDir in $(sourceDirs) {
}
Application MediaPlayer :
# interface
DrawingTidbits.cpp
SeekSlider.cpp
TransportButton.cpp
VolumeSlider.cpp
# media_node_framework
NodeManager.cpp
PlaybackListener.cpp
PlaybackLOAdapter.cpp
PlaybackManager.cpp
# media_node_framework/audio
AudioAdapter.cpp
AudioChannelConverter.cpp
AudioFormatConverter.cpp
AudioProducer.cpp
AudioReader.cpp
AudioResampler.cpp
AudioSupplier.cpp
# media_node_framework/video
VideoConsumer.cpp
VideoProducer.cpp
VideoSupplier.cpp
VideoTarget.cpp
# playlist
CopyPLItemsCommand.cpp
ImportPLItemsCommand.cpp
@@ -32,22 +62,27 @@ Application MediaPlayer :
PlaylistWindow.cpp
RemovePLItemsCommand.cpp
# supplier
# settings
Settings.cpp
SettingsWindow.cpp
# supplier
AudioSupplier.cpp
AudioTrackSupplier.cpp
MediaTrackAudioSupplier.cpp
MediaTrackVideoSupplier.cpp
VideoSupplier.cpp
ProxyAudioSupplier.cpp
ProxyVideoSupplier.cpp
VideoTrackSupplier.cpp
# support
AbstractLOAdapter.cpp
Command.cpp
CommandStack.cpp
Event.cpp
EventQueue.cpp
Listener.cpp
ListenerAdapter.cpp
MessageEvent.cpp
Notifier.cpp
RWLocker.cpp
FileReadWrite.cpp
@@ -57,23 +92,13 @@ Application MediaPlayer :
Controller.cpp
ControllerObserver.cpp
ControllerView.cpp
DrawingTidbits.cpp
InfoWin.cpp
MainApp.cpp
MainWin.cpp
SoundOutput.cpp
TransportButton.cpp
TransportControlGroup.cpp
SeekSlider.cpp
VideoNode.cpp
VideoView.cpp
VolumeSlider.cpp
: be media tracker translation textencoding $(TARGET_LIBSTDC++)
: MediaPlayer.rdef
;
if $(TARGET_PLATFORM) = libbe_test {
HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : MediaPlayer
: tests!apps ;
}
+7
View File
@@ -30,6 +30,8 @@
#include <stdio.h>
#include <unistd.h>
#include "EventQueue.h"
MainApp *gMainApp;
const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
@@ -221,8 +223,13 @@ MainApp::_BroadcastMessage(const BMessage& _message)
int
main()
{
EventQueue::CreateDefault();
gMainApp = new MainApp;
gMainApp->Run();
delete gMainApp;
EventQueue::DeleteDefault();
return 0;
}
+8 -8
View File
@@ -150,8 +150,7 @@ MainWin::MainWin()
// video view
rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
fMenuBarHeight + 10);
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE);
fBackground->AddChild(fVideoView);
// controls
@@ -171,7 +170,6 @@ MainWin::MainWin()
fPlaylist->AddListener(fPlaylistObserver);
fController->SetVideoView(fVideoView);
fController->AddListener(fControllerObserver);
fVideoView->IsOverlaySupported();
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
// printf("fControlsHeight %d\n", fControlsHeight);
@@ -224,8 +222,13 @@ MainWin::~MainWin()
}
delete fPlaylist;
delete fController;
delete fFilePanel;
// quit the Controller looper thread
thread_id controllerThread = fController->Thread();
fController->PostMessage(B_QUIT_REQUESTED);
status_t exitValue;
wait_for_thread(controllerThread, &exitValue);
}
@@ -1237,10 +1240,7 @@ MainWin::_KeyDown(BMessage *msg)
switch (raw_char) {
case B_SPACE:
if (fController->IsPaused() || fController->IsStopped())
fController->Play();
else
fController->Pause();
fController->TogglePlaying();
return B_OK;
case B_ESCAPE:
-139
View File
@@ -1,139 +0,0 @@
/*
* SoundOutput.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "SoundOutput.h"
#include <OS.h>
#include <SoundPlayer.h>
#include <Debug.h>
#include <new>
#include <string.h>
using std::nothrow;
SoundOutput::SoundOutput(const char *name, const media_multi_audio_format &format)
: fSoundPlayer(new(nothrow) BSoundPlayer(static_cast<const media_raw_audio_format *>(&format), name, play_buffer, NULL, this))
, fBuffer(new(nothrow) uint8[format.buffer_size])
, fBufferSize(format.buffer_size)
, fBufferWriteable(create_sem(1, "SoundOutput writeable"))
, fBufferReadable(create_sem(0, "SoundOutput readable"))
, fBufferDuration(0)
, fIsPlaying(false)
{
if (format.channel_count > 0 && format.frame_rate > 0 && (format.format & 0xf) != 0)
fBufferDuration = bigtime_t(1000000.0 * (format.buffer_size / (format.channel_count * (format.format & 0xf))) / format.frame_rate);
printf("SoundOutput: buffer duration is %Ld\n", fBufferDuration);
}
SoundOutput::~SoundOutput()
{
delete fSoundPlayer;
delete_sem(fBufferWriteable);
delete_sem(fBufferReadable);
delete [] fBuffer;
}
status_t
SoundOutput::InitCheck()
{
if (!fSoundPlayer || !fBuffer || fBufferSize <= 0 || fBufferWriteable < B_OK || fBufferReadable < B_OK)
return B_ERROR;
return fSoundPlayer->InitCheck();
}
media_raw_audio_format
SoundOutput::Format() const
{
return fSoundPlayer->Format();
}
bigtime_t
SoundOutput::Latency()
{
bigtime_t latency = 0;
if (InitCheck() >= B_OK)
latency += fSoundPlayer->Latency();
// Because of buffering, latency of SoundOutput is
// slightly higher then the BSoundPlayer latency.
return latency + min_c(1000, fBufferDuration / 4);
}
float
SoundOutput::Volume()
{
return fSoundPlayer->Volume();
}
void
SoundOutput::SetVolume(float new_volume)
{
fSoundPlayer->SetVolume(new_volume);
}
void
SoundOutput::Play(const void *data, size_t size)
{
ASSERT(size > 0 && size <= fBufferSize);
acquire_sem(fBufferWriteable);
memcpy(fBuffer, data, size);
size_t fillsize = fBufferSize - size;
if (fillsize)
memset(fBuffer + size, 0, fillsize);
release_sem(fBufferReadable);
if (!fIsPlaying) {
fSoundPlayer->SetHasData(true);
fSoundPlayer->Start();
fIsPlaying = true;
}
}
void
SoundOutput::PlayBuffer(void *buffer)
{
if (acquire_sem_etc(fBufferReadable, 1, B_RELATIVE_TIMEOUT, fBufferDuration / 2) != B_OK) {
// printf("SoundOutput: buffer not ready, playing silence\n");
memset(buffer, 0, fBufferSize);
return;
}
memcpy(buffer, fBuffer, fBufferSize);
release_sem(fBufferWriteable);
}
void
SoundOutput::play_buffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format & format)
{
ASSERT(size == static_cast<SoundOutput *>(cookie)->fBufferSize);
ASSERT(size == format.buffer_size);
static_cast<SoundOutput *>(cookie)->PlayBuffer(buffer);
}
-58
View File
@@ -1,58 +0,0 @@
/*
* SoundOutput.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef __SOUND_OUTPUT_H
#define __SOUND_OUTPUT_H
#include <MediaDefs.h>
class BSoundPlayer;
class SoundOutput
{
public:
SoundOutput(const char *name, const media_multi_audio_format &format);
~SoundOutput();
status_t InitCheck();
media_raw_audio_format Format() const;
bigtime_t Latency();
float Volume();
void SetVolume(float new_volume);
void Play(const void *data, size_t size);
private:
static void play_buffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format & format);
void PlayBuffer(void *buffer);
private:
BSoundPlayer * fSoundPlayer;
uint8 * fBuffer;
size_t fBufferSize;
sem_id fBufferWriteable;
sem_id fBufferReadable;
bigtime_t fBufferDuration;
bool fIsPlaying;
};
#endif
+124 -224
View File
@@ -1,269 +1,169 @@
/*
* VideoView.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright © 2006-2008 Stephan Aßmus <supersti[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include <Message.h>
#include <Bitmap.h>
#include "VideoView.h"
#include <stdio.h>
#include <string.h>
VideoView::VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags)
: BView(frame, name, resizeMask, flags)
, fController(NULL)
, fOverlayActive(false)
#include <Bitmap.h>
VideoView::VideoView(BRect frame, const char* name, uint32 resizeMask)
: BView(frame, name, resizeMask, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fOverlayMode(false)
{
SetViewColor(B_TRANSPARENT_COLOR);
// SetViewColor(127,255,127);
rgb_color r = {255, 0, 0, 255};
fOverlayKeyColor = r;
// might be reset to overlay key color if overlays are used
SetHighColor(0, 0, 0);
// create some hopefully sensible default overlay restrictions
fOverlayRestrictions.min_width_scale = 0.25;
fOverlayRestrictions.max_width_scale = 8.0;
fOverlayRestrictions.min_height_scale = 0.25;
fOverlayRestrictions.max_height_scale = 8.0;
}
VideoView::~VideoView()
{
}
void
VideoView::SetController(Controller *controller)
{
fController = controller;
}
void
VideoView::AttachedToWindow()
{
}
void
VideoView::OverlayLockAcquire()
{
printf("VideoView::OverlayLockAcquire\n");
}
void
VideoView::OverlayLockRelease()
{
printf("VideoView::OverlayLockRelease\n");
// overlaybitmap->UnlockBits
}
void
VideoView::OverlayScreenshotPrepare()
{
printf("OverlayScreenshotPrepare enter\n");
/*
fController->LockBitmap();
if (fOverlayActive) {
BBitmap *bmp = fController->Bitmap();
if (bmp) {
// Window()->UpdateIfNeeded();
// Sync();
BBitmap *tmp = new BBitmap(bmp->Bounds(), 0, B_RGB32);
// ConvertBitmap(tmp, bmp);
ClearViewOverlay();
DrawBitmap(tmp, Bounds());
delete tmp;
// Sync();
}
}
fController->UnlockBitmap();
*/
printf("OverlayScreenshotPrepare leave\n");
}
void
VideoView::OverlayScreenshotCleanup()
{
printf("OverlayScreenshotCleanup enter\n");
/*
snooze(50000); // give app server some time to take the screenshot
fController->LockBitmap();
if (fOverlayActive) {
BBitmap *bmp = fController->Bitmap();
if (bmp) {
DrawBitmap(bmp, Bounds());
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
Invalidate();
}
}
fController->UnlockBitmap();
*/
printf("OverlayScreenshotCleanup leave\n");
}
void
VideoView::RemoveVideoDisplay()
{
printf("VideoView::RemoveVideoDisplay\n");
if (fOverlayActive) {
ClearViewOverlay();
fOverlayActive = false;
}
Invalidate();
}
void
VideoView::RemoveOverlay()
{
printf("VideoView::RemoveOverlay\n");
if (LockLooperWithTimeout(50000) == B_OK) {
ClearViewOverlay();
fOverlayActive = false;
UnlockLooper();
}
}
void
VideoView::Draw(BRect updateRect)
{
if (fOverlayActive) {
SetHighColor(fOverlayKeyColor);
bool fillBlack = true;
if (LockBitmap()) {
BRect r(Bounds());
if (const BBitmap* bitmap = GetBitmap()) {
fillBlack = false;
if (!fOverlayMode)
DrawBitmap(bitmap, bitmap->Bounds(), r);
}
UnlockBitmap();
}
if (fillBlack)
FillRect(updateRect);
} else {
fController->LockBitmap();
BBitmap *bmp = fController->Bitmap();
if (bmp)
DrawBitmap(bmp, Bounds());
fController->UnlockBitmap();
}
}
void
VideoView::DrawFrame()
VideoView::SetBitmap(const BBitmap* bitmap)
{
// printf("VideoView::DrawFrame\n");
if (LockLooperWithTimeout(50000) != B_OK)
return;
VideoTarget::SetBitmap(bitmap);
// Attention: Don't lock the window, if the bitmap is NULL. Otherwise
// we're going to deadlock when the window tells the node manager to
// stop the nodes (Window -> NodeManager -> VideoConsumer -> VideoView
// -> Window).
if (bitmap && LockLooperWithTimeout(10000) == B_OK) {
if (LockBitmap()) {
// if (fOverlayMode || bitmap->Flags() & B_BITMAP_WILL_OVERLAY) {
if (fOverlayMode || bitmap->ColorSpace() == B_YCbCr422) {
if (!fOverlayMode) {
// init overlay
rgb_color key;
status_t ret = SetViewOverlay(bitmap, bitmap->Bounds(),
Bounds(), &key, B_FOLLOW_ALL,
B_OVERLAY_FILTER_HORIZONTAL
| B_OVERLAY_FILTER_VERTICAL);
if (ret == B_OK) {
fOverlayKeyColor = key;
SetViewColor(key);
SetLowColor(key);
snooze(20000);
FillRect(Bounds(), B_SOLID_LOW);
Sync();
// use overlay from here on
fOverlayMode = true;
fController->LockBitmap();
BBitmap *bmp = fController->Bitmap();
if (bmp) {
bool want_overlay = bmp->ColorSpace() == B_YCbCr422;
if (!want_overlay && fOverlayActive) {
if (LockLooperWithTimeout(50000) == B_OK) {
// update restrictions
overlay_restrictions restrictions;
if (bitmap->GetOverlayRestrictions(&restrictions)
== B_OK)
fOverlayRestrictions = restrictions;
} else {
// try again next time
// synchronous draw
FillRect(Bounds());
Sync();
}
} else {
// transfer overlay channel
rgb_color key;
SetViewOverlay(bitmap, bitmap->Bounds(), Bounds(),
&key, B_FOLLOW_ALL, B_OVERLAY_FILTER_HORIZONTAL
| B_OVERLAY_FILTER_VERTICAL
| B_OVERLAY_TRANSFER_CHANNEL);
}
} else if (fOverlayMode && bitmap->ColorSpace() != B_YCbCr422) {
fOverlayMode = false;
ClearViewOverlay();
UnlockLooper();
fOverlayActive = false;
} else {
printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n");
return;
SetViewColor(B_TRANSPARENT_COLOR);
}
}
if (!fOverlayMode)
DrawBitmap(bitmap, bitmap->Bounds(), Bounds());
if (want_overlay && !fOverlayActive ) {
printf("trying to activate overlay...");
// reserve overlay channel
status_t ret = SetViewOverlay(bmp, bmp->Bounds(), Bounds(),
&fOverlayKeyColor, B_FOLLOW_ALL,
B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
if (ret == B_OK) {
printf("success\n");
fOverlayActive = true;
Invalidate();
} else {
printf("failed: %s\n", strerror(ret));
}
} else if (fOverlayActive) {
// transfer overlay channel
rgb_color overlayKey;
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &overlayKey,
B_FOLLOW_ALL, B_OVERLAY_TRANSFER_CHANNEL
| B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
} else {
// no overlay
DrawBitmap(bmp, Bounds());
UnlockBitmap();
}
UnlockLooper();
}
fController->UnlockBitmap();
UnlockLooper();
}
void
VideoView::MessageReceived(BMessage *msg)
VideoView::GetOverlayScaleLimits(float* minScale, float* maxScale) const
{
switch (msg->what) {
*minScale = max_c(fOverlayRestrictions.min_width_scale,
fOverlayRestrictions.min_height_scale);
*maxScale = max_c(fOverlayRestrictions.max_width_scale,
fOverlayRestrictions.max_height_scale);
}
default:
BView::MessageReceived(msg);
}
void
VideoView::OverlayScreenshotPrepare()
{
// TODO: Do nothing if the current bitmap is in RGB color space
// and no overlay. Otherwise, convert current bitmap to RGB color
// space an draw it in place of the normal display.
}
void
VideoView::OverlayScreenshotCleanup()
{
// TODO: Do nothing if the current bitmap is in RGB color space
// and no overlay. Otherwise clean view area with overlay color.
}
bool
VideoView::IsOverlaySupported()
VideoView::IsOverlayActive()
{
struct colorcombo {
color_space colspace;
const char *name;
} colspace[] = {
{ B_RGB32, "B_RGB32"},
{ B_RGBA32, "B_RGBA32"},
{ B_RGB24, "B_RGB24"},
{ B_RGB16, "B_RGB16"},
{ B_RGB15, "B_RGB15"},
{ B_RGBA15, "B_RGBA15"},
{ B_RGB32_BIG, "B_RGB32_BIG"},
{ B_RGBA32_BIG, "B_RGBA32_BIG "},
{ B_RGB24_BIG, "B_RGB24_BIG "},
{ B_RGB16_BIG, "B_RGB16_BIG "},
{ B_RGB15_BIG, "B_RGB15_BIG "},
{ B_RGBA15_BIG, "B_RGBA15_BIG "},
{ B_YCbCr422, "B_YCbCr422"},
{ B_YCbCr411, "B_YCbCr411"},
{ B_YCbCr444, "B_YCbCr444"},
{ B_YCbCr420, "B_YCbCr420"},
{ B_YUV422, "B_YUV422"},
{ B_YUV411, "B_YUV411"},
{ B_YUV444, "B_YUV444"},
{ B_YUV420, "B_YUV420"},
{ B_NO_COLOR_SPACE, NULL}
};
bool supported = false;
for (int i = 0; colspace[i].name; i++) {
BBitmap *test = new BBitmap(BRect(0,0,319,239), B_BITMAP_WILL_OVERLAY | B_BITMAP_RESERVE_OVERLAY_CHANNEL, colspace[i].colspace);
if (test->InitCheck() == B_OK) {
printf("Display supports %s (0x%08x) overlay\n", colspace[i].name, colspace[i].colspace);
supported = true;
}
delete test;
// if (supported)
// break;
bool active = false;
if (LockBitmap()) {
active = fOverlayMode;
UnlockBitmap();
}
return supported;
return active;
}
void
VideoView::DisableOverlay()
{
if (!fOverlayMode)
return;
FillRect(Bounds());
Sync();
ClearViewOverlay();
snooze(20000);
Sync();
fOverlayMode = false;
}
+29 -45
View File
@@ -1,58 +1,42 @@
/*
* VideoView.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright © 2006-2008 Stephan Aßmus <supersti[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef __VIDEO_VIEW_H
#define __VIDEO_VIEW_H
#ifndef VIDEO_VIEW_H
#define VIDEO_VIEW_H
#include <View.h>
#include "Controller.h"
class VideoView : public BView
{
#include "VideoTarget.h"
class VideoView : public BView, public VideoTarget {
public:
VideoView(BRect frame, const char *name, uint32 resizeMask, uint32 flags);
~VideoView();
VideoView(BRect frame, const char* name,
uint32 resizeMask);
virtual ~VideoView();
void SetController(Controller *controller);
void RemoveVideoDisplay();
void RemoveOverlay();
bool IsOverlaySupported();
// BView interface
virtual void Draw(BRect updateRect);
void OverlayLockAcquire();
void OverlayLockRelease();
// VideoTarget interface
virtual void SetBitmap(const BBitmap* bitmap);
void OverlayScreenshotPrepare();
void OverlayScreenshotCleanup();
void DrawFrame();
// VideoView
void GetOverlayScaleLimits(float* minScale,
float* maxScale) const;
void OverlayScreenshotPrepare();
void OverlayScreenshotCleanup();
bool IsOverlayActive();
void DisableOverlay();
private:
void AttachedToWindow();
void MessageReceived(BMessage *msg);
void Draw(BRect updateRect);
private:
Controller * fController;
volatile bool fOverlayActive;
rgb_color fOverlayKeyColor;
bool fOverlayMode;
overlay_restrictions fOverlayRestrictions;
rgb_color fOverlayKeyColor;
};
#endif
#endif // VIDEO_VIEW_H
@@ -0,0 +1,688 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "NodeManager.h"
#include <stdio.h>
#include <string.h>
#include <MediaRoster.h>
#include <scheduler.h>
#include <TimeSource.h>
#include "AudioProducer.h"
#include "AudioSupplier.h"
#include "VideoConsumer.h"
#include "VideoProducer.h"
#include "VideoSupplier.h"
// debugging
//#define TRACE_NODE_MANAGER
#ifdef TRACE_NODE_MANAGER
# define TRACE(x...) printf(x)
# define ERROR(x...) fprintf(stderr, x)
#else
# define TRACE(x...)
# define ERROR(x...) fprintf(stderr, x)
#endif
#define print_error(str, status) printf(str ", error: %s\n", strerror(status))
NodeManager::Connection::Connection()
: connected(false)
{
memset(&format, 0, sizeof(media_format));
}
// constructor
NodeManager::NodeManager()
: PlaybackManager(),
fMediaRoster(NULL),
fAudioProducer(NULL),
fVideoConsumer(NULL),
fVideoProducer(NULL),
fTimeSource(media_node::null),
fAudioConnection(),
fVideoConnection(),
fPerformanceTimeBase(0),
fStatus(B_NO_INIT),
fVideoTarget(NULL),
fAudioSupplier(NULL),
fVideoSupplier(NULL),
fVideoBounds(0, 0, -1, -1)
{
}
// destructor
NodeManager::~NodeManager()
{
_StopNodes();
_TearDownNodes();
}
// Init
status_t
NodeManager::Init(BRect videoBounds, float videoFrameRate, int32 loopingMode,
bool loopingEnabled, float speed)
{
// init base class
PlaybackManager::Init(videoFrameRate, loopingMode, loopingEnabled, speed);
// get some objects from a derived class
if (!fVideoTarget)
fVideoTarget = CreateVideoTarget();
if (!fVideoSupplier)
fVideoSupplier = CreateVideoSupplier();
if (!fAudioSupplier)
fAudioSupplier = CreateAudioSupplier();
return FormatChanged(videoBounds, videoFrameRate, true);
}
// InitCheck
status_t
NodeManager::InitCheck()
{
return fStatus;
}
// SetPlayMode
void
NodeManager::SetPlayMode(int32 mode, bool continuePlaying)
{
// if (fMediaRoster && fMediaRoster->Lock()) {
// BMediaNode::run_mode runMode = mode > 0 ?
// BMediaNode::B_DROP_DATA : BMediaNode::B_OFFLINE;
// fMediaRoster->SetRunModeNode(fVideoConnection.consumer, runMode);
// fMediaRoster->Unlock();
// }
PlaybackManager::SetPlayMode(mode, continuePlaying);
}
// CleanupNodes
status_t
NodeManager::CleanupNodes()
{
_StopNodes();
return _TearDownNodes(false);
}
// FormatChanged
status_t
NodeManager::FormatChanged(BRect videoBounds, float videoFrameRate, bool force)
{
if (!force && videoBounds == VideoBounds()
&& videoFrameRate == FramesPerSecond())
return B_OK;
if (videoFrameRate != FramesPerSecond()) {
PlaybackManager::Init(videoFrameRate, LoopMode(), IsLoopingEnabled(),
Speed(), MODE_PLAYING_PAUSED_FORWARD, CurrentFrame());
}
_StopNodes();
_TearDownNodes();
SetVideoBounds(videoBounds);
status_t ret = _SetUpNodes();
if (ret == B_OK)
_StartNodes();
else
fprintf(stderr, "unable to setup nodes: %s\n", strerror(ret));
return ret;
}
// RealTimeForTime
bigtime_t
NodeManager::RealTimeForTime(bigtime_t time) const
{
bigtime_t result = 0;
if (fVideoProducer) {
result = fVideoProducer->TimeSource()->RealTimeFor(
fPerformanceTimeBase + time, 0);
}
return result;
}
// TimeForRealTime
bigtime_t
NodeManager::TimeForRealTime(bigtime_t time) const
{
bigtime_t result = 0;
if (fVideoProducer) {
result = fVideoProducer->TimeSource()->PerformanceTimeFor(time)
- fPerformanceTimeBase;
} else if (fAudioProducer) {
result = fAudioProducer->TimeSource()->PerformanceTimeFor(time)
- fPerformanceTimeBase;
}
return result;
}
// SetCurrentAudioTime
void
NodeManager::SetCurrentAudioTime(bigtime_t time)
{
//printf("NodeManager::SetCurrentAudioTime(%lld)\n", time);
PlaybackManager::SetCurrentAudioTime(time);
if (!fVideoProducer) {
// running without video, update video time as well
PlaybackManager::SetCurrentVideoTime(time);
}
}
// SetVideoBounds
void
NodeManager::SetVideoBounds(BRect bounds)
{
if (bounds != fVideoBounds) {
fVideoBounds = bounds;
NotifyVideoBoundsChanged(fVideoBounds);
}
}
// VideoBounds
BRect
NodeManager::VideoBounds() const
{
return fVideoBounds;
}
// SetVideoTarget
void
NodeManager::SetVideoTarget(VideoTarget* videoTarget)
{
if (videoTarget != fVideoTarget) {
fVideoTarget = videoTarget;
if (fVideoConsumer)
fVideoConsumer->SetTarget(fVideoTarget);
}
}
// GetVideoTarget
VideoTarget*
NodeManager::GetVideoTarget() const
{
return fVideoTarget;
}
// SetVolume
void
NodeManager::SetVolume(float percent)
{
// TODO: would be nice to set the volume on the system mixer input of
// our audio node...
}
// #pragma mark -
// _SetUpNodes
status_t
NodeManager::_SetUpNodes()
{
printf("NodeManager::_SetUpNodes()\n");
// find the media roster
fStatus = B_OK;
fMediaRoster = BMediaRoster::Roster(&fStatus);
if (fStatus != B_OK) {
print_error("Can't find the media roster", fStatus);
fMediaRoster = NULL;
return fStatus;
}
if (!fMediaRoster->Lock())
return B_ERROR;
// find the time source
fStatus = fMediaRoster->GetTimeSource(&fTimeSource);
if (fStatus != B_OK) {
print_error("Can't get a time source", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
// setup the video nodes
if (fVideoBounds.IsValid()) {
fStatus = _SetUpVideoNodes();
if (fStatus != B_OK) {
print_error("Error setting up video nodes", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
} else
printf("running without video node\n");
// setup the audio nodes
fStatus = _SetUpAudioNodes();
if (fStatus != B_OK) {
print_error("Error setting up video nodes", fStatus);
fMediaRoster->Unlock();
return fStatus;
}
// we're done mocking with the media roster
fMediaRoster->Unlock();
return fStatus;
}
// _SetUpVideoNodes
status_t
NodeManager::_SetUpVideoNodes()
{
// create the video producer node
fVideoProducer = new VideoProducer(NULL, "MediaPlayer Video Out", 0,
this, fVideoSupplier);
// register the producer node
fStatus = fMediaRoster->RegisterNode(fVideoProducer);
if (fStatus != B_OK) {
print_error("Can't register the video producer", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fVideoProducer->Node().node,
// &fVideoConnection.producer);
fVideoConnection.producer = fVideoProducer->Node();
// create the video consumer node
fVideoConsumer = new VideoConsumer("MediaPlayer Video In", NULL, 0, this,
fVideoTarget);
// register the consumer node
fStatus = fMediaRoster->RegisterNode(fVideoConsumer);
if (fStatus != B_OK) {
print_error("Can't register the video consumer", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fVideoConsumer->Node().node,
// &fVideoConnection.consumer);
fVideoConnection.consumer = fVideoConsumer->Node();
// find free producer output
media_input videoInput;
media_output videoOutput;
int32 count = 1;
fStatus = fMediaRoster->GetFreeOutputsFor(fVideoConnection.producer,
&videoOutput, 1, &count, B_MEDIA_RAW_VIDEO);
if (fStatus != B_OK || count < 1) {
fStatus = B_RESOURCE_UNAVAILABLE;
print_error("Can't find an available video stream", fStatus);
return fStatus;
}
// find free consumer input
count = 1;
fStatus = fMediaRoster->GetFreeInputsFor(fVideoConnection.consumer,
&videoInput, 1, &count, B_MEDIA_RAW_VIDEO);
if (fStatus != B_OK || count < 1) {
fStatus = B_RESOURCE_UNAVAILABLE;
print_error("Can't find an available connection to the video window",
fStatus);
return fStatus;
}
// connect the nodes
media_format format;
format.type = B_MEDIA_RAW_VIDEO;
media_raw_video_format videoFormat = {
FramesPerSecond(), 1, 0,
fVideoBounds.IntegerWidth(),
B_VIDEO_TOP_LEFT_RIGHT, 1, 1,
{
B_YCbCr422,
fVideoBounds.IntegerWidth() + 1,
fVideoBounds.IntegerHeight() + 1,
0, 0, 0
}
};
format.u.raw_video = videoFormat;
// connect video producer to consumer (B_YCbCr422)
fStatus = fMediaRoster->Connect(videoOutput.source, videoInput.destination,
&format, &videoOutput, &videoInput);
if (fStatus != B_OK) {
print_error("Can't connect the video source to the video window... "
"trying B_RGB32", fStatus);
format.u.raw_video.display.format = B_RGB32;
// connect video producer to consumer (B_RGB32)
fStatus = fMediaRoster->Connect(videoOutput.source,
videoInput.destination, &format, &videoOutput, &videoInput);
}
// bail if second attempt failed too
if (fStatus != B_OK) {
print_error("Can't connect the video source to the video window",
fStatus);
return fStatus;
}
// the inputs and outputs might have been reassigned during the
// nodes' negotiation of the Connect(). That's why we wait until
// after Connect() finishes to save their contents.
fVideoConnection.format = format;
fVideoConnection.source = videoOutput.source;
fVideoConnection.destination = videoInput.destination;
fVideoConnection.connected = true;
// set time sources
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConnection.producer.node,
fTimeSource.node);
if (fStatus != B_OK) {
print_error("Can't set the timesource for the video source", fStatus);
return fStatus;
}
fStatus = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
fTimeSource.node);
if (fStatus != B_OK) {
print_error("Can't set the timesource for the video window", fStatus);
return fStatus;
}
return fStatus;
}
// _SetUpAudioNodes
status_t
NodeManager::_SetUpAudioNodes()
{
fAudioProducer = new AudioProducer("MediaPlayer Audio Out", fAudioSupplier);
fStatus = fMediaRoster->RegisterNode(fAudioProducer);
if (fStatus != B_OK) {
print_error("unable to register audio producer node!\n", fStatus);
return fStatus;
}
// make sure the Media Roster knows that we're using the node
// fMediaRoster->GetNodeFor(fAudioProducer->Node().node,
// &fAudioConnection.producer);
fAudioConnection.producer = fAudioProducer->Node();
// connect to the mixer
fStatus = fMediaRoster->GetAudioMixer(&fAudioConnection.consumer);
if (fStatus != B_OK) {
print_error("unable to get the system mixer", fStatus);
return fStatus;
}
fMediaRoster->SetTimeSourceFor(fAudioConnection.producer.node,
fTimeSource.node);
// got the nodes; now we find the endpoints of the connection
media_input mixerInput;
media_output soundOutput;
int32 count = 1;
fStatus = fMediaRoster->GetFreeOutputsFor(fAudioConnection.producer,
&soundOutput, 1, &count);
if (fStatus != B_OK) {
print_error("unable to get a free output from the producer node",
fStatus);
return fStatus;
}
count = 1;
fStatus = fMediaRoster->GetFreeInputsFor(fAudioConnection.consumer,
&mixerInput, 1, &count);
if (fStatus != B_OK) {
print_error("unable to get a free input to the mixer", fStatus);
return fStatus;
}
// got the endpoints; now we connect it!
media_format audio_format;
audio_format.type = B_MEDIA_RAW_AUDIO;
audio_format.u.raw_audio = media_raw_audio_format::wildcard;
fStatus = fMediaRoster->Connect(soundOutput.source, mixerInput.destination,
&audio_format, &soundOutput, &mixerInput);
if (fStatus != B_OK) {
print_error("unable to connect audio nodes", fStatus);
return fStatus;
}
// the inputs and outputs might have been reassigned during the
// nodes' negotiation of the Connect(). That's why we wait until
// after Connect() finishes to save their contents.
fAudioConnection.format = audio_format;
fAudioConnection.source = soundOutput.source;
fAudioConnection.destination = mixerInput.destination;
fAudioConnection.connected = true;
// Set an appropriate run mode for the producer
fMediaRoster->SetRunModeNode(fAudioConnection.producer,
BMediaNode::B_INCREASE_LATENCY);
return fStatus;
}
// _TearDownNodes
status_t
NodeManager::_TearDownNodes(bool disconnect)
{
TRACE("NodeManager::_TearDownNodes()\n");
status_t err = B_OK;
fMediaRoster = BMediaRoster::Roster(&err);
if (err != B_OK) {
fprintf(stderr, "NodeManager::_TearDownNodes() - error getting media "
"roster: %s\n", strerror(err));
fMediaRoster = NULL;
}
// begin mucking with the media roster
bool mediaRosterLocked = false;
if (fMediaRoster && fMediaRoster->Lock())
mediaRosterLocked = true;
if (fVideoConsumer && fVideoProducer && fVideoConnection.connected) {
// disconnect
if (fMediaRoster) {
TRACE(" disconnecting video...\n");
err = fMediaRoster->Disconnect(fVideoConnection.producer.node,
fVideoConnection.source, fVideoConnection.consumer.node,
fVideoConnection.destination);
if (err < B_OK)
print_error("unable to disconnect video nodes", err);
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
"disconnect video nodes, no media server!\n");
}
fVideoConnection.connected = false;
}
if (fVideoProducer) {
TRACE(" releasing video producer...\n");
fVideoProducer->Release();
fVideoProducer = NULL;
}
if (fVideoConsumer) {
TRACE(" releasing video consumer...\n");
fVideoConsumer->Release();
fVideoConsumer = NULL;
}
if (fAudioProducer) {
disconnect = fAudioConnection.connected;
// Ordinarily we'd stop *all* of the nodes in the chain at this point.
// However, one of the nodes is the System Mixer, and stopping the
// Mixer is a Bad Idea (tm). So, we just disconnect from it, and
// release our references to the nodes that we're using. We *are*
// supposed to do that even for global nodes like the Mixer.
if (fMediaRoster && disconnect) {
TRACE(" disconnecting audio...\n");
err = fMediaRoster->Disconnect(fAudioConnection.producer.node,
fAudioConnection.source, fAudioConnection.consumer.node,
fAudioConnection.destination);
if (err < B_OK) {
print_error("unable to disconnect audio nodes", err);
disconnect = false;
}
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot "
"disconnect audio nodes, no media server!\n");
}
TRACE(" releasing audio producer...\n");
fAudioProducer->Release();
fAudioProducer = NULL;
fAudioConnection.connected = false;
if (fMediaRoster && disconnect) {
TRACE(" releasing audio consumer...\n");
fMediaRoster->ReleaseNode(fAudioConnection.consumer);
} else {
fprintf(stderr, "NodeManager::_TearDownNodes() - cannot release "
"audio consumer (system mixer)!\n");
}
}
// we're done mucking with the media roster
if (mediaRosterLocked && fMediaRoster)
fMediaRoster->Unlock();
TRACE("NodeManager::_TearDownNodes() done\n");
return err;
}
// _StartNodes
status_t
NodeManager::_StartNodes()
{
status_t status = B_NO_INIT;
if (!fMediaRoster || !fAudioProducer)
return status;
// begin mucking with the media roster
if (fMediaRoster->Lock()) {
bigtime_t latency = 0;
bigtime_t initLatency = 0;
if (fVideoProducer && fVideoConsumer) {
// figure out what recording delay to use
status = fMediaRoster->GetLatencyFor(fVideoConnection.producer,
&latency);
if (status < B_OK) {
print_error("error getting latency for video producer",
status);
} else
TRACE("video latency: %Ld\n", latency);
status = fMediaRoster->SetProducerRunModeDelay(
fVideoConnection.producer, latency);
if (status < B_OK) {
print_error("error settings run mode delay for video producer",
status);
}
// start the nodes
status = fMediaRoster->GetInitialLatencyFor(
fVideoConnection.producer, &initLatency);
if (status < B_OK) {
print_error("error getting initial latency for video producer",
status);
}
}
initLatency += estimate_max_scheduling_latency();
bigtime_t audioLatency = 0;
status = fMediaRoster->GetLatencyFor(fAudioConnection.producer,
&audioLatency);
TRACE("audio latency: %Ld\n", audioLatency);
BTimeSource* timeSource;
if (fVideoProducer) {
timeSource = fMediaRoster->MakeTimeSourceFor(
fVideoConnection.producer);
} else {
timeSource = fMediaRoster->MakeTimeSourceFor(
fAudioConnection.producer);
}
bool running = timeSource->IsRunning();
// workaround for people without sound cards
// because the system time source won't be running
bigtime_t real = BTimeSource::RealTime();
if (!running) {
status = fMediaRoster->StartTimeSource(fTimeSource, real);
if (status != B_OK) {
timeSource->Release();
print_error("cannot start time source!", status);
return status;
}
status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real);
if (status != B_OK) {
timeSource->Release();
print_error("cannot seek time source!", status);
return status;
}
}
bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
+ initLatency);
timeSource->Release();
// start the nodes
if (fVideoProducer && fVideoConsumer) {
status = fMediaRoster->StartNode(fVideoConnection.consumer, perf);
if (status != B_OK) {
print_error("Can't start the video consumer", status);
return status;
}
status = fMediaRoster->StartNode(fVideoConnection.producer, perf);
if (status != B_OK) {
print_error("Can't start the video producer", status);
return status;
}
}
fAudioProducer->SetRunning(true);
status = fMediaRoster->StartNode(fAudioConnection.producer, perf);
if (status != B_OK) {
print_error("Can't start the audio producer", status);
return status;
}
fPerformanceTimeBase = perf;
// done mucking with the media roster
fMediaRoster->Unlock();
}
return status;
}
// _StopNodes
void
NodeManager::_StopNodes()
{
TRACE("NodeManager::_StopNodes()\n");
// if (PlayMode() == MODE_PLAYING_PAUSED_FORWARD
// || PlayMode() == MODE_PLAYING_PAUSED_FORWARD)
// return;
fMediaRoster = BMediaRoster::Roster();
if (!fMediaRoster) {
if (fAudioProducer)
fAudioProducer->SetRunning(false);
return;
} else if (fMediaRoster->Lock()) {
// begin mucking with the media roster
if (fVideoProducer) {
TRACE(" stopping video producer...\n");
fMediaRoster->StopNode(fVideoConnection.producer, 0, true);
}
if (fAudioProducer) {
TRACE(" stopping audio producer...\n");
fAudioProducer->SetRunning(false);
fMediaRoster->StopNode(fAudioConnection.producer, 0, true);
// synchronous stop
}
if (fVideoConsumer) {
TRACE(" stopping video consumer...\n");
fMediaRoster->StopNode(fVideoConnection.consumer, 0, true);
}
TRACE(" all nodes stopped\n");
// done mucking with the media roster
fMediaRoster->Unlock();
}
TRACE("NodeManager::_StopNodes() done\n");
}
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
//! This class controls our media nodes and general playback
#ifndef NODE_MANAGER_H
#define NODE_MANAGER_H
#include <MediaNode.h>
#include "PlaybackManager.h"
class AudioProducer;
class VideoTarget;
class VideoProducer;
class VideoConsumer;
class AudioSupplier;
class VideoSupplier;
class NodeManager : public PlaybackManager {
public:
NodeManager();
virtual ~NodeManager();
// must be implemented in derived classes
virtual VideoTarget* CreateVideoTarget() = 0;
virtual VideoSupplier* CreateVideoSupplier() = 0;
virtual AudioSupplier* CreateAudioSupplier() = 0;
// NodeManager
status_t Init(BRect videoBounds, float videoFrameRate,
int32 loopingMode = LOOPING_ALL,
bool loopingEnabled = true,
float speed = 1.0);
status_t InitCheck();
// only call this if the
// media_server has died!
status_t CleanupNodes();
status_t FormatChanged(BRect videoBounds,
float videoFrameRate, bool force = false);
virtual void SetPlayMode(int32 mode,
bool continuePlaying = true);
virtual bigtime_t RealTimeForTime(bigtime_t time) const;
virtual bigtime_t TimeForRealTime(bigtime_t time) const;
virtual void SetCurrentAudioTime(bigtime_t time);
void SetVideoBounds(BRect bounds);
virtual BRect VideoBounds() const;
void SetVideoTarget(VideoTarget* vcTarget);
VideoTarget* GetVideoTarget() const;
virtual void SetVolume(float percent);
private:
status_t _SetUpNodes();
status_t _SetUpVideoNodes();
status_t _SetUpAudioNodes();
status_t _TearDownNodes(bool disconnect = true);
status_t _StartNodes();
void _StopNodes();
private:
struct Connection {
Connection();
media_node producer;
media_node consumer;
media_source source;
media_destination destination;
media_format format;
bool connected;
};
private:
BMediaRoster* fMediaRoster;
// media nodes
AudioProducer* fAudioProducer;
VideoConsumer* fVideoConsumer;
VideoProducer* fVideoProducer;
media_node fTimeSource;
Connection fAudioConnection;
Connection fVideoConnection;
bigtime_t fPerformanceTimeBase;
status_t fStatus;
//
VideoTarget* fVideoTarget;
AudioSupplier* fAudioSupplier;
VideoSupplier* fVideoSupplier;
BRect fVideoBounds;
};
#endif // NODE_MANAGER_H
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include <Message.h>
#include "PlaybackLOAdapter.h"
PlaybackLOAdapter::PlaybackLOAdapter(BHandler* handler)
: AbstractLOAdapter(handler)
{
}
PlaybackLOAdapter::PlaybackLOAdapter(const BMessenger& messenger)
: AbstractLOAdapter(messenger)
{
}
PlaybackLOAdapter::~PlaybackLOAdapter()
{
}
void
PlaybackLOAdapter::PlayModeChanged(int32 mode)
{
BMessage message(MSG_PLAYBACK_PLAY_MODE_CHANGED);
message.AddInt32("play mode", mode);
DeliverMessage(message);
}
void
PlaybackLOAdapter::LoopModeChanged(int32 mode)
{
BMessage message(MSG_PLAYBACK_LOOP_MODE_CHANGED);
message.AddInt32("loop mode", mode);
DeliverMessage(message);
}
void
PlaybackLOAdapter::LoopingEnabledChanged(bool enabled)
{
BMessage message(MSG_PLAYBACK_LOOPING_ENABLED_CHANGED);
message.AddBool("looping enabled", enabled);
DeliverMessage(message);
}
void
PlaybackLOAdapter::VideoBoundsChanged(BRect bounds)
{
BMessage message(MSG_PLAYBACK_VIDEO_BOUNDS_CHANGED);
message.AddRect("video bounds", bounds);
DeliverMessage(message);
}
void
PlaybackLOAdapter::FramesPerSecondChanged(float fps)
{
BMessage message(MSG_PLAYBACK_FPS_CHANGED);
message.AddFloat("fps", fps);
DeliverMessage(message);
}
void
PlaybackLOAdapter::CurrentFrameChanged(double frame)
{
BMessage message(MSG_PLAYBACK_CURRENT_FRAME_CHANGED);
message.AddDouble("current frame", frame);
DeliverMessage(message);
}
void
PlaybackLOAdapter::SpeedChanged(float speed)
{
BMessage message(MSG_PLAYBACK_SPEED_CHANGED);
message.AddFloat("speed", speed);
DeliverMessage(message);
}
void
PlaybackLOAdapter::FrameDropped()
{
DeliverMessage(MSG_PLAYBACK_FRAME_DROPPED);
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef PLAYBACK_LO_ADAPTER_H
#define PLAYBACK_LO_ADAPTER_H
#include "AbstractLOAdapter.h"
#include "PlaybackListener.h"
enum {
MSG_PLAYBACK_PLAY_MODE_CHANGED = 'ppmc',
MSG_PLAYBACK_LOOP_MODE_CHANGED = 'plmc',
MSG_PLAYBACK_LOOPING_ENABLED_CHANGED = 'plec',
MSG_PLAYBACK_VIDEO_BOUNDS_CHANGED = 'pmbc',
MSG_PLAYBACK_FPS_CHANGED = 'pfps',
MSG_PLAYBACK_CURRENT_FRAME_CHANGED = 'pcfc',
MSG_PLAYBACK_SPEED_CHANGED = 'pspc',
MSG_PLAYBACK_FRAME_DROPPED = 'pfdr',
};
class PlaybackLOAdapter : public AbstractLOAdapter, public PlaybackListener {
public:
PlaybackLOAdapter(BHandler* handler);
PlaybackLOAdapter(
const BMessenger& messenger);
virtual ~PlaybackLOAdapter();
virtual void PlayModeChanged(int32 mode);
virtual void LoopModeChanged(int32 mode);
virtual void LoopingEnabledChanged(bool enabled);
virtual void VideoBoundsChanged(BRect bounds);
virtual void FramesPerSecondChanged(float fps);
virtual void CurrentFrameChanged(double frame);
virtual void SpeedChanged(float speed);
virtual void FrameDropped();
};
#endif // PLAYBACK_LO_ADAPTER_H
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "PlaybackListener.h"
#include <stdio.h>
PlaybackListener::PlaybackListener()
{
}
PlaybackListener::~PlaybackListener()
{
}
void
PlaybackListener::PlayModeChanged(int32 mode)
{
}
void
PlaybackListener::LoopModeChanged(int32 mode)
{
}
void
PlaybackListener::LoopingEnabledChanged(bool enabled)
{
}
void
PlaybackListener::VideoBoundsChanged(BRect bounds)
{
}
void
PlaybackListener::FramesPerSecondChanged(float fps)
{
}
void
PlaybackListener::CurrentFrameChanged(double frame)
{
}
void
PlaybackListener::SpeedChanged(float speed)
{
}
void
PlaybackListener::FrameDropped()
{
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
/*! This class listens to a PlaybackManager
The hooks are called by PlaybackManager after it executed a command,
to keep every listener informed. FrameDropped() is something the nodes
can call and it is passed onto the contollers, so that they can respond
by displaying some kind of warning. */
#ifndef PLAYBACK_LISTENER_H
#define PLAYBACK_LISTENER_H
#include <Rect.h>
#include <SupportDefs.h>
class PlaybackListener {
public:
PlaybackListener();
virtual ~PlaybackListener();
virtual void PlayModeChanged(int32 mode);
virtual void LoopModeChanged(int32 mode);
virtual void LoopingEnabledChanged(bool enabled);
virtual void VideoBoundsChanged(BRect bounds);
virtual void FramesPerSecondChanged(float fps);
virtual void SpeedChanged(float speed);
virtual void CurrentFrameChanged(double frame);
virtual void FrameDropped();
};
#endif // PLAYBACK_LISTENER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,234 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
/*! This class controls our playback
Note: Add/RemoveListener() are the only methods that lock the object
themselves. All other methods need it to be locked before.
*/
#ifndef PLAYBACK_MANAGER_H
#define PLAYBACK_MANAGER_H
#include <List.h>
#include <Looper.h>
#include <Rect.h>
class PlaybackListener;
enum {
MODE_PLAYING_FORWARD = 1,
MODE_PLAYING_BACKWARD = 2,
MODE_PLAYING_PAUSED_FORWARD = -1,
MODE_PLAYING_PAUSED_BACKWARD = -2,
};
enum {
LOOPING_ALL = 0,
LOOPING_RANGE = 1,
LOOPING_SELECTION = 2,
LOOPING_VISIBLE = 3,
};
enum {
MSG_PLAYBACK_FORCE_UPDATE = 'pbfu',
MSG_PLAYBACK_SET_RANGE = 'pbsr',
MSG_PLAYBACK_SET_VISIBLE = 'pbsv',
MSG_PLAYBACK_SET_LOOP_MODE = 'pbsl',
};
class PlaybackManager : public BLooper {
private:
struct PlayingState;
struct SpeedInfo;
public:
PlaybackManager();
virtual ~PlaybackManager();
void Init(float frameRate,
int32 loopingMode = LOOPING_ALL,
bool loopingEnabled = true,
float speed = 1.0,
int32 playMode = MODE_PLAYING_PAUSED_FORWARD,
int32 currentFrame = 0);
void Cleanup();
// BHandler interface
virtual void MessageReceived(BMessage* message);
void StartPlaying(bool atBeginning = false);
void StopPlaying();
void TogglePlaying(bool atBeginning = false);
void PausePlaying();
bool IsPlaying() const;
int32 PlayMode() const;
int32 LoopMode() const;
bool IsLoopingEnabled() const;
int32 CurrentFrame() const;
float Speed() const;
virtual void SetFramesPerSecond(float framesPerSecond);
float FramesPerSecond() const;
virtual BRect VideoBounds() const = 0;
virtual void FrameDropped() const;
void DurationChanged();
virtual int64 Duration() = 0;
virtual void SetVolume(float percent) = 0;
// playing state manipulation
void SetCurrentFrame(int64 frame);
virtual void SetPlayMode(int32 mode,
bool continuePlaying = true);
void SetLoopMode(int32 mode,
bool continuePlaying = true);
void SetLoopingEnabled(bool enabled,
bool continuePlaying = true);
void SetSpeed(float speed);
// playing state change info
int64 NextFrame() const;
int64 NextPlaylistFrame() const;
int64 FirstPlaybackRangeFrame();
int64 LastPlaybackRangeFrame();
// playing state access
int64 StartFrameAtFrame(int64 frame);
int64 StartFrameAtTime(bigtime_t time);
int64 EndFrameAtFrame(int64 frame);
int64 EndFrameAtTime(bigtime_t time);
int64 FrameCountAtFrame(int64 frame);
int64 FrameCountAtTime(bigtime_t time);
int32 PlayModeAtFrame(int64 frame);
int32 PlayModeAtTime(bigtime_t time);
int32 LoopModeAtFrame(int64 frame);
int32 LoopModeAtTime(bigtime_t time);
// ...
// playing frame/time/interval info
int64 PlaylistFrameAtFrame(int64 frame,
int32& playingDirection,
bool& newState) const;
int64 PlaylistFrameAtFrame(int64 frame,
int32& playingDirection) const;
int64 PlaylistFrameAtFrame(int64 frame) const;
int64 NextChangeFrame(int64 startFrame,
int64 endFrame) const;
bigtime_t NextChangeTime(bigtime_t startTime,
bigtime_t endTime) const;
void GetPlaylistFrameInterval(
int64 startFrame, int64& endFrame,
int64& xStartFrame, int64& xEndFrame,
int32& playingDirection) const;
// PlaybackManagerInterface
virtual void GetPlaylistTimeInterval(
bigtime_t startTime, bigtime_t& endTime,
bigtime_t& xStartTime, bigtime_t& xEndTime,
float& playingSpeed) const;
// conversion: video frame <-> (performance) time
int64 FrameForTime(bigtime_t time) const;
bigtime_t TimeForFrame(int64 frame) const;
// conversion: (performance) time <-> real time
virtual bigtime_t RealTimeForTime(bigtime_t time) const = 0;
virtual bigtime_t TimeForRealTime(bigtime_t time) const = 0;
// conversion: Playist frame <-> Playlist time
int64 PlaylistFrameForTime(bigtime_t time) const;
bigtime_t PlaylistTimeForFrame(int64 frame) const;
// to be called by audio/video producers
virtual void SetCurrentAudioTime(bigtime_t time);
void SetCurrentVideoFrame(int64 frame);
void SetCurrentVideoTime(bigtime_t time);
void SetPerformanceFrame(int64 frame);
void SetPerformanceTime(bigtime_t time);
// listener support
void AddListener(PlaybackListener* listener);
void RemoveListener(PlaybackListener* listener);
virtual void NotifyPlayModeChanged(int32 mode) const;
virtual void NotifyLoopModeChanged(int32 mode) const;
virtual void NotifyLoopingEnabledChanged(
bool enabled) const;
virtual void NotifyVideoBoundsChanged(BRect bounds) const;
virtual void NotifyFPSChanged(float fps) const;
virtual void NotifyCurrentFrameChanged(int32 frame) const;
virtual void NotifySpeedChanged(float speed) const;
virtual void NotifyFrameDropped() const;
virtual void NotifyStopFrameReached() const;
// debugging
void PrintState(PlayingState* state);
void PrintStateAtFrame(int64 frame);
private:
// state management
void _PushState(PlayingState* state,
bool adjustCurrentFrame);
void _UpdateStates();
int32 _IndexForFrame(int64 frame) const;
int32 _IndexForTime(bigtime_t time) const;
PlayingState* _LastState() const;
PlayingState* _StateAt(int32 index) const;
PlayingState* _StateAtTime(bigtime_t time) const;
PlayingState* _StateAtFrame(int64 frame) const;
static int32 _PlayingDirectionFor(int32 playingMode);
static int32 _PlayingDirectionFor(PlayingState* state);
static void _GetPlayingBoundsFor(PlayingState* state,
int64& startFrame,
int64& endFrame,
int64& frameCount);
static int64 _PlayingStartFrameFor(PlayingState* state);
static int64 _PlayingEndFrameFor(PlayingState* state);
static int64 _RangeFrameForFrame(PlayingState* state,
int64 frame);
static int64 _FrameForRangeFrame(PlayingState* state,
int64 index);
static int64 _NextFrameInRange(PlayingState* state,
int64 frame);
// speed management
void _PushSpeedInfo(SpeedInfo* info);
SpeedInfo* _LastSpeedInfo() const;
SpeedInfo* _SpeedInfoAt(int32 index) const;
int32 _SpeedInfoIndexForFrame(int64 frame) const;
int32 _SpeedInfoIndexForTime(bigtime_t time) const;
SpeedInfo* _SpeedInfoForFrame(int64 frame) const;
SpeedInfo* _SpeedInfoForTime(bigtime_t time) const;
void _UpdateSpeedInfos();
bigtime_t _TimeForLastFrame() const;
bigtime_t _TimeForNextFrame() const;
void _CheckStopPlaying();
private:
BList fStates;
BList fSpeeds;
volatile bigtime_t fCurrentAudioTime;
volatile bigtime_t fCurrentVideoTime;
volatile bigtime_t fPerformanceTime;
volatile float fFrameRate; // video frame rate
volatile bigtime_t fStopPlayingFrame; // frame at which playing
// shall be stopped,
// disabled: -1
BList fListeners;
};
#endif // PLAYBACK_MANAGER_H
@@ -0,0 +1,110 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "AudioAdapter.h"
#include <new>
#include <algobase.h>
#include <stdio.h>
#include <string.h>
#include <ByteOrder.h>
#include "AudioChannelConverter.h"
#include "AudioFormatConverter.h"
#include "AudioResampler.h"
using std::nothrow;
#define TRACE_AUDIO_ADAPTER
#ifdef TRACE_AUDIO_ADAPTER
# define TRACE(x...) printf(x)
#else
# define TRACE(x...)
#endif
AudioAdapter::AudioAdapter(AudioReader* source, const media_format& format)
: AudioReader(format),
fSource(source),
fFinalConverter(NULL),
fFormatConverter(NULL),
fChannelConverter(NULL),
fResampler(NULL)
{
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
fFormat.u.raw_audio.byte_order = hostByteOrder;
if (source && source->Format().type == B_MEDIA_RAW_AUDIO) {
if (fFormat.u.raw_audio.format != source->Format().u.raw_audio.format
|| source->Format().u.raw_audio.byte_order != hostByteOrder) {
TRACE("AudioAdapter() - using format converter\n");
fFormatConverter = new (nothrow) AudioFormatConverter(source,
fFormat.u.raw_audio.format, hostByteOrder);
source = fFormatConverter;
}
if (fFormat.u.raw_audio.frame_rate
!= source->Format().u.raw_audio.frame_rate) {
TRACE("AudioAdapter() - using resampler\n");
fResampler = new (nothrow) AudioResampler(source,
fFormat.u.raw_audio.frame_rate);
source = fResampler;
}
if (fFormat.u.raw_audio.channel_count
!= source->Format().u.raw_audio.channel_count) {
TRACE("AudioAdapter() - using channel converter\n");
fChannelConverter = new (nothrow) AudioChannelConverter(source,
fFormat);
source = fChannelConverter;
}
fFinalConverter = source;
} else
fSource = NULL;
}
AudioAdapter::~AudioAdapter()
{
delete fFormatConverter;
delete fChannelConverter;
delete fResampler;
}
status_t
AudioAdapter::Read(void* buffer, int64 pos, int64 frames)
{
// TRACE("AudioAdapter::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
status_t error = InitCheck();
if (error != B_OK)
return error;
pos += fOutOffset;
status_t ret = fFinalConverter->Read(buffer, pos, frames);
// TRACE("AudioAdapter::Read() done: %s\n", strerror(ret));
return ret;
}
status_t
AudioAdapter::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && !fFinalConverter)
error = B_NO_INIT;
if (error == B_OK && fFinalConverter)
error = fFinalConverter->InitCheck();
return error;
}
AudioReader*
AudioAdapter::Source() const
{
return fSource;
}
@@ -0,0 +1,45 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
/*! This AudioReader slaves an AudioConverter and an AudioResampler
to convert the source data to a given format.
At this time the number of channels cannot be changed and the output
format byte order is set to the one of the host.
If input and output format are the same, the overhead is quit small.
*/
#ifndef AUDIO_ADAPTER_H
#define AUDIO_ADAPTER_H
#include "AudioReader.h"
class AudioChannelConverter;
class AudioFormatConverter;
class AudioResampler;
class AudioAdapter : public AudioReader {
public:
AudioAdapter(AudioReader* source,
const media_format& format);
virtual ~AudioAdapter();
virtual status_t Read(void* buffer, int64 pos, int64 frames);
virtual status_t InitCheck() const;
AudioReader* Source() const;
protected:
void _ConvertChannels(void* buffer,
int64 frames) const;
AudioReader* fSource;
AudioReader* fFinalConverter;
AudioFormatConverter* fFormatConverter;
AudioChannelConverter* fChannelConverter;
AudioResampler* fResampler;
};
#endif // AUDIO_ADAPTER_H
@@ -0,0 +1,140 @@
/*
* Copyright © 2008 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "AudioChannelConverter.h"
#include <new>
#include <algobase.h>
#include <stdio.h>
#include <string.h>
using std::nothrow;
//#define TRACE_AUDIO_CONVERTER
#ifdef TRACE_AUDIO_CONVERTER
# define TRACE(x...) printf(x)
#else
# define TRACE(x...)
#endif
AudioChannelConverter::AudioChannelConverter(AudioReader* source,
const media_format& format)
: AudioReader(format),
fSource(source)
{
// TODO: check the format and make sure everything matches
// except for channel count
}
AudioChannelConverter::~AudioChannelConverter()
{
}
template<typename Type, typename BigType>
static void
convert(Type* inBuffer, Type* outBuffer, int32 inChannels, int32 outChannels,
int32 frames)
{
// TODO: more conversions!
switch (inChannels) {
case 1:
switch (outChannels) {
case 2:
for (int32 i = 0; i < frames; i++) {
*outBuffer++ = *inBuffer;
*outBuffer++ = *inBuffer++;
}
break;
}
break;
case 2:
switch (outChannels) {
case 1:
for (int32 i = 0; i < frames; i++) {
*outBuffer++
= (Type)((BigType)inBuffer[0] + inBuffer[1]) / 2;
inBuffer += 2;
}
break;
}
break;
}
}
status_t
AudioChannelConverter::Read(void* outBuffer, int64 pos, int64 frames)
{
TRACE("AudioChannelConverter::Read(%p, %Ld, %Ld)\n", outBuffer, pos, frames);
status_t error = InitCheck();
if (error != B_OK)
return error;
pos += fOutOffset;
int32 inChannels = fSource->Format().u.raw_audio.channel_count;
int32 outChannels = fFormat.u.raw_audio.channel_count;
TRACE(" convert %ld -> %ld channels\n", inChannels, outChannels);
int32 inSampleSize = fSource->Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 inFrameSize = inSampleSize * inChannels;
uint8* inBuffer = new (nothrow) uint8[inFrameSize * frames];
TRACE(" fSource->Read()\n");
status_t ret = fSource->Read(inBuffer, pos, frames);
if (ret != B_OK) {
delete[] inBuffer;
return ret;
}
// We know that both formats are the same except for channel count
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert<float, float>((float*)inBuffer, (float*)outBuffer,
inChannels, outChannels, frames);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert<int32, int64>((int32*)inBuffer, (int32*)outBuffer,
inChannels, outChannels, frames);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert<int16, int32>((int16*)inBuffer, (int16*)outBuffer,
inChannels, outChannels, frames);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert<uint8, uint16>((uint8*)inBuffer, (uint8*)outBuffer,
inChannels, outChannels, frames);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert<int8, int16>((int8*)inBuffer, (int8*)outBuffer,
inChannels, outChannels, frames);
break;
}
delete[] inBuffer;
TRACE("AudioChannelConverter::Read() done: %s\n", strerror(ret));
return ret;
}
status_t
AudioChannelConverter::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && !fSource)
error = B_NO_INIT;
return error;
}
AudioReader*
AudioChannelConverter::Source() const
{
return fSource;
}
@@ -0,0 +1,32 @@
/*
* Copyright © 2008 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
/*! This AudioReader just converts the source channel count
into another one, e.g. 1 -> 2. Frame rate and sample format
remain unchanged.
*/
#ifndef AUDIO_CHANNEL_CONVERTER_H
#define AUDIO_CHANNEL_CONVERTER_H
#include "AudioReader.h"
class AudioChannelConverter : public AudioReader {
public:
AudioChannelConverter(AudioReader* source,
const media_format& format);
virtual ~AudioChannelConverter();
virtual status_t Read(void* buffer, int64 pos, int64 frames);
virtual status_t InitCheck() const;
AudioReader* Source() const;
protected:
AudioReader* fSource;
};
#endif // AUDIO_CHANNEL_CONVERTER_H
@@ -0,0 +1,381 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* Copyright © 2008 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "AudioFormatConverter.h"
#include <algobase.h>
#include <ByteOrder.h>
#include <MediaDefs.h>
//#define TRACE_AUDIO_CONVERTER
#ifdef TRACE_AUDIO_CONVERTER
# define TRACE(x...) printf(x)
#else
# define TRACE(x...)
#endif
AudioFormatConverter::AudioFormatConverter(AudioReader* source, uint32 format,
uint32 byte_order)
: AudioReader(),
fSource(NULL)
{
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
if (source && source->Format().type == B_MEDIA_RAW_AUDIO
&& source->Format().u.raw_audio.byte_order == hostByteOrder) {
fFormat = source->Format();
fFormat.u.raw_audio.format = format;
fFormat.u.raw_audio.byte_order = byte_order;
int32 inSampleSize = source->Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 outSampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
if (inSampleSize != outSampleSize) {
fFormat.u.raw_audio.buffer_size
= source->Format().u.raw_audio.buffer_size * outSampleSize
/ inSampleSize;
}
} else
source = NULL;
fSource = source;
}
AudioFormatConverter::~AudioFormatConverter()
{
}
struct ReadFloat {
inline int operator()(const void* buffer) const {
// 0 == mid, -1.0 == bottom, 1.0 == top
float b = *(float*)buffer;
if (b < -1.0f)
b = -1.0f;
else if (b > 1.0f)
b = 1.0f;
return (int)((double)b * (double)0x7fffffff);
}
};
struct ReadInt {
inline int operator()(const void* buffer) const {
// 0 == mid, 0x80000001 == bottom, 0x7fffffff == top
short b = *(int*)buffer;
if (b == 0x80000000)
b++;
return b;
}
};
struct ReadShort {
inline int operator()(const void* buffer) const {
// 0 == mid, -32767 == bottom, +32767
short b = *(short*)buffer;
if (b == -32768)
b++;
return int(int64(b) * 0x7fffffff / 32767);
}
};
struct ReadUChar {
inline int operator()(const void* buffer) const {
// 128 == mid, 1 == bottom, 255 == top
uchar b = *(uchar*)buffer;
if (b == 0)
b++;
return int((int64(b) - 0x80) * 0x7fffffff / 127);
}
};
struct ReadChar {
inline int operator()(const void* buffer) const {
// 0 == mid, -127 == bottom, +127 == top
char b = *(char*)buffer;
if (b == 0)
b++;
return int(int64(b) * 0x7fffffff / 127);
}
};
struct WriteFloat {
inline void operator()(void* buffer, int value) const {
*(float*)buffer = (double)value / (double)0x7fffffff;
}
};
struct WriteInt {
inline void operator()(void* buffer, int value) const {
*(int*)buffer = value;
}
};
struct WriteShort {
inline void operator()(void* buffer, int value) const {
*(short*)buffer = (short)(value / (int)0x10000);
}
};
struct WriteUChar {
inline void operator()(void* buffer, int value) const {
*(uchar*)buffer = (uchar)(value / (int)0x1000000 + 128);
}
};
struct WriteChar {
inline void operator()(void* buffer, int value) const {
*(char*)buffer = (char)(value / (int)0x1000000);
}
};
template<typename ReadT, typename WriteT>
static void
convert(const ReadT& read, const WriteT& write,
const char* inBuffer, char* outBuffer, int32 frames,
int32 inSampleSize, int32 outSampleSize, int32 channelCount)
{
for (int32 i = 0; i < frames; i++) {
for (int32 c = 0; c < channelCount; c++) {
write(outBuffer, read(inBuffer));
inBuffer += inSampleSize;
outBuffer += outSampleSize;
}
}
}
static
void
swap_sample_byte_order(void* buffer, uint32 format, size_t length)
{
type_code type = B_ANY_TYPE;
switch (format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
type = B_FLOAT_TYPE;
break;
case media_raw_audio_format::B_AUDIO_INT:
type = B_INT32_TYPE;
break;
case media_raw_audio_format::B_AUDIO_SHORT:
type = B_INT16_TYPE;
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
break;
case media_raw_audio_format::B_AUDIO_CHAR:
break;
}
if (type != B_ANY_TYPE)
swap_data(type, buffer, length, B_SWAP_ALWAYS);
}
status_t
AudioFormatConverter::Read(void* buffer, int64 pos, int64 frames)
{
TRACE("AudioFormatConverter::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
status_t error = InitCheck();
if (error != B_OK) {
TRACE("AudioFormatConverter::Read() done 1\n");
return error;
}
pos += fOutOffset;
if (fFormat.u.raw_audio.format == fSource->Format().u.raw_audio.format
&& fFormat.u.raw_audio.byte_order
== fSource->Format().u.raw_audio.byte_order) {
TRACE("AudioFormatConverter::Read() done 2\n");
return fSource->Read(buffer, pos, frames);
}
int32 inSampleSize = fSource->Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 outSampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 channelCount = fFormat.u.raw_audio.channel_count;
int32 inFrameSize = inSampleSize * channelCount;
int32 outFrameSize = outSampleSize * channelCount;
char* reformatBuffer = NULL;
char* inBuffer = (char*)buffer;
#ifdef TRACE_AUDIO_CONVERTER
char formatString[256];
string_for_format(fSource->Format(), formatString, 256);
TRACE(" source format: %s\n", formatString);
TRACE(" in format : format: %lx, sample size: %ld, channels: %ld, "
"byte order: %lu\n", fSource->Format().u.raw_audio.format,
inSampleSize, channelCount,
fSource->Format().u.raw_audio.byte_order);
TRACE(" out format: format: %lx, sample size: %ld, channels: %ld, "
"byte order: %lu\n", fFormat.u.raw_audio.format, outSampleSize,
channelCount, fFormat.u.raw_audio.byte_order);
#endif // TRACE_AUDIO_CONVERTER
if (inSampleSize != outSampleSize) {
reformatBuffer = new char[frames * inFrameSize];
inBuffer = reformatBuffer;
}
error = fSource->Read(inBuffer, pos, frames);
// convert samples to host endianess
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
if (fSource->Format().u.raw_audio.byte_order != hostByteOrder) {
swap_sample_byte_order(inBuffer, fSource->Format().u.raw_audio.format,
frames * inFrameSize);
}
// convert the sample type
switch (fSource->Format().u.raw_audio.format) {
// float
case media_raw_audio_format::B_AUDIO_FLOAT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadFloat(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadFloat(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadFloat(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadFloat(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
// int
case media_raw_audio_format::B_AUDIO_INT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadInt(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadInt(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadInt(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadInt(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
// short
case media_raw_audio_format::B_AUDIO_SHORT:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadShort(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadShort(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadShort(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadShort(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
// uchar
case media_raw_audio_format::B_AUDIO_UCHAR:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadUChar(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadUChar(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadUChar(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
break;
case media_raw_audio_format::B_AUDIO_CHAR:
convert(ReadUChar(), WriteChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
}
break;
// char
case media_raw_audio_format::B_AUDIO_CHAR:
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
convert(ReadChar(), WriteFloat(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_INT:
convert(ReadChar(), WriteInt(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
convert(ReadChar(), WriteShort(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
convert(ReadChar(), WriteUChar(), inBuffer, (char*)buffer,
frames, inSampleSize, outSampleSize, channelCount);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
break;
}
break;
}
// convert samples to output endianess
if (fFormat.u.raw_audio.byte_order != hostByteOrder) {
swap_sample_byte_order(buffer, fFormat.u.raw_audio.format,
frames * outFrameSize);
}
delete[] reformatBuffer;
TRACE("AudioFormatConverter::Read() done\n");
return B_OK;
}
status_t
AudioFormatConverter::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && !fSource)
error = B_NO_INIT;
if (error == B_OK)
error = fSource->InitCheck();
return error;
}
AudioReader*
AudioFormatConverter::Source() const
{
return fSource;
}
@@ -0,0 +1,33 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* Copyright © 2008 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
/*! This AudioReader just converts the source sample format (and byte order)
into another one, e.g. LE short -> BE float. Frame rate and channel
count remain unchanged.
*/
#ifndef AUDIO_FORMAT_CONVERTER_H
#define AUDIO_FORMAT_CONVERTER_H
#include "AudioReader.h"
class AudioFormatConverter : public AudioReader {
public:
AudioFormatConverter(AudioReader* source,
uint32 format, uint32 byte_order);
virtual ~AudioFormatConverter();
virtual status_t Read(void* buffer, int64 pos, int64 frames);
virtual status_t InitCheck() const;
AudioReader* Source() const;
protected:
AudioReader* fSource;
};
#endif // AUDIO_FORMAT_CONVERTER_H
@@ -0,0 +1,742 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "AudioProducer.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <BufferGroup.h>
#include <Buffer.h>
#include <MediaDefs.h>
#include <ParameterWeb.h>
#include <TimeSource.h>
#include "AudioSupplier.h"
#define DEBUG_TO_FILE 0
#if DEBUG_TO_FILE
# include <Entry.h>
# include <MediaFormats.h>
# include <MediaFile.h>
# include <MediaTrack.h>
#endif // DEBUG_TO_FILE
// debugging
//#define TRACE_AUDIO_PRODUCER
#ifdef TRACE_AUDIO_PRODUCER
# define TRACE(x...) printf(x)
# define ERROR(x...) fprintf(stderr, x)
#else
# define TRACE(x...)
# define ERROR(x...) fprintf(stderr, x)
#endif
#if DEBUG_TO_FILE
static BMediaFile*
init_media_file(media_format format, BMediaTrack** _track)
{
static BMediaFile* file = NULL;
static BMediaTrack* track = NULL;
if (file == NULL) {
entry_ref ref;
get_ref_for_path("/boot/home/Desktop/test.wav", &ref);
media_file_format fileFormat;
int32 cookie = 0;
while (get_next_file_format(&cookie, &fileFormat) == B_OK) {
if (strcmp(fileFormat.short_name, "wav") == 0) {
break;
}
}
file = new BMediaFile(&ref, &fileFormat);
media_codec_info info;
cookie = 0;
while (get_next_encoder(&cookie, &info) == B_OK) {
if (strcmp(info.short_name, "raw-audio") == 0)
break;
}
track = file->CreateTrack(&format, &info);
if (!track)
printf("failed to create track\n");
file->CommitHeader();
}
*_track = track;
return track != NULL ? file : NULL;
}
#endif // DEBUG_TO_FILE
// constructor
AudioProducer::AudioProducer(const char* name, AudioSupplier* supplier,
bool lowLatency)
: BMediaNode(name),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BMediaEventLooper(),
fBufferGroup(NULL),
fLatency(0),
fInternalLatency(0),
fLowLatency(lowLatency),
fOutputEnabled(true),
fFramesSent(0),
fStartTime(0),
fSupplier(supplier),
fRunning(false)
{
TRACE("%p->AudioProducer::AudioProducer(%s, %p, %d)\n", this, name,
supplier, lowLatency);
// initialize our preferred format object
fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
fPreferredFormat.u.raw_audio.format
= media_raw_audio_format::B_AUDIO_FLOAT;
// = media_raw_audio_format::B_AUDIO_SHORT;
fPreferredFormat.u.raw_audio.channel_count = 2;
fPreferredFormat.u.raw_audio.frame_rate = 44100.0;
fPreferredFormat.u.raw_audio.byte_order
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
// NOTE: the (buffer_size * 1000000) needs to be dividable by
// fPreferredFormat.u.raw_audio.frame_rate!
fPreferredFormat.u.raw_audio.buffer_size = 441 * 2
* (fPreferredFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK);
if (!fLowLatency)
fPreferredFormat.u.raw_audio.buffer_size *= 6;
// we're not connected yet
fOutput.destination = media_destination::null;
fOutput.format = fPreferredFormat;
// init the audio supplier
if (fSupplier) {
fSupplier->SetAudioProducer(this);
}
}
AudioProducer::~AudioProducer()
{
TRACE("%p->AudioProducer::~AudioProducer()\n", this);
#if DEBUG_TO_FILE
BMediaTrack* track;
if (BMediaFile* file = init_media_file(fPreferredFormat, &track)) {
printf("deleting file...\n");
track->Flush();
file->ReleaseTrack(track);
file->CloseFile();
delete file;
}
#endif // DEBUG_TO_FILE
// Stop the BMediaEventLooper thread
Quit();
TRACE("AudioProducer::~AudioProducer() done\n");
}
BMediaAddOn*
AudioProducer::AddOn(int32* internalId) const
{
return NULL;
}
status_t
AudioProducer::FormatSuggestionRequested(media_type type, int32 quality,
media_format* _format)
{
TRACE("%p->AudioProducer::FormatSuggestionRequested()\n", this);
if (!_format)
return B_BAD_VALUE;
// this is the format we'll be returning (our preferred format)
*_format = fPreferredFormat;
// a wildcard type is okay; we can specialize it
if (type == B_MEDIA_UNKNOWN_TYPE)
type = B_MEDIA_RAW_AUDIO;
// we only support raw audio
if (type != B_MEDIA_RAW_AUDIO)
return B_MEDIA_BAD_FORMAT;
return B_OK;
}
status_t
AudioProducer::FormatProposal(const media_source& output, media_format* format)
{
TRACE("%p->AudioProducer::FormatProposal()\n", this);
// is this a proposal for our one output?
if (output != fOutput.source) {
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
// TODO: we might want to support different audio formats
// we only support floating-point raw audio, so we always return that, but
// we supply an error code depending on whether we found the proposal
// acceptable.
media_type requestedType = format->type;
*format = fPreferredFormat;
// raw audio or wildcard type, either is okay by us
if (requestedType != B_MEDIA_UNKNOWN_TYPE
&& requestedType != B_MEDIA_RAW_AUDIO) {
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
return B_OK;
}
status_t
AudioProducer::FormatChangeRequested(const media_source& source,
const media_destination& destination, media_format* ioFormat,
int32* _deprecated_)
{
TRACE("%p->AudioProducer::FormatChangeRequested()\n", this);
if (destination != fOutput.destination) {
TRACE(" -> B_MEDIA_BAD_DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
if (source != fOutput.source) {
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
fOutput.format = *ioFormat;
// notify our audio supplier of the format change
if (fSupplier)
fSupplier->SetFormat(fOutput.format);
return _AllocateBuffers(ioFormat);
}
status_t
AudioProducer::GetNextOutput(int32* cookie, media_output* _output)
{
TRACE("%p->AudioProducer::GetNextOutput(%ld)\n", this, *cookie);
// we have only a single output; if we supported multiple outputs, we'd
// iterate over whatever data structure we were using to keep track of
// them.
if (0 == *cookie) {
*_output = fOutput;
*cookie += 1;
return B_OK;
}
return B_BAD_INDEX;
}
status_t
AudioProducer::DisposeOutputCookie(int32 cookie)
{
// do nothing because we don't use the cookie for anything special
return B_OK;
}
status_t
AudioProducer::SetBufferGroup(const media_source& forSource,
BBufferGroup* newGroup)
{
TRACE("%p->AudioProducer::SetBufferGroup()\n", this);
if (forSource != fOutput.source)
return B_MEDIA_BAD_SOURCE;
if (newGroup == fBufferGroup)
return B_OK;
if (fUsingOurBuffers && fBufferGroup)
delete fBufferGroup; // waits for all buffers to recycle
if (newGroup != NULL) {
// we were given a valid group; just use that one from now on
fBufferGroup = newGroup;
fUsingOurBuffers = false;
} else {
// we were passed a NULL group pointer; that means we construct
// our own buffer group to use from now on
size_t size = fOutput.format.u.raw_audio.buffer_size;
int32 count = int32(fLatency / BufferDuration() + 1 + 1);
fBufferGroup = new BBufferGroup(size, count);
fUsingOurBuffers = true;
}
return B_OK;
}
status_t
AudioProducer::GetLatency(bigtime_t* _latency)
{
TRACE("%p->AudioProducer::GetLatency()\n", this);
// report our *total* latency: internal plus downstream plus scheduling
*_latency = EventLatency() + SchedulingLatency();
return B_OK;
}
status_t
AudioProducer::PrepareToConnect(const media_source& what,
const media_destination& where, media_format* format,
media_source* _source, char* _name)
{
TRACE("%p->AudioProducer::PrepareToConnect()\n", this);
// trying to connect something that isn't our source?
if (what != fOutput.source) {
TRACE(" -> B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
// are we already connected?
if (fOutput.destination != media_destination::null) {
TRACE(" -> B_MEDIA_ALREADY_CONNECTED\n");
return B_MEDIA_ALREADY_CONNECTED;
}
// the format may not yet be fully specialized (the consumer might have
// passed back some wildcards). Finish specializing it now, and return an
// error if we don't support the requested format.
if (format->type != B_MEDIA_RAW_AUDIO) {
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
// TODO: we might want to support different audio formats
} else if (format->u.raw_audio.format
!= fPreferredFormat.u.raw_audio.format) {
TRACE(" -> B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
// !!! validate all other fields except for buffer_size here, because the
// consumer might have supplied different values from AcceptFormat()?
// check the buffer size, which may still be wildcarded
if (format->u.raw_audio.buffer_size
== media_raw_audio_format::wildcard.buffer_size) {
// pick something comfortable to suggest
// NOTE: the (buffer_size * 1000000) needs to be dividable by
// fPreferredFormat.u.raw_audio.frame_rate!
// TODO: this needs to depend on the other parameters
// (but it doesn't matter sincer the AudioProducer is not
// currently used like that)
TRACE(" -> adjusting buffer size, it was wildcard\n");
format->u.raw_audio.buffer_size = 441 * 2 * sizeof(float);
}
// Now reserve the connection, and return information about it
fOutput.destination = where;
fOutput.format = *format;
*_source = fOutput.source;
strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH);
TRACE(" -> B_OK\n");
return B_OK;
}
static bigtime_t
estimate_internal_latency(const media_format& format)
{
bigtime_t startTime = system_time();
// calculate the number of samples per buffer
int32 sampleSize = format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 sampleCount = format.u.raw_audio.buffer_size / sampleSize;
// alloc float buffers of this size
const int bufferCount = 10; // number of input buffers
float* buffers[bufferCount + 1];
for (int32 i = 0; i < bufferCount + 1; i++)
buffers[i] = new float[sampleCount];
float* outBuffer = buffers[bufferCount];
// fill all buffers save the last one with arbitrary data and merge them
// into the last one
for (int32 i = 0; i < bufferCount; i++) {
for (int32 k = 0; k < sampleCount; k++) {
buffers[i][k] = ((float)i * (float)k)
/ float(bufferCount * sampleCount);
}
}
for (int32 k = 0; k < sampleCount; k++) {
outBuffer[k] = 0;
for (int32 i = 0; i < bufferCount; i++)
outBuffer[k] += buffers[i][k];
outBuffer[k] /= bufferCount;
}
// cleanup
for (int32 i = 0; i < bufferCount + 1; i++)
delete[] buffers[i];
return system_time() - startTime;
}
void
AudioProducer::Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format,
char* _name)
{
TRACE("AudioProducer::Connect(%s)\n", strerror(error));
// If something earlier failed, Connect() might still be called, but with
// a non-zero error code. When that happens we simply unreserve the
// connection and do nothing else.
if (error != B_OK) {
fOutput.destination = media_destination::null;
fOutput.format = fPreferredFormat;
return;
}
// Okay, the connection has been confirmed. Record the destination and
// format that we agreed on, and report our connection name again.
fOutput.destination = destination;
fOutput.format = format;
strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH);
// tell our audio supplier about the format
if (fSupplier) {
TRACE("AudioProducer::Connect() fSupplier->SetFormat()\n");
fSupplier->SetFormat(fOutput.format);
}
TRACE("AudioProducer::Connect() FindLatencyFor()\n");
// Now that we're connected, we can determine our downstream latency.
// Do so, then make sure we get our events early enough.
media_node_id id;
FindLatencyFor(fOutput.destination, &fLatency, &id);
// Use a dry run to see how long it takes me to fill a buffer of data
size_t sampleSize = fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
size_t samplesPerBuffer
= fOutput.format.u.raw_audio.buffer_size / sampleSize;
fInternalLatency = estimate_internal_latency(fOutput.format);
if (!fLowLatency)
fInternalLatency *= 32;
SetEventLatency(fLatency + fInternalLatency);
// reset our buffer duration, etc. to avoid later calculations
bigtime_t duration = bigtime_t(1000000)
* samplesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate
* fOutput.format.u.raw_audio.channel_count);
TRACE("AudioProducer::Connect() SetBufferDuration(%lld)\n", duration);
SetBufferDuration(duration);
TRACE("AudioProducer::Connect() _AllocateBuffers()\n");
// Set up the buffer group for our connection, as long as nobody handed
// us a buffer group (via SetBufferGroup()) prior to this. That can
// happen, for example, if the consumer calls SetOutputBuffersFor() on
// us from within its Connected() method.
if (!fBufferGroup)
_AllocateBuffers(&fOutput.format);
TRACE("AudioProducer::Connect() done\n");
}
void
AudioProducer::Disconnect(const media_source& what,
const media_destination& where)
{
TRACE("%p->AudioProducer::Disconnect()\n", this);
// Make sure that our connection is the one being disconnected
if ((where == fOutput.destination) && (what == fOutput.source)) {
fOutput.destination = media_destination::null;
fOutput.format = fPreferredFormat;
TRACE("AudioProducer: deleting buffer group...\n");
// Always delete the buffer group, even if it is not ours.
// (See BeBook::SetBufferGroup()).
delete fBufferGroup;
TRACE("AudioProducer: buffer group deleted\n");
fBufferGroup = NULL;
}
TRACE("%p->AudioProducer::Disconnect() done\n", this);
}
void
AudioProducer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
bigtime_t performanceTime)
{
ERROR("%p->AudioProducer::LateNoticeReceived(%lld, %lld)\n", this, howMuch,
performanceTime);
// If we're late, we need to catch up. Respond in a manner appropriate
// to our current run mode.
if (what == fOutput.source) {
if (RunMode() == B_RECORDING) {
// ...
} else if (RunMode() == B_INCREASE_LATENCY) {
fInternalLatency += howMuch;
SetEventLatency(fLatency + fInternalLatency);
} else {
size_t sampleSize
= fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
size_t nSamples
= fOutput.format.u.raw_audio.buffer_size / sampleSize;
fFramesSent += nSamples;
}
}
}
void
AudioProducer::EnableOutput(const media_source& what, bool enabled,
int32* _deprecated_)
{
TRACE("%p->AudioProducer::EnableOutput(%d)\n", this, enabled);
if (what == fOutput.source)
fOutputEnabled = enabled;
}
status_t
AudioProducer::SetPlayRate(int32 numer, int32 denom)
{
return B_ERROR;
}
status_t
AudioProducer::HandleMessage(int32 message, const void *data, size_t size)
{
TRACE("%p->AudioProducer::HandleMessage()\n", this);
return B_ERROR;
}
void
AudioProducer::AdditionalBufferRequested(const media_source& source,
media_buffer_id prevBuffer, bigtime_t prevTime,
const media_seek_tag *prevTag)
{
TRACE("%p->AudioProducer::AdditionalBufferRequested()\n", this);
}
void
AudioProducer::LatencyChanged(const media_source& source,
const media_destination& destination, bigtime_t newLatency, uint32 flags)
{
TRACE("%p->AudioProducer::LatencyChanged(%lld)\n", this, newLatency);
if ((source == fOutput.source) && (destination == fOutput.destination)) {
fLatency = newLatency;
SetEventLatency(fLatency + fInternalLatency);
}
}
void
AudioProducer::NodeRegistered()
{
TRACE("%p->AudioProducer::NodeRegistered()\n", this);
// Start the BMediaEventLooper thread
SetPriority(B_REAL_TIME_PRIORITY);
Run();
// set up as much information about our output as we can
fOutput.source.port = ControlPort();
fOutput.source.id = 0;
fOutput.node = Node();
::strcpy(fOutput.name, "MediaPlayer Sound Output");
}
void
AudioProducer::SetRunMode(run_mode mode)
{
TRACE("%p->AudioProducer::SetRunMode()\n", this);
if (B_OFFLINE == mode)
ReportError(B_NODE_FAILED_SET_RUN_MODE);
else
BBufferProducer::SetRunMode(mode);
}
void
AudioProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
TRACE("%p->AudioProducer::HandleEvent()\n", this);
switch (event->type) {
case BTimedEventQueue::B_START:
TRACE("AudioProducer::HandleEvent(B_START)\n");
if (RunState() != B_STARTED) {
fFramesSent = 0;
fStartTime = event->event_time;
media_timed_event firstBufferEvent(fStartTime,
BTimedEventQueue::B_HANDLE_BUFFER);
EventQueue()->AddEvent(firstBufferEvent);
}
TRACE("AudioProducer::HandleEvent(B_START) done\n");
break;
case BTimedEventQueue::B_STOP:
TRACE("AudioProducer::HandleEvent(B_STOP)\n");
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER);
TRACE("AudioProducer::HandleEvent(B_STOP) done\n");
break;
case BTimedEventQueue::B_HANDLE_BUFFER: {
TRACE("AudioProducer::HandleEvent(B_HANDLE_BUFFER)\n");
if ((RunState() == BMediaEventLooper::B_STARTED)
&& (fOutput.destination != media_destination::null)) {
BBuffer* buffer = _FillNextBuffer(event->event_time);
if (buffer) {
status_t err = B_ERROR;
if (fOutputEnabled)
err = SendBuffer(buffer, fOutput.destination);
if (err)
buffer->Recycle();
}
size_t sampleSize
= fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
size_t nFrames = fOutput.format.u.raw_audio.buffer_size
/ (sampleSize
* fOutput.format.u.raw_audio.channel_count);
fFramesSent += nFrames;
bigtime_t nextEvent = fStartTime
+ bigtime_t(double(fFramesSent) * 1000000.0
/ double(fOutput.format.u.raw_audio.frame_rate));
media_timed_event nextBufferEvent(nextEvent,
BTimedEventQueue::B_HANDLE_BUFFER);
EventQueue()->AddEvent(nextBufferEvent);
}
TRACE("AudioProducer::HandleEvent(B_HANDLE_BUFFER) done\n");
break;
}
default:
break;
}
}
void
AudioProducer::SetRunning(bool running)
{
TRACE("%p->AudioProducer::SetRunning(%d)\n", this, running);
fRunning = running;
}
// #pragma mark -
status_t
AudioProducer::_AllocateBuffers(media_format* format)
{
TRACE("%p->AudioProducer::_AllocateBuffers()\n", this);
if (fBufferGroup && fUsingOurBuffers) {
delete fBufferGroup;
fBufferGroup = NULL;
}
size_t size = format->u.raw_audio.buffer_size;
int32 bufferDuration = BufferDuration();
int32 count = 0;
if (bufferDuration > 0) {
count = (int32)((fLatency + fInternalLatency)
/ bufferDuration + 2);
}
fBufferGroup = new BBufferGroup(size, count);
fUsingOurBuffers = true;
return fBufferGroup->InitCheck();
}
BBuffer*
AudioProducer::_FillNextBuffer(bigtime_t eventTime)
{
BBuffer* buffer = fBufferGroup->RequestBuffer(
fOutput.format.u.raw_audio.buffer_size, BufferDuration());
if (!buffer) {
TRACE("AudioProducer::_FillNextBuffer() - no buffer\n");
return NULL;
}
size_t sampleSize = fOutput.format.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
size_t numSamples = fOutput.format.u.raw_audio.buffer_size / sampleSize;
// number of sample in the buffer
// fill in the buffer header
media_header* hdr = buffer->Header();
hdr->type = B_MEDIA_RAW_AUDIO;
hdr->time_source = TimeSource()->ID();
buffer->SetSizeUsed(fOutput.format.u.raw_audio.buffer_size);
bigtime_t performanceTime = bigtime_t(double(fFramesSent)
* 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate));
// fill in data from audio supplier
size_t frameCount = numSamples / fOutput.format.u.raw_audio.channel_count;
bigtime_t startTime = performanceTime;
bigtime_t endTime = bigtime_t(double(fFramesSent + frameCount)
* 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate));
if (!fSupplier || fSupplier->InitCheck() != B_OK
|| fSupplier->GetFrames(buffer->Data(), frameCount, startTime,
endTime) != B_OK) {
TRACE("AudioProducer::_FillNextBuffer() - error -> silence\n");
memset(buffer->Data(), 0, buffer->SizeUsed());
}
// stamp buffer
if (RunMode() == B_RECORDING) {
hdr->start_time = eventTime;
} else {
hdr->start_time = fStartTime + performanceTime;
}
#if DEBUG_TO_FILE
BMediaTrack* track;
if (BMediaFile* file = init_media_file(fOutput.format, &track)) {
track->WriteFrames(buffer->Data(), frameCount);
}
#endif // DEBUG_TO_FILE
return buffer;
}
@@ -0,0 +1,121 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef AUDIO_PRODUCER_H
#define AUDIO_PRODUCER_H
#include <BufferProducer.h>
//#include <Controllable.h>
#include <MediaEventLooper.h>
class AudioSupplier;
class AudioProducer : public BBufferProducer, public BMediaEventLooper {
public:
AudioProducer(const char* name,
AudioSupplier* supplier,
bool lowLatency = true);
virtual ~AudioProducer();
// BMediaNode interface
virtual BMediaAddOn* AddOn(int32 *internal_id) const;
// BBufferProducer interface
virtual status_t FormatSuggestionRequested(media_type type,
int32 quality, media_format* _format);
virtual status_t FormatProposal(const media_source& output,
media_format* format);
virtual status_t FormatChangeRequested(
const media_source& source,
const media_destination& destination,
media_format* ioFormat,
int32* _deprecated_);
virtual status_t GetNextOutput(int32* cookie,
media_output* _output);
virtual status_t DisposeOutputCookie(int32 cookie);
virtual status_t SetBufferGroup(const media_source& forSource,
BBufferGroup* group);
virtual status_t GetLatency(bigtime_t* _latency);
virtual status_t PrepareToConnect(const media_source& what,
const media_destination& where,
media_format* format,
media_source* outSource, char* outName);
virtual void Connect(status_t error,
const media_source& source,
const media_destination& destination,
const media_format& format,
char* ioName);
virtual void Disconnect(const media_source &what,
const media_destination& where);
virtual void LateNoticeReceived(const media_source& what,
bigtime_t howMuch,
bigtime_t performanceTime);
virtual void EnableOutput(const media_source& what,
bool enabled, int32* _deprecated_);
virtual status_t SetPlayRate(int32 numer, int32 denom);
virtual status_t HandleMessage(int32 message,
const void* data, size_t size);
virtual void AdditionalBufferRequested(
const media_source& source,
media_buffer_id prevBuffer,
bigtime_t prevTime,
const media_seek_tag* prevTag);
// may be NULL
virtual void LatencyChanged(const media_source& source,
const media_destination& destination,
bigtime_t newLatency, uint32 flags);
// BMediaEventLooper interface
virtual void NodeRegistered();
virtual void SetRunMode(run_mode mode);
virtual void HandleEvent(const media_timed_event* event,
bigtime_t lateness,
bool realTimeEvent = false);
void SetRunning(bool running);
private:
status_t _AllocateBuffers(media_format* format);
BBuffer* _FillNextBuffer(bigtime_t eventTime);
void _FillSampleBuffer(float* data,
size_t numSamples);
BBufferGroup* fBufferGroup;
bool fUsingOurBuffers;
bigtime_t fLatency;
bigtime_t fInternalLatency;
bool fLowLatency;
media_output fOutput;
bool fOutputEnabled;
media_format fPreferredFormat;
uint64 fFramesSent;
bigtime_t fStartTime;
AudioSupplier* fSupplier;
volatile bool fRunning;
};
#endif // AUDIO_PRODUCER_H
@@ -0,0 +1,151 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include <algobase.h>
#include <string.h>
#include "AudioReader.h"
AudioReader::AudioReader()
: fFormat(),
fOutOffset(0)
{
}
AudioReader::AudioReader(const media_format& format)
: fFormat(format),
fOutOffset(0)
{
}
AudioReader::~AudioReader()
{
}
status_t
AudioReader::InitCheck() const
{
return B_OK;
}
void
AudioReader::SetFormat(const media_format& format)
{
fFormat = format;
}
const media_format&
AudioReader::Format() const
{
return fFormat;
}
void
AudioReader::SetOutOffset(int64 offset)
{
fOutOffset = offset;
}
int64
AudioReader::OutOffset() const
{
return fOutOffset;
}
int64
AudioReader::FrameForTime(bigtime_t time) const
{
double frameRate = fFormat.u.raw_audio.frame_rate;
return int64(double(time) * frameRate / 1000000.0);
}
bigtime_t
AudioReader::TimeForFrame(int64 frame) const
{
double frameRate = fFormat.u.raw_audio.frame_rate;
return bigtime_t(double(frame) * 1000000.0 / frameRate);
}
//! helper function for ReadSilence()
template<typename sample_t>
inline void
fill_buffer(void* buffer, int32 count, sample_t value)
{
sample_t* buf = (sample_t*)buffer;
sample_t* bufferEnd = buf + count;
for (; buf < bufferEnd; buf++)
*buf = value;
}
/*! Fills the supplied buffer with /frames/ frames of silence and returns a
pointer to the frames after the filled range.
/frames/ must be >= 0.*/
void*
AudioReader::ReadSilence(void* buffer, int64 frames) const
{
void* bufferEnd = SkipFrames(buffer, frames);
int32 sampleCount = frames * fFormat.u.raw_audio.channel_count;
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
fill_buffer(buffer, sampleCount, (float)0);
break;
case media_raw_audio_format::B_AUDIO_INT:
fill_buffer(buffer, sampleCount, (int)0);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
fill_buffer(buffer, sampleCount, (short)0);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
fill_buffer(buffer, sampleCount, (uchar)128);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
fill_buffer(buffer, sampleCount, (uchar)0);
break;
default:
memset(buffer, 0, (char*)bufferEnd - (char*)buffer);
break;
}
return bufferEnd;
}
//! Returns a buffer pointer offset by /frames/ frames.
void*
AudioReader::SkipFrames(void* buffer, int64 frames) const
{
int32 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
return (char*)buffer + frames * frameSize;
}
void
AudioReader::ReverseFrames(void* buffer, int64 frames) const
{
int32 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
char* front = (char*)buffer;
char* back = (char*)buffer + (frames - 1) * frameSize;
while (front < back) {
for (int32 i = 0; i < frameSize; i++)
swap(front[i], back[i]);
front += frameSize;
back -= frameSize;
}
}
@@ -0,0 +1,40 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#ifndef AUDIO_READER_H
#define AUDIO_READER_H
#include <MediaDefs.h>
class AudioReader {
public:
AudioReader();
AudioReader(const media_format& format);
virtual ~AudioReader();
virtual status_t InitCheck() const;
void SetFormat(const media_format& format);
const media_format& Format() const;
virtual status_t Read(void* buffer, int64 pos, int64 frames) = 0;
void SetOutOffset(int64 offset);
int64 OutOffset() const;
int64 FrameForTime(bigtime_t time) const;
bigtime_t TimeForFrame(int64 frame) const;
protected:
void* ReadSilence(void* buffer, int64 frames) const;
void* SkipFrames(void* buffer, int64 frames) const;
void ReverseFrames(void* buffer,
int64 frames) const;
protected:
media_format fFormat;
int64 fOutOffset;
};
#endif // AUDIO_READER_H
@@ -0,0 +1,277 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "AudioResampler.h"
#include <algobase.h>
#include <stdio.h>
#include "SampleBuffer.h"
//#define TRACE_AUDIO_RESAMPLER
#ifdef TRACE_AUDIO_RESAMPLER
# define TRACE(x...) printf(x)
#else
# define TRACE(x...)
#endif
AudioResampler::AudioResampler()
: AudioReader(),
fSource(NULL),
fTimeScale(1.0),
fInOffset(0)
{
}
AudioResampler::AudioResampler(AudioReader* source, float frameRate,
float timeScale)
: AudioReader(),
fSource(NULL),
fTimeScale(timeScale),
fInOffset(0)
{
SetSource(source);
if (fSource)
fFormat.u.raw_audio.frame_rate = frameRate;
}
AudioResampler::~AudioResampler()
{
}
//! Calculates the greatest common divider of /a/ and /b/.
template<typename T> inline
T
gcd(T a, T b)
{
while (b != 0) {
T r = a % b;
a = b;
b = r;
}
return a;
}
template<typename Buffer>
static void
resample_linear(void* _inBuffer, void* _outBuffer, uint32 channelCount,
double inFrameRate, double outFrameRate, int32 frames)
{
typedef double sample_t;
Buffer inBuffer(_inBuffer);
Buffer outFrameBuf(_outBuffer);
for (sample_t outFrame = 0; outFrame < frames; outFrame++) {
// time of the out sample
sample_t outTime = outFrame / outFrameRate;
// first in frame
int64 inFrame = int64(outTime * inFrameRate);
// time of the first and the second in frame
sample_t inTime1 = (sample_t)inFrame / inFrameRate;
sample_t inTime2 = (sample_t)(inFrame + 1) / inFrameRate;
// differences between the out frame time and the in frame times
sample_t timeDiff1 = outTime - inTime1;
sample_t timeDiff2 = inTime2 - outTime;
sample_t timeDiff = timeDiff1 + timeDiff2;
// pointer to the first and second in frame
Buffer inFrameBuf1 = inBuffer + inFrame * channelCount;
Buffer inFrameBuf2 = inFrameBuf1 + channelCount;
for (uint32 c = 0; c < channelCount;
c++, inFrameBuf1++, inFrameBuf2++, outFrameBuf++) {
// sum weighted according to the distance to the respective other
// in frame
outFrameBuf.WriteSample((timeDiff2 * inFrameBuf1.ReadSample()
+ timeDiff1 * inFrameBuf2.ReadSample()) / timeDiff);
}
}
}
status_t
AudioResampler::Read(void* buffer, int64 pos, int64 frames)
{
TRACE("AudioResampler::Read(%p, %Ld, %Ld)\n", buffer, pos, frames);
status_t error = InitCheck();
if (error != B_OK) {
TRACE("AudioResampler::Read() done1\n");
return error;
}
// calculate position and frames in the source data
int64 sourcePos = ConvertToSource(pos);
int64 sourceFrames = ConvertToSource(pos + frames) - sourcePos;
// check the frame counts
if (sourceFrames == frames) {
TRACE("AudioResampler::Read() done2\n");
return fSource->Read(buffer, sourcePos, sourceFrames);
}
if (sourceFrames == 0) {
ReadSilence(buffer, frames);
TRACE("AudioResampler::Read() done3\n");
return B_OK;
}
// check, if playing backwards
bool backward = false;
if (sourceFrames < 0) {
sourceFrames = -sourceFrames;
sourcePos -= sourceFrames;
backward = true;
}
// we need at least two frames to interpolate
sourceFrames += 2;
int32 sampleSize = media_raw_audio_format::B_AUDIO_SIZE_MASK;
uint32 channelCount = fFormat.u.raw_audio.channel_count;
char* inBuffer = new char[sourceFrames * channelCount * sampleSize];
error = fSource->Read(inBuffer, sourcePos, sourceFrames);
if (error != B_OK) {
TRACE("AudioResampler::_ReadLinear() done4\n");
return error;
}
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
double outFrameRate = (double)fFormat.u.raw_audio.frame_rate
/ (double)fTimeScale;
// choose the sample buffer to be used
switch (fFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
resample_linear< FloatSampleBuffer<double> >(inBuffer, buffer,
channelCount, inFrameRate, outFrameRate, (int32)frames);
break;
case media_raw_audio_format::B_AUDIO_INT:
resample_linear< IntSampleBuffer<double> >(inBuffer, buffer,
channelCount, inFrameRate, outFrameRate, (int32)frames);
break;
case media_raw_audio_format::B_AUDIO_SHORT:
resample_linear< ShortSampleBuffer<double> >(inBuffer, buffer,
channelCount, inFrameRate, outFrameRate, (int32)frames);
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
resample_linear< UCharSampleBuffer<double> >(inBuffer, buffer,
channelCount, inFrameRate, outFrameRate, (int32)frames);
break;
case media_raw_audio_format::B_AUDIO_CHAR:
resample_linear< CharSampleBuffer<double> >(inBuffer, buffer,
channelCount, inFrameRate, outFrameRate, (int32)frames);
break;
}
// reverse the frame order if reading backwards
if (backward)
ReverseFrames(buffer, frames);
delete[] inBuffer;
TRACE("AudioResampler::Read() done\n");
return B_OK;
}
status_t
AudioResampler::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && !fSource)
error = B_NO_INIT;
return error;
}
void
AudioResampler::SetSource(AudioReader* source)
{
if (!source) {
TRACE("AudioResampler::SetSource() - NULL source\n");
return;
}
if (source->Format().type != B_MEDIA_RAW_AUDIO) {
TRACE("AudioResampler::SetSource() - not B_MEDIA_RAW_AUDIO\n");
return;
}
uint32 hostByteOrder
= (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
if (source->Format().u.raw_audio.byte_order != hostByteOrder) {
TRACE("AudioResampler::SetSource() - not host byte order\n");
return;
}
float frameRate = FrameRate();
// don't overwrite previous audio frame rate
fSource = source;
fFormat = source->Format();
fFormat.u.raw_audio.frame_rate = frameRate;
}
void
AudioResampler::SetFrameRate(float frameRate)
{
fFormat.u.raw_audio.frame_rate = frameRate;
}
void
AudioResampler::SetTimeScale(float timeScale)
{
fTimeScale = timeScale;
}
AudioReader*
AudioResampler::Source() const
{
return fSource;
}
float
AudioResampler::FrameRate() const
{
return fFormat.u.raw_audio.frame_rate;
}
float
AudioResampler::TimeScale() const
{
return fTimeScale;
}
void
AudioResampler::SetInOffset(int64 offset)
{
fInOffset = offset;
}
int64
AudioResampler::InOffset() const
{
return fInOffset;
}
int64
AudioResampler::ConvertFromSource(int64 pos) const
{
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
double outFrameRate = fFormat.u.raw_audio.frame_rate;
return (int64)((double)(pos - fInOffset) * outFrameRate / inFrameRate
/ (double)fTimeScale) - fOutOffset;
}
int64
AudioResampler::ConvertToSource(int64 pos) const
{
double inFrameRate = fSource->Format().u.raw_audio.frame_rate;
double outFrameRate = fFormat.u.raw_audio.frame_rate;
return (int64)((double)(pos + fOutOffset) * inFrameRate / outFrameRate
* (double)fTimeScale) + fInOffset;
}
@@ -0,0 +1,52 @@
/*
* Copyright © 2000-2006 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
/*! This AudioReader does both resampling an audio source to a different
sample rate and rescaling the time, e.g. it is possible to convert the
source data from 41.1 KHz to 96 KHz played backward twice as fast
(time scale = -2).
*/
#ifndef AUDIO_RESAMPLER_H
#define AUDIO_RESAMPLER_H
#include "AudioReader.h"
class AudioResampler : public AudioReader {
public:
AudioResampler();
AudioResampler(AudioReader* source,
float frameRate, float timeScale = 1.0);
virtual ~AudioResampler();
virtual status_t Read(void* buffer, int64 pos, int64 frames);
virtual status_t InitCheck() const;
void SetSource(AudioReader* source);
void SetFrameRate(float frameRate);
void SetTimeScale(float timeScale);
AudioReader* Source() const;
float FrameRate() const;
float TimeScale() const;
void SetInOffset(int64 offset);
int64 InOffset() const;
int64 ConvertFromSource(int64 pos) const;
int64 ConvertToSource(int64 pos) const;
private:
status_t _ReadLinear(void* buffer, int64 pos,
int64 frames);
private:
AudioReader* fSource;
float fTimeScale; // speed
int64 fInOffset;
};
#endif // AUDIO_RESAMPLER_H
@@ -0,0 +1,31 @@
/* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "AudioSupplier.h"
#include "AudioProducer.h"
AudioSupplier::AudioSupplier()
{
}
AudioSupplier::~AudioSupplier()
{
}
void
AudioSupplier::SetAudioProducer(AudioProducer* producer)
{
fAudioProducer = producer;
}
status_t
AudioSupplier::InitCheck() const
{
return (fAudioProducer ? B_OK : B_NO_INIT);
}
@@ -0,0 +1,36 @@
/* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
/*! This class is an interface used by the AudioProducer to retreive the
audio data to be played. */
#ifndef AUDIO_SUPPLIER_H
#define AUDIO_SUPPLIER_H
#include <MediaDefs.h>
class AudioProducer;
class AudioSupplier {
public:
AudioSupplier();
virtual ~AudioSupplier();
virtual void SetAudioProducer(AudioProducer* producer);
virtual status_t GetFrames(void* buffer, int64 frameCount,
bigtime_t startTime,
bigtime_t endTime) = 0;
virtual void SetFormat(const media_format& format) = 0;
virtual status_t InitCheck() const;
protected:
AudioProducer* fAudioProducer;
};
#endif // AUDIO_SUPPLIER_H
@@ -0,0 +1,151 @@
/*
* Copyright © 2000-2003 Ingo Weinhold <[email protected]>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
// This file contains the following classes:
// * SampleBuffer
// * FloatSampleBuffer
// * IntSampleBuffer
// * ShortSampleBuffer
// * UCharSampleBuffer
// * CharSampleBuffer
#ifndef SAMPLE_BUFFER_H
#define SAMPLE_BUFFER_H
#include <SupportDefs.h>
// SampleBuffer
template<int BYTES_PER_SAMPLE>
class SampleBuffer {
protected:
typedef uint8 sample_block_t[BYTES_PER_SAMPLE];
public:
inline SampleBuffer(void* buffer)
: fBuffer((sample_block_t*)buffer) { }
inline void operator+=(int samples) {
fBuffer += samples;
}
inline void operator-=(int samples) {
fBuffer -= samples;
}
inline void operator++(int) {
fBuffer++;
}
inline void operator--(int) {
fBuffer--;
}
inline void* operator+(int samples) {
return fBuffer + samples;
}
inline void* operator-(int samples) {
return fBuffer + samples;
}
protected:
sample_block_t* fBuffer;
};
// FloatSampleBuffer
template<typename sample_t>
class FloatSampleBuffer : public SampleBuffer<sizeof(float)> {
public:
inline FloatSampleBuffer(void* buffer)
: SampleBuffer<sizeof(float)>(buffer) {
}
inline sample_t ReadSample() const {
return *((float*)fBuffer);
}
inline void WriteSample(sample_t value) {
*((float*)fBuffer) = value;
}
};
// IntSampleBuffer
template<typename sample_t>
class IntSampleBuffer : public SampleBuffer<sizeof(int)> {
public:
inline IntSampleBuffer(void* buffer)
: SampleBuffer<sizeof(int)>(buffer) {
}
inline sample_t ReadSample() const {
return (sample_t)*((int*)fBuffer) / (sample_t)0x7fffffff;
}
inline void WriteSample(sample_t value) {
*((int*)fBuffer) = int(value * (sample_t)0x7fffffff);
}
};
// ShortSampleBuffer
template<typename sample_t>
class ShortSampleBuffer : public SampleBuffer<sizeof(short)> {
public:
inline ShortSampleBuffer(void* buffer)
: SampleBuffer<sizeof(short)>(buffer) {
}
inline sample_t ReadSample() const {
return (sample_t)*((short*)fBuffer) / (sample_t)32767;
}
inline void WriteSample(sample_t value) {
*((short*)fBuffer) = short(value * (sample_t)32767);
}
};
// UCharSampleBuffer
template<typename sample_t>
class UCharSampleBuffer : public SampleBuffer<sizeof(uchar)> {
public:
inline UCharSampleBuffer(void* buffer)
: SampleBuffer<sizeof(uchar)>(buffer) {
}
inline sample_t ReadSample() const {
return ((sample_t)*((uchar*)fBuffer) - 128) / (sample_t)127;
}
inline void WriteSample(sample_t value) {
*((uchar*)fBuffer) = uchar(value * (sample_t)127 + 128);
}
};
// CharSampleBuffer
template<typename sample_t>
class CharSampleBuffer : public SampleBuffer<sizeof(char)> {
public:
inline CharSampleBuffer(void* buffer)
: SampleBuffer<sizeof(char)>(buffer) {
}
inline sample_t ReadSample() const {
return (sample_t)*((char*)fBuffer) / (sample_t)127;
}
inline void WriteSample(sample_t value) {
*((uchar*)fBuffer) = uchar(value * (sample_t)127);
}
};
#endif // SAMPLE_BUFFER_H
@@ -0,0 +1,616 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <[email protected]>,
* Copyright (c) 2000-2008, Stephan Aßmus <[email protected]>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "VideoConsumer.h"
#include <stdio.h>
#include <fcntl.h>
#include <Buffer.h>
#include <unistd.h>
#include <string.h>
#include <NodeInfo.h>
#include <Application.h>
#include <Bitmap.h>
#include <View.h>
#include <Window.h>
#include <scheduler.h>
#include <TimeSource.h>
#include <MediaRoster.h>
#include <BufferGroup.h>
#include "NodeManager.h"
#include "VideoTarget.h"
// debugging
//#define TRACE_VIDEO_CONSUMER
#ifdef TRACE_VIDEO_CONSUMER
# define TRACE(x...) printf(x)
# define PROGRESS(x...) printf(x)
# define FUNCTION(x...) printf(x)
# define LOOP(x...) printf(x)
# define ERROR(x...) fprintf(stderr, x)
#else
# define TRACE(x...)
# define PROGRESS(x...)
# define FUNCTION(x...)
# define LOOP(x...)
# define ERROR(x...) fprintf(stderr, x)
#endif
#define M1 ((double)1000000.0)
#define JITTER 20000
VideoConsumer::VideoConsumer(const char* name, BMediaAddOn* addon,
const uint32 internal_id, NodeManager* manager,
VideoTarget* target)
: BMediaNode(name),
BMediaEventLooper(),
BBufferConsumer(B_MEDIA_RAW_VIDEO),
fInternalID(internal_id),
fAddOn(addon),
fConnectionActive(false),
fMyLatency(20000),
fOurBuffers(false),
fBuffers(NULL),
fManager(manager),
fTargetLock(),
fTarget(target),
fTargetBufferIndex(-1)
{
FUNCTION("VideoConsumer::VideoConsumer\n");
AddNodeKind(B_PHYSICAL_OUTPUT);
SetEventLatency(0);
for (uint32 i = 0; i < kBufferCount; i++) {
fBitmap[i] = NULL;
fBufferMap[i] = 0;
}
SetPriority(B_DISPLAY_PRIORITY);
}
VideoConsumer::~VideoConsumer()
{
Quit();
DeleteBuffers();
}
BMediaAddOn*
VideoConsumer::AddOn(long* cookie) const
{
FUNCTION("VideoConsumer::AddOn\n");
// do the right thing if we're ever used with an add-on
*cookie = fInternalID;
return fAddOn;
}
// This implementation is required to get around a bug in
// the ppc compiler.
void
VideoConsumer::Start(bigtime_t performance_time)
{
BMediaEventLooper::Start(performance_time);
}
void
VideoConsumer::Stop(bigtime_t performance_time, bool immediate)
{
BMediaEventLooper::Stop(performance_time, immediate);
}
void
VideoConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
{
BMediaEventLooper::Seek(media_time, performance_time);
}
void
VideoConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
{
BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
}
void
VideoConsumer::NodeRegistered()
{
FUNCTION("VideoConsumer::NodeRegistered\n");
fIn.destination.port = ControlPort();
fIn.destination.id = 0;
fIn.source = media_source::null;
fIn.format.type = B_MEDIA_RAW_VIDEO;
// wild cards yet
fIn.format.u.raw_video = media_raw_video_format::wildcard;
fIn.format.u.raw_video.interlace = 1;
fIn.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
fIn.format.u.raw_video.display.bytes_per_row = 0;
fIn.format.u.raw_video.display.line_width = 0;
fIn.format.u.raw_video.display.line_count = 0;
Run();
}
status_t
VideoConsumer::RequestCompleted(const media_request_info& info)
{
FUNCTION("VideoConsumer::RequestCompleted\n");
switch(info.what) {
case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
if (info.status != B_OK)
ERROR("VideoConsumer::RequestCompleted: Not using our "
"buffers!\n");
break;
default:
break;
}
return B_OK;
}
status_t
VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
{
return B_OK;
}
void
VideoConsumer::BufferReceived(BBuffer* buffer)
{
LOOP("VideoConsumer::Buffer #%ld received\n", buffer->ID());
if (RunState() == B_STOPPED) {
buffer->Recycle();
return;
}
media_timed_event event(buffer->Header()->start_time,
BTimedEventQueue::B_HANDLE_BUFFER, buffer,
BTimedEventQueue::B_RECYCLE_BUFFER);
EventQueue()->AddEvent(event);
}
void
VideoConsumer::ProducerDataStatus(const media_destination& forWhom,
int32 status, bigtime_t atMediaTime)
{
FUNCTION("VideoConsumer::ProducerDataStatus\n");
if (forWhom != fIn.destination)
return;
}
status_t
VideoConsumer::CreateBuffers(const media_format& format)
{
FUNCTION("VideoConsumer::CreateBuffers\n");
// delete any old buffers
DeleteBuffers();
status_t status = B_OK;
// create a buffer group
uint32 width = format.u.raw_video.display.line_width;
uint32 height = format.u.raw_video.display.line_count;
color_space colorSpace = format.u.raw_video.display.format;
PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorSpace);
fBuffers = new BBufferGroup();
status = fBuffers->InitCheck();
if (B_OK != status) {
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
return status;
}
// and attach the bitmaps to the buffer group
BRect bounds(0, 0, width - 1, height - 1);
for (uint32 i = 0; i < kBufferCount; i++) {
// figure out the bitmap creation flags
uint32 bitmapFlags = 0;
if (colorSpace == B_YCbCr422 || colorSpace == B_YCbCr444) {
// try to use hardware overlay
bitmapFlags |= B_BITMAP_WILL_OVERLAY;
if (i == 0)
bitmapFlags |= B_BITMAP_RESERVE_OVERLAY_CHANNEL;
} else
bitmapFlags = B_BITMAP_IS_LOCKED;
fBitmap[i] = new BBitmap(bounds, bitmapFlags, colorSpace);
status = fBitmap[i]->InitCheck();
if (status >= B_OK) {
buffer_clone_info info;
uint8* bits = (uint8*)fBitmap[i]->Bits();
info.area = area_for(bits);
area_info bitmapAreaInfo;
status = get_area_info(info.area, &bitmapAreaInfo);
if (status != B_OK) {
fprintf(stderr, "VideoConsumer::CreateBuffers() - "
"get_area_info(): %s\n", strerror(status));
return status;
}
//printf("area info for bitmap %ld (%p):\n", i, fBitmap[i]->Bits());
//printf(" area: %ld\n", bitmapAreaInfo.area);
//printf(" size: %ld\n", bitmapAreaInfo.size);
//printf(" lock: %ld\n", bitmapAreaInfo.lock);
//printf(" protection: %ld\n", bitmapAreaInfo.protection);
//printf(" ram size: %ld\n", bitmapAreaInfo.ram_size);
//printf(" copy_count: %ld\n", bitmapAreaInfo.copy_count);
//printf(" out_count: %ld\n", bitmapAreaInfo.out_count);
//printf(" address: %p\n", bitmapAreaInfo.address);
info.offset = bits - (uint8*)bitmapAreaInfo.address;
info.size = (size_t)fBitmap[i]->BitsLength();
info.flags = 0;
info.buffer = 0;
BBuffer *buffer = NULL;
if ((status = fBuffers->AddBuffer(info, &buffer)) != B_OK) {
ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
return status;
} else
PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
} else {
ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING BUFFER: %s\n", strerror(status));
return status;
}
}
BBuffer** buffList = new BBuffer*[kBufferCount];
for (uint32 i = 0; i < kBufferCount; i++)
buffList[i] = 0;
if ((status = fBuffers->GetBufferList(kBufferCount, buffList)) == B_OK) {
for (uint32 i = 0; i < kBufferCount; i++) {
if (buffList[i] != NULL) {
fBufferMap[i] = (uint32)buffList[i];
PROGRESS(" i = %lu buffer = %08lx\n", i, fBufferMap[i]);
} else {
ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
return B_ERROR;
}
}
} else
ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
return status;
}
void
VideoConsumer::DeleteBuffers()
{
FUNCTION("VideoConsumer::DeleteBuffers\n");
if (fBuffers) {
fTargetLock.Lock();
if (fTargetBufferIndex >= 0) {
if (fTarget)
fTarget->SetBitmap(NULL);
fTargetBufferIndex = -1;
}
fTargetLock.Unlock();
delete fBuffers;
fBuffers = NULL;
for (uint32 i = 0; i < kBufferCount; i++) {
snooze(20000);
delete fBitmap[i];
fBitmap[i] = NULL;
}
}
FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
}
void
VideoConsumer::SetTarget(VideoTarget* target)
{
fTargetLock.Lock();
if (fTarget)
fTarget->SetBitmap(NULL);
fTarget = target;
if (fTarget && fTargetBufferIndex >= 0)
fTarget->SetBitmap(fBitmap[fTargetBufferIndex]);
fTargetLock.Unlock();
}
status_t
VideoConsumer::Connected(const media_source& producer,
const media_destination& where, const media_format& format,
media_input* outInput)
{
FUNCTION("VideoConsumer::Connected\n");
fIn.source = producer;
fIn.format = format;
fIn.node = Node();
sprintf(fIn.name, "Video Consumer");
*outInput = fIn;
uint32 user_data = 0;
int32 change_tag = 1;
if (CreateBuffers(format) == B_OK) {
BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
fBuffers, (void *)&user_data,
&change_tag, true);
fIn.format.u.raw_video.display.bytes_per_row = fBitmap[0]->BytesPerRow();
} else {
ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
return B_ERROR;
}
*outInput = fIn;
// bytes per row might have changed
fConnectionActive = true;
FUNCTION("VideoConsumer::Connected - EXIT\n");
return B_OK;
}
void
VideoConsumer::Disconnected(const media_source& producer,
const media_destination& where)
{
FUNCTION("VideoConsumer::Disconnected\n");
if (where != fIn.destination || producer != fIn.source)
return;
// reclaim our buffers
int32 changeTag = 0;
BBufferConsumer::SetOutputBuffersFor(producer, fDestination, NULL,
NULL, &changeTag, false);
if (fOurBuffers) {
status_t reclaimError = fBuffers->ReclaimAllBuffers();
if (reclaimError != B_OK) {
fprintf(stderr, "VideoConsumer::Disconnected() - Failed to "
"reclaim our buffers: %s\n", strerror(reclaimError));
}
}
// disconnect the connection
fIn.source = media_source::null;
fConnectionActive = false;
// Unset the target's bitmap. Just to be safe -- since it is usually
// done when the stop event arrives, but someone may disonnect
// without stopping us before.
fTargetLock.Lock();
if (fTargetBufferIndex >= 0) {
if (fTarget)
fTarget->SetBitmap(NULL);
if (fOurBuffers)
((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
fTargetBufferIndex = -1;
}
fTargetLock.Unlock();
}
status_t
VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
{
FUNCTION("VideoConsumer::AcceptFormat\n");
if (dest != fIn.destination) {
ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
return B_MEDIA_BAD_DESTINATION;
}
if (format->type == B_MEDIA_NO_TYPE)
format->type = B_MEDIA_RAW_VIDEO;
if (format->type != B_MEDIA_RAW_VIDEO) {
ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
if (format->u.raw_video.display.format != B_YCbCr444 &&
format->u.raw_video.display.format != B_YCbCr422 &&
format->u.raw_video.display.format != B_RGB32 &&
format->u.raw_video.display.format != B_RGB16 &&
format->u.raw_video.display.format != B_RGB15 &&
format->u.raw_video.display.format != B_GRAY8 &&
format->u.raw_video.display.format
!= media_raw_video_format::wildcard.display.format) {
ERROR("AcceptFormat - not a format we know about!\n");
return B_MEDIA_BAD_FORMAT;
}
char string[256];
string[0] = 0;
string_for_format(*format, string, 256);
FUNCTION("VideoConsumer::AcceptFormat: %s\n", string);
return B_OK;
}
status_t
VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
{
FUNCTION("VideoConsumer::GetNextInput\n");
// custom build a destination for this connection
// put connection number in id
if (*cookie < 1) {
fIn.node = Node();
fIn.destination.id = *cookie;
sprintf(fIn.name, "Video Consumer");
*outInput = fIn;
(*cookie)++;
return B_OK;
} else {
return B_MEDIA_BAD_DESTINATION;
}
}
void
VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
{
}
status_t
VideoConsumer::GetLatencyFor(
const media_destination &for_whom,
bigtime_t * out_latency,
media_node_id * out_timesource)
{
FUNCTION("VideoConsumer::GetLatencyFor\n");
if (for_whom != fIn.destination)
return B_MEDIA_BAD_DESTINATION;
*out_latency = fMyLatency;
*out_timesource = TimeSource()->ID();
return B_OK;
}
status_t
VideoConsumer::FormatChanged(const media_source& producer,
const media_destination& consumer, int32 fromChangeCount,
const media_format& format)
{
FUNCTION("VideoConsumer::FormatChanged\n");
if (consumer != fIn.destination)
return B_MEDIA_BAD_DESTINATION;
if (producer != fIn.source)
return B_MEDIA_BAD_SOURCE;
fIn.format = format;
return CreateBuffers(format);
}
void
VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
LOOP("VideoConsumer::HandleEvent\n");
BBuffer* buffer;
switch (event->type) {
case BTimedEventQueue::B_START:
PROGRESS("VideoConsumer::HandleEvent - START\n");
break;
case BTimedEventQueue::B_STOP:
PROGRESS("VideoConsumer::HandleEvent - STOP\n");
EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
// unset the target's bitmap
fTargetLock.Lock();
if (fTargetBufferIndex >= 0) {
if (fTarget)
fTarget->SetBitmap(NULL);
if (fOurBuffers)
((BBuffer*)fBufferMap[fTargetBufferIndex])->Recycle();
fTargetBufferIndex = -1;
}
fTargetLock.Unlock();
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
buffer = (BBuffer *) event->pointer;
if (RunState() == B_STARTED && fConnectionActive) {
// see if this is one of our buffers
uint32 index = 0;
fOurBuffers = true;
while (index < kBufferCount) {
if ((uint32)buffer == fBufferMap[index])
break;
else
index++;
}
if (index == kBufferCount) {
// no, buffers belong to consumer
fOurBuffers = false;
index = (fTargetBufferIndex + 1) % kBufferCount;
}
bool dropped = false;
bool recycle = true;
if ((RunMode() == B_OFFLINE)
// TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
// not do the trick. I made the VideoConsumer check the performance
// time of the buffer and if it is 0, it plays it regardless.
|| (buffer->Header()->start_time == 2 * fMyLatency + SchedulingLatency())
|| (/*(TimeSource()->Now()
> (buffer->Header()->start_time - JITTER)) &&*/
(TimeSource()->Now() < (buffer->Header()->start_time
+ JITTER)))) {
if (!fOurBuffers) {
memcpy(fBitmap[index]->Bits(), buffer->Data(),
fBitmap[index]->BitsLength());
}
fTargetLock.Lock();
if (fTarget) {
fTarget->SetBitmap(fBitmap[index]);
if (fOurBuffers) {
// recycle the previous but not the current buffer
if (fTargetBufferIndex >= 0) {
((BBuffer*)fBufferMap[fTargetBufferIndex])
->Recycle();
}
recycle = false;
}
fTargetBufferIndex = index;
}
fTargetLock.Unlock();
} else {
dropped = true;
PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n"
" start_time: %lld, current: %lld, latency: %lld\n",
buffer->Header()->start_time, TimeSource()->Now(),
SchedulingLatency());
}
if (dropped) {
if (fManager->LockWithTimeout(10000) == B_OK) {
fManager->FrameDropped();
fManager->Unlock();
}
}
if (recycle)
buffer->Recycle();
} else {
TRACE("RunState() != B_STARTED\n");
buffer->Recycle();
}
break;
default:
ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
break;
}
}
@@ -0,0 +1,124 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef VIDEO_CONSUMER_H
#define VIDEO_CONSUMER_H
#include <BufferConsumer.h>
#include <Locker.h>
#include <MediaEventLooper.h>
class BBitmap;
class NodeManager;
class VideoTarget;
// TODO: The buffer count should depend on the latency!
static const unsigned int kBufferCount = 4;
class VideoConsumer : public BMediaEventLooper, public BBufferConsumer {
public:
VideoConsumer(
const char* name,
BMediaAddOn* addon,
const uint32 internal_id,
NodeManager* manager,
VideoTarget* target);
~VideoConsumer();
// BMediaNode interface
public:
virtual BMediaAddOn* AddOn(long* cookie) const;
protected:
virtual void Start(bigtime_t performanceTime);
virtual void Stop(bigtime_t performanceTime,
bool immediate);
virtual void Seek(bigtime_t mediaTime,
bigtime_t performanceTime);
virtual void TimeWarp(bigtime_t atRealTime,
bigtime_t toPerformanceTime);
virtual void NodeRegistered();
virtual status_t RequestCompleted(
const media_request_info& info);
virtual status_t HandleMessage(int32 message, const void* data,
size_t size);
// BMediaEventLooper interface
protected:
virtual void HandleEvent(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent);
// BBufferConsumer interface
public:
virtual status_t AcceptFormat(const media_destination& dest,
media_format* format);
virtual status_t GetNextInput(int32* cookie,
media_input* _input);
virtual void DisposeInputCookie(int32 cookie);
protected:
virtual void BufferReceived(BBuffer* buffer);
private:
virtual void ProducerDataStatus(
const media_destination& forWhom,
int32 status,
bigtime_t atMediaTime);
virtual status_t GetLatencyFor(
const media_destination& forWhom,
bigtime_t* outLatency,
media_node_id* outId);
virtual status_t Connected(const media_source& producer,
const media_destination& where,
const media_format& withFormat,
media_input* outInput);
virtual void Disconnected(const media_source& producer,
const media_destination& where);
virtual status_t FormatChanged(const media_source& producer,
const media_destination& consumer,
int32 from_change_count,
const media_format& format);
// VideoConsumer
public:
status_t CreateBuffers(
const media_format& withFormat);
void DeleteBuffers();
void SetTarget(VideoTarget* target);
private:
uint32 fInternalID;
BMediaAddOn* fAddOn;
bool fConnectionActive;
media_input fIn;
media_destination fDestination;
bigtime_t fMyLatency;
BBitmap* fBitmap[kBufferCount];
bool fOurBuffers;
BBufferGroup* fBuffers;
uint32 fBufferMap[kBufferCount];
NodeManager* fManager;
BLocker fTargetLock; // locks the following variable
VideoTarget* volatile fTarget;
int32 fTargetBufferIndex;
};
#endif // VIDEO_CONSUMER_H
@@ -0,0 +1,851 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "VideoProducer.h"
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <TimeSource.h>
#include "NodeManager.h"
#include "VideoSupplier.h"
// debugging
//#define TRACE_VIDEO_PRODUCER
#ifdef TRACE_VIDEO_PRODUCER
# define TRACE(x...) printf("VideoProducer::"); printf(x)
# define FUNCTION(x...) TRACE(x)
# define ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
#else
# define TRACE(x...)
# define FUNCTION(x...)
# define ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
#endif
#define BUFFER_COUNT 3
#define TOUCH(x) ((void)(x))
VideoProducer::VideoProducer(BMediaAddOn* addon, const char* name,
int32 internalId, NodeManager* manager, VideoSupplier* supplier)
: BMediaNode(name),
BMediaEventLooper(),
BBufferProducer(B_MEDIA_RAW_VIDEO),
fInitStatus(B_NO_INIT),
fInternalID(internalId),
fAddOn(addon),
fBufferGroup(NULL),
fUsedBufferGroup(NULL),
fThread(-1),
fFrameSync(-1),
fRunning(false),
fConnected(false),
fEnabled(false),
fManager(manager),
fSupplier(supplier)
{
fOutput.destination = media_destination::null;
fInitStatus = B_OK;
}
VideoProducer::~VideoProducer()
{
if (fInitStatus == B_OK) {
// Clean up after ourselves, in case the application didn't make us
// do so.
if (fConnected)
Disconnect(fOutput.source, fOutput.destination);
if (fRunning)
_HandleStop();
}
Quit();
}
port_id
VideoProducer::ControlPort() const
{
return BMediaNode::ControlPort();
}
BMediaAddOn*
VideoProducer::AddOn(int32* _internalId) const
{
if (_internalId)
*_internalId = fInternalID;
return fAddOn;
}
status_t
VideoProducer::HandleMessage(int32 message, const void* data, size_t size)
{
return B_ERROR;
}
void
VideoProducer::SetTimeSource(BTimeSource* timeSource)
{
// Tell frame generation thread to recalculate delay value
release_sem(fFrameSync);
}
status_t
VideoProducer::RequestCompleted(const media_request_info& info)
{
return BMediaNode::RequestCompleted(info);
}
void
VideoProducer::NodeRegistered()
{
if (fInitStatus != B_OK) {
ReportError(B_NODE_IN_DISTRESS);
return;
}
fOutput.node = Node();
fOutput.source.port = ControlPort();
fOutput.source.id = 0;
fOutput.destination = media_destination::null;
strcpy(fOutput.name, Name());
// fill with wild cards at this point in time
fOutput.format.type = B_MEDIA_RAW_VIDEO;
fOutput.format.u.raw_video = media_raw_video_format::wildcard;
fOutput.format.u.raw_video.interlace = 1;
fOutput.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
fOutput.format.u.raw_video.display.bytes_per_row = 0;
fOutput.format.u.raw_video.display.line_width = 0;
fOutput.format.u.raw_video.display.line_count = 0;
// start the BMediaEventLooper control loop running
Run();
}
void
VideoProducer::Start(bigtime_t performanceTime)
{
// notify the manager in case we were started from the outside world
// fManager->StartPlaying();
BMediaEventLooper::Start(performanceTime);
}
void
VideoProducer::Stop(bigtime_t performanceTime, bool immediate)
{
// notify the manager in case we were stopped from the outside world
// fManager->StopPlaying();
BMediaEventLooper::Stop(performanceTime, immediate);
}
void
VideoProducer::Seek(bigtime_t media_time, bigtime_t performanceTime)
{
BMediaEventLooper::Seek(media_time, performanceTime);
}
void
VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
{
BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
}
status_t
VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
{
return BMediaEventLooper::AddTimer(at_performance_time, cookie);
}
void
VideoProducer::SetRunMode(run_mode mode)
{
printf("VideoProducer::SetRunMode(%d)\n", mode);
TRACE("SetRunMode(%d)\n", mode);
BMediaEventLooper::SetRunMode(mode);
}
void
VideoProducer::HandleEvent(const media_timed_event* event,
bigtime_t lateness, bool realTimeEvent)
{
TOUCH(lateness); TOUCH(realTimeEvent);
switch(event->type) {
case BTimedEventQueue::B_START:
_HandleStart(event->event_time);
break;
case BTimedEventQueue::B_STOP:
_HandleStop();
break;
case BTimedEventQueue::B_WARP:
_HandleTimeWarp(event->bigdata);
break;
case BTimedEventQueue::B_SEEK:
_HandleSeek(event->bigdata);
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
case BTimedEventQueue::B_DATA_STATUS:
case BTimedEventQueue::B_PARAMETER:
default:
TRACE("HandleEvent: Unhandled event -- %lx\n", event->type);
break;
}
}
void
VideoProducer::CleanUpEvent(const media_timed_event *event)
{
BMediaEventLooper::CleanUpEvent(event);
}
bigtime_t
VideoProducer::OfflineTime()
{
return BMediaEventLooper::OfflineTime();
}
void
VideoProducer::ControlLoop()
{
BMediaEventLooper::ControlLoop();
}
status_t
VideoProducer::DeleteHook(BMediaNode* node)
{
return BMediaEventLooper::DeleteHook(node);
}
status_t
VideoProducer::FormatSuggestionRequested(media_type type, int32 quality,
media_format* _format)
{
FUNCTION("FormatSuggestionRequested\n");
if (type != B_MEDIA_ENCODED_VIDEO)
return B_MEDIA_BAD_FORMAT;
TOUCH(quality);
*_format = fOutput.format;
return B_OK;
}
status_t
VideoProducer::FormatProposal(const media_source& output, media_format* format)
{
#ifdef TRACE_VIDEO_PRODUCER
char string[256];
string_for_format(*format, string, 256);
FUNCTION("FormatProposal(%s)\n", string);
#endif
if (!format)
return B_BAD_VALUE;
if (output != fOutput.source)
return B_MEDIA_BAD_SOURCE;
status_t ret = format_is_compatible(*format, fOutput.format) ?
B_OK : B_MEDIA_BAD_FORMAT;
if (ret != B_OK)
ERROR("FormatProposal() error\n");
// change any wild cards to specific values
return ret;
}
status_t
VideoProducer::FormatChangeRequested(const media_source& source,
const media_destination& destination, media_format* ioFormat,
int32 *_deprecated_)
{
TOUCH(destination); TOUCH(ioFormat); TOUCH(_deprecated_);
if (source != fOutput.source)
return B_MEDIA_BAD_SOURCE;
return B_ERROR;
}
status_t
VideoProducer::GetNextOutput(int32* cookie, media_output* outOutput)
{
if (!outOutput)
return B_BAD_VALUE;
if ((*cookie) != 0)
return B_BAD_INDEX;
*outOutput = fOutput;
(*cookie)++;
return B_OK;
}
status_t
VideoProducer::DisposeOutputCookie(int32 cookie)
{
TOUCH(cookie);
return B_OK;
}
status_t
VideoProducer::SetBufferGroup(const media_source& forSource,
BBufferGroup *group)
{
if (forSource != fOutput.source)
return B_MEDIA_BAD_SOURCE;
TRACE("VideoProducer::SetBufferGroup() - using buffer group of "
"consumer.\n");
fUsedBufferGroup = group;
return B_OK;
}
status_t
VideoProducer::VideoClippingChanged(const media_source& forSource,
int16 numShorts, int16* clipData, const media_video_display_info& display,
int32* _deprecated_)
{
TOUCH(forSource); TOUCH(numShorts); TOUCH(clipData);
TOUCH(display); TOUCH(_deprecated_);
return B_ERROR;
}
status_t
VideoProducer::GetLatency(bigtime_t* _latency)
{
if (!_latency)
return B_BAD_VALUE;
*_latency = EventLatency() + SchedulingLatency();
return B_OK;
}
status_t
VideoProducer::PrepareToConnect(const media_source& source,
const media_destination& destination, media_format* format,
media_source* outSource, char* outName)
{
FUNCTION("PrepareToConnect() %ldx%ld\n",
format->u.raw_video.display.line_width,
format->u.raw_video.display.line_count);
if (fConnected) {
ERROR("PrepareToConnect() - already connected!\n");
return B_MEDIA_ALREADY_CONNECTED;
}
if (source != fOutput.source)
return B_MEDIA_BAD_SOURCE;
if (fOutput.destination != media_destination::null) {
ERROR("PrepareToConnect() - destination != null.\n");
return B_MEDIA_ALREADY_CONNECTED;
}
// The format parameter comes in with the suggested format, and may be
// specialized as desired by the node
if (!format_is_compatible(*format, fOutput.format)) {
ERROR("PrepareToConnect() - incompatible format.\n");
*format = fOutput.format;
return B_MEDIA_BAD_FORMAT;
}
if (format->u.raw_video.display.line_width == 0)
format->u.raw_video.display.line_width = 384;
if (format->u.raw_video.display.line_count == 0)
format->u.raw_video.display.line_count = 288;
if (format->u.raw_video.field_rate == 0)
format->u.raw_video.field_rate = 25.0;
if (format->u.raw_video.display.bytes_per_row == 0)
format->u.raw_video.display.bytes_per_row = format->u.raw_video.display.line_width * 4;
*outSource = fOutput.source;
strcpy(outName, fOutput.name);
return B_OK;
}
#define NODE_LATENCY 20000
void
VideoProducer::Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format,
char* _name)
{
FUNCTION("Connect() %ldx%ld\n",
format.u.raw_video.display.line_width,
format.u.raw_video.display.line_count);
if (fConnected) {
ERROR("Connect() - already connected.\n");
return;
}
if (source != fOutput.source) {
ERROR("Connect() - wrong source.\n");
return;
}
if (error < B_OK) {
ERROR("Connect() - consumer error: %s\n", strerror(error));
return;
}
if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) {
ERROR("Connect() - format mismatch.\n");
return;
}
fOutput.destination = destination;
strcpy(_name, fOutput.name);
if (fOutput.format.u.raw_video.field_rate != 0.0f) {
fPerformanceTimeBase = fPerformanceTimeBase
+ (bigtime_t)((fFrame - fFrameBase)
* 1000000 / fOutput.format.u.raw_video.field_rate);
fFrameBase = fFrame;
}
fConnectedFormat = format.u.raw_video;
if (fConnectedFormat.display.bytes_per_row == 0) {
ERROR("Connect() - connected format still has BPR wildcard!\n");
fConnectedFormat.display.bytes_per_row
= 4 * fConnectedFormat.display.line_width;
}
// get the latency
bigtime_t latency = 0;
media_node_id tsID = 0;
FindLatencyFor(fOutput.destination, &latency, &tsID);
SetEventLatency(latency + NODE_LATENCY);
// Create the buffer group
if (!fUsedBufferGroup) {
fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row
* fConnectedFormat.display.line_count, BUFFER_COUNT);
status_t err = fBufferGroup->InitCheck();
if (err < B_OK) {
delete fBufferGroup;
fBufferGroup = NULL;
ERROR("Connect() - buffer group error: %s\n", strerror(err));
return;
}
fUsedBufferGroup = fBufferGroup;
}
fConnected = true;
fEnabled = true;
// Tell frame generation thread to recalculate delay value
release_sem(fFrameSync);
}
void
VideoProducer::Disconnect(const media_source& source,
const media_destination& destination)
{
FUNCTION("Disconnect()\n");
if (!fConnected) {
ERROR("Disconnect() - Not connected\n");
return;
}
if ((source != fOutput.source) || (destination != fOutput.destination)) {
ERROR("Disconnect() - Bad source and/or destination\n");
return;
}
fEnabled = false;
fOutput.destination = media_destination::null;
if (fLock.Lock()) {
// Always delete the buffer group, even if it is not ours.
// (See BeBook::SetBufferGroup()).
delete fUsedBufferGroup;
fUsedBufferGroup = NULL;
fBufferGroup = NULL;
fLock.Unlock();
}
fConnected = false;
TRACE("Disconnect() done\n");
}
void
VideoProducer::LateNoticeReceived(const media_source &source,
bigtime_t how_much, bigtime_t performanceTime)
{
TOUCH(source); TOUCH(how_much); TOUCH(performanceTime);
TRACE("Late!!!\n");
}
void
VideoProducer::EnableOutput(const media_source& source, bool enabled,
int32* _deprecated_)
{
TOUCH(_deprecated_);
if (source != fOutput.source)
return;
fEnabled = enabled;
}
status_t
VideoProducer::SetPlayRate(int32 numer, int32 denom)
{
TOUCH(numer); TOUCH(denom);
return B_ERROR;
}
void
VideoProducer::AdditionalBufferRequested(const media_source& source,
media_buffer_id prevBuffer, bigtime_t prevTime,
const media_seek_tag* prevTag)
{
TOUCH(source); TOUCH(prevBuffer); TOUCH(prevTime); TOUCH(prevTag);
}
void
VideoProducer::LatencyChanged(const media_source& source,
const media_destination& destination,
bigtime_t newLatency, uint32 flags)
{
TOUCH(source); TOUCH(destination); TOUCH(newLatency); TOUCH(flags);
TRACE("Latency changed!\n");
}
// #pragma mark -
void
VideoProducer::_HandleStart(bigtime_t performanceTime)
{
// Start producing frames, even if the output hasn't been connected yet.
TRACE("_HandleStart(%Ld)\n", performanceTime);
if (fRunning) {
TRACE("_HandleStart: Node already started\n");
return;
}
fFrame = 0;
fFrameBase = 0;
fPerformanceTimeBase = performanceTime;
fFrameSync = create_sem(0, "frame synchronization");
if (fFrameSync < B_OK)
return;
fThread = spawn_thread(_FrameGeneratorThreadEntry, "frame generator",
B_NORMAL_PRIORITY, this);
if (fThread < B_OK) {
delete_sem(fFrameSync);
return;
}
resume_thread(fThread);
fRunning = true;
return;
}
void
VideoProducer::_HandleStop()
{
TRACE("_HandleStop()\n");
if (!fRunning) {
TRACE("_HandleStop: Node isn't running\n");
return;
}
delete_sem(fFrameSync);
wait_for_thread(fThread, &fThread);
fRunning = false;
}
void
VideoProducer::_HandleTimeWarp(bigtime_t performanceTime)
{
fPerformanceTimeBase = performanceTime;
fFrameBase = fFrame;
// Tell frame generation thread to recalculate delay value
release_sem(fFrameSync);
}
void
VideoProducer::_HandleSeek(bigtime_t performanceTime)
{
fPerformanceTimeBase = performanceTime;
fFrameBase = fFrame;
// Tell frame generation thread to recalculate delay value
release_sem(fFrameSync);
}
int32
VideoProducer::_FrameGeneratorThreadEntry(void* data)
{
return ((VideoProducer*)data)->_FrameGeneratorThread();
}
int32
VideoProducer::_FrameGeneratorThread()
{
bool forceSendingBuffer = true;
bigtime_t lastFrameSentAt = 0;
int64 lastPlaylistFrame = 0;
bool running = true;
while (running) {
TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame);
// lock the node manager
status_t err = fManager->LockWithTimeout(10000);
bool ignoreEvent = false;
// Data to be retrieved from the node manager.
bigtime_t performanceTime = 0;
bigtime_t nextPerformanceTime = 0;
bigtime_t waitUntil = 0;
bigtime_t nextWaitUntil = 0;
bigtime_t maxRenderTime = 0;
int32 playingDirection = 0;
int32 playingMode = 0;
int64 playlistFrame = 0;
switch (err) {
case B_OK: {
TRACE("_FrameGeneratorThread: node manager successfully "
"locked\n");
// get the times for the current and the next frame
performanceTime = fManager->TimeForFrame(fFrame);
nextPerformanceTime = fManager->TimeForFrame(fFrame + 1);
maxRenderTime = min_c(bigtime_t(33334 * 0.9),
max_c(fSupplier->ProcessingLatency(), maxRenderTime));
playingMode = fManager->PlayModeAtFrame(fFrame);
waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
+ performanceTime, 0) - maxRenderTime;
nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
+ nextPerformanceTime, 0) - maxRenderTime;
// get playing direction and playlist frame for the current
// frame
bool newPlayingState;
playlistFrame = fManager->PlaylistFrameAtFrame(fFrame,
playingDirection, newPlayingState);
TRACE("_FrameGeneratorThread: performance time: %Ld, "
"playlist frame: %lld\n", performanceTime, playlistFrame);
forceSendingBuffer |= newPlayingState;
if (lastPlaylistFrame != playlistFrame) {
forceSendingBuffer = true;
lastPlaylistFrame = playlistFrame;
}
fManager->SetCurrentVideoTime(nextPerformanceTime);
fManager->Unlock();
break;
}
case B_TIMED_OUT:
TRACE("_FrameGeneratorThread: Couldn't lock the node "
"manager.\n");
ignoreEvent = true;
waitUntil = system_time() - 1;
break;
default:
ERROR("_FrameGeneratorThread: Couldn't lock the node manager. "
"Terminating video producer frame generator thread.\n");
TRACE("_FrameGeneratorThread: frame generator thread done.\n");
// do not access any member variables, since this could
// also me the Node has been deleted
return B_OK;
}
TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil);
// wait until...
err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil);
// The only acceptable responses are B_OK and B_TIMED_OUT. Everything
// else means the thread should quit. Deleting the semaphore, as in
// VideoProducer::_HandleStop(), will trigger this behavior.
switch (err) {
case B_OK:
TRACE("_FrameGeneratorThread: going back to sleep.\n");
break;
case B_TIMED_OUT:
TRACE("_FrameGeneratorThread: timed out => event\n");
// Catch the cases in which the node manager could not be
// locked and we therefore have no valid data to work with,
// or the producer is not running or enabled.
if (ignoreEvent || !fRunning || !fEnabled) {
TRACE("_FrameGeneratorThread: ignore event\n");
// nothing to do
} else if (nextWaitUntil < system_time()) {
// Drop frame if it's at least a frame late.
//printf("VideoProducer: dropped frame (%ld)\n", fFrame);
if (fManager->LockWithTimeout(10000) == B_OK) {
fManager->FrameDropped();
fManager->Unlock();
}
// next frame
fFrame++;
} else if (playingDirection != 0 || forceSendingBuffer) {
// Send buffers only, if playing, the node is running and
// the output has been enabled
TRACE("_FrameGeneratorThread: produce frame\n");
BAutolock _(fLock);
// Fetch a buffer from the buffer group
BBuffer *buffer = fUsedBufferGroup->RequestBuffer(
fConnectedFormat.display.bytes_per_row
* fConnectedFormat.display.line_count, 0LL);
if (buffer) {
// Fill out the details about this buffer.
media_header *h = buffer->Header();
h->type = B_MEDIA_RAW_VIDEO;
h->time_source = TimeSource()->ID();
h->size_used = fConnectedFormat.display.bytes_per_row
* fConnectedFormat.display.line_count;
// For a buffer originating from a device, you might
// want to calculate this based on the
// PerformanceTimeFor the time your buffer arrived at
// the hardware (plus any applicable adjustments).
h->start_time = fPerformanceTimeBase + performanceTime;
// TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
// not do the trick. I made the VideoConsumer check the performance
// time of the buffer and if it is 0, it plays it regardless.
if (playingMode < 0) {
h->start_time = 0;
}
h->file_pos = 0;
h->orig_size = 0;
h->data_offset = 0;
h->u.raw_video.field_gamma = 1.0;
h->u.raw_video.field_sequence = fFrame;
h->u.raw_video.field_number = 0;
h->u.raw_video.pulldown_number = 0;
h->u.raw_video.first_active_line = 1;
h->u.raw_video.line_count
= fConnectedFormat.display.line_count;
// Fill in a frame
media_format mf;
mf.type = B_MEDIA_RAW_VIDEO;
mf.u.raw_video = fConnectedFormat;
TRACE("_FrameGeneratorThread: frame: %Ld, "
"playlistFrame: %Ld\n", fFrame, playlistFrame);
bool forceOrWasCached = forceSendingBuffer;
if (fManager->LockWithTimeout(5000) == B_OK) {
// we need to lock the manager, or our
// fSupplier might work on bad data
err = fSupplier->FillBuffer(playlistFrame,
buffer->Data(), &mf, forceOrWasCached);
fManager->Unlock();
} else {
err = B_ERROR;
}
// clean the buffer if something went wrong
if (err != B_OK) {
memset(buffer->Data(), 0, h->size_used);
err = B_OK;
}
// Send the buffer on down to the consumer
if (SendBuffer(buffer, fOutput.destination) < B_OK) {
ERROR("_FrameGeneratorThread: Error "
"sending buffer\n");
// If there is a problem sending the buffer,
// or if we don't send the buffer because its
// contents are the same as the last one,
// return it to its buffer group.
buffer->Recycle();
// we tell the supplier to delete
// its caches if there was a problem sending
// the buffer
fSupplier->DeleteCaches();
}
// Only if everything went fine we clear the flag
// that forces us to send a buffer even if not
// playing.
if (err == B_OK) {
forceSendingBuffer = false;
lastFrameSentAt = performanceTime;
}
} else {
TRACE("_FrameGeneratorThread: no buffer!\n");
// ERROR("_FrameGeneratorThread: no buffer!\n");
}
// next frame
fFrame++;
} else {
TRACE("_FrameGeneratorThread: not playing\n");
// next frame
fFrame++;
}
break;
default:
TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. "
"Error: %s\n", strerror(err));
running = false;
break;
}
}
TRACE("_FrameGeneratorThread: frame generator thread done.\n");
return B_OK;
}
@@ -0,0 +1,145 @@
/* Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef _VIDEO_PRODUCER_H
#define _VIDEO_PRODUCER_H
#include <BufferProducer.h>
#include <Controllable.h>
#include <Locker.h>
#include <MediaDefs.h>
#include <MediaEventLooper.h>
#include <MediaNode.h>
#include <OS.h>
#include <Rect.h>
class NodeManager;
class VideoSupplier;
class VideoProducer : public virtual BMediaEventLooper,
public virtual BBufferProducer {
public:
VideoProducer(BMediaAddOn* addon, const char* name,
int32 internalId, NodeManager* manager,
VideoSupplier* supplier);
virtual ~VideoProducer();
virtual status_t InitCheck() const
{ return fInitStatus; }
// BMediaNode interface
public:
virtual port_id ControlPort() const;
virtual BMediaAddOn* AddOn(int32* _internalId) const;
virtual status_t HandleMessage(int32 message, const void* data,
size_t size);
protected:
virtual void SetTimeSource(BTimeSource* timeSource);
virtual status_t RequestCompleted(const media_request_info& info);
// BMediaEventLooper interface
protected:
virtual void NodeRegistered();
virtual void Start(bigtime_t performanceTime);
virtual void Stop(bigtime_t performanceTime, bool immediate);
virtual void Seek(bigtime_t mediaTime,
bigtime_t performanceTime);
virtual void TimeWarp(bigtime_t atRealTime,
bigtime_t toPerformanceTime);
virtual status_t AddTimer(bigtime_t atPerformanceTime,
int32 cookie);
virtual void SetRunMode(run_mode mode);
virtual void HandleEvent(const media_timed_event* event,
bigtime_t lateness,
bool realTimeEvent = false);
virtual void CleanUpEvent(const media_timed_event* event);
virtual bigtime_t OfflineTime();
virtual void ControlLoop();
virtual status_t DeleteHook(BMediaNode* node);
// BBufferProducer interface
protected:
virtual status_t FormatSuggestionRequested(media_type type,
int32 quality, media_format* format);
virtual status_t FormatProposal(const media_source &output,
media_format* format);
virtual status_t FormatChangeRequested(const media_source& source,
const media_destination& destination,
media_format* ioFormat, int32* _deprecated_);
virtual status_t GetNextOutput(int32* cookie,
media_output* outOutput);
virtual status_t DisposeOutputCookie(int32 cookie);
virtual status_t SetBufferGroup(const media_source& forSource,
BBufferGroup* group);
virtual status_t VideoClippingChanged(const media_source& forSource,
int16 numShorts, int16* clipData,
const media_video_display_info& display,
int32* _deprecated_);
virtual status_t GetLatency(bigtime_t* out_latency);
virtual status_t PrepareToConnect(const media_source& what,
const media_destination& where,
media_format* format, media_source* outSource,
char* out_name);
virtual void Connect(status_t error, const media_source& source,
const media_destination& destination,
const media_format& format, char* ioName);
virtual void Disconnect(const media_source& what,
const media_destination& where);
virtual void LateNoticeReceived(const media_source& what,
bigtime_t howMuch, bigtime_t performanceTime);
virtual void EnableOutput(const media_source& what, bool enabled,
int32* _deprecated_);
virtual status_t SetPlayRate(int32 numer, int32 denom);
virtual void AdditionalBufferRequested(
const media_source& source,
media_buffer_id prevBuffer,
bigtime_t prevTime,
const media_seek_tag* prevTag);
virtual void LatencyChanged(const media_source& source,
const media_destination& destination,
bigtime_t newLatency, uint32 flags);
private:
void _HandleStart(bigtime_t performance_time);
void _HandleStop();
void _HandleTimeWarp(bigtime_t performance_time);
void _HandleSeek(bigtime_t performance_time);
static int32 _FrameGeneratorThreadEntry(void* data);
int32 _FrameGeneratorThread();
status_t fInitStatus;
int32 fInternalID;
BMediaAddOn* fAddOn;
BLocker fLock;
BBufferGroup* fBufferGroup;
BBufferGroup* fUsedBufferGroup;
thread_id fThread;
sem_id fFrameSync;
// The remaining variables should be declared volatile, but they
// are not here to improve the legibility of the sample code.
int64 fFrame;
int64 fFrameBase;
bigtime_t fPerformanceTimeBase;
media_output fOutput;
media_raw_video_format fConnectedFormat;
bool fRunning;
bool fConnected;
bool fEnabled;
NodeManager* fManager;
VideoSupplier* fSupplier;
};
#endif // VIDEO_PRODUCER_H
@@ -0,0 +1,23 @@
/*
* Copyright 2001-2008 Ingo Weinhold <ingo_weinhold@gmx.de>
* Copyright 2001-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "VideoSupplier.h"
VideoSupplier::VideoSupplier()
: fProcessingLatency(1000)
{
}
VideoSupplier::~VideoSupplier()
{
}
void
VideoSupplier::DeleteCaches()
{
}
@@ -0,0 +1,34 @@
/*
* Copyright 2001-2008 Ingo Weinhold <ingo_weinhold@gmx.de>
* Copyright 2001-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#ifndef VIDEO_SUPPLIER_H
#define VIDEO_SUPPLIER_H
#include <SupportDefs.h>
struct media_format;
class VideoSupplier {
public:
VideoSupplier();
virtual ~VideoSupplier();
virtual status_t FillBuffer(int64 startFrame, void* buffer,
const media_format* format,
bool& wasCached) = 0;
virtual void DeleteCaches();
inline bigtime_t ProcessingLatency() const
{ return fProcessingLatency; }
protected:
bigtime_t fProcessingLatency;
};
#endif // VIDEO_SUPPLIER_H
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2008 Ingo Weinhold <ingo_weinhold@gmx.de> All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include "VideoTarget.h"
VideoTarget::VideoTarget()
: fBitmapLock(),
fBitmap(NULL)
{
}
VideoTarget::~VideoTarget()
{
}
bool
VideoTarget::LockBitmap()
{
return fBitmapLock.Lock();
}
void
VideoTarget::UnlockBitmap()
{
fBitmapLock.Unlock();
}
void
VideoTarget::SetBitmap(const BBitmap* bitmap)
{
LockBitmap();
fBitmap = bitmap;
UnlockBitmap();
}
const BBitmap*
VideoTarget::GetBitmap() const
{
return fBitmap;
}
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2008 Ingo Weinhold <ingo_weinhold@gmx.de> All rights reserved.
* Distributed under the terms of the MIT license.
*/
/*! Derived classes are video consumer targets. Each time the consumer
has received a frame (that is not late and thus dropped) it calls
SetBitmap(). This method should immediately do whatever has to be done.
Until the next call to SetBitmap() the bitmap can be used -- thereafter
it is not allowed to use it any longer. Therefore the bitmap variable
is protected by a lock. Anytime it is going to be accessed, the object
must be locked.
*/
#ifndef VIDEO_TARGET_H
#define VIDEO_TARGET_H
#include <Locker.h>
class BBitmap;
class VideoTarget {
public:
VideoTarget();
virtual ~VideoTarget();
bool LockBitmap();
void UnlockBitmap();
virtual void SetBitmap(const BBitmap* bitmap);
const BBitmap* GetBitmap() const;
protected:
BLocker fBitmapLock;
const BBitmap* volatile fBitmap;
};
#endif // VIDEO_TARGET_H
@@ -1,33 +0,0 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef AUDIO_SUPPLIER_H
#define AUDIO_SUPPLIER_H
#include <MediaDefs.h>
#include <MediaFormats.h>
class AudioSupplier {
public:
AudioSupplier();
virtual ~AudioSupplier();
virtual media_format Format() const = 0;
virtual status_t GetEncodedFormat(media_format* format)
const = 0;
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
virtual status_t ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime) = 0;
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
virtual bigtime_t Position() const = 0;
virtual bigtime_t Duration() const = 0;
};
#endif // AUDIO_SUPPLIER_H
@@ -5,15 +5,16 @@
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "AudioSupplier.h"
#include "AudioTrackSupplier.h"
AudioSupplier::AudioSupplier()
AudioTrackSupplier::AudioTrackSupplier()
: AudioReader()
{
}
AudioSupplier::~AudioSupplier()
AudioTrackSupplier::~AudioTrackSupplier()
{
}
@@ -0,0 +1,28 @@
/*
* Copyright 2007-2008, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef AUDIO_TRACK_SUPPLIER_H
#define AUDIO_TRACK_SUPPLIER_H
#include <MediaDefs.h>
#include <MediaFormats.h>
#include "AudioReader.h"
class AudioTrackSupplier : public AudioReader {
public:
AudioTrackSupplier();
virtual ~AudioTrackSupplier();
virtual const media_format& Format() const = 0;
virtual status_t GetEncodedFormat(media_format* format)
const = 0;
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
virtual bigtime_t Duration() const = 0;
};
#endif // AUDIO_TRACK_SUPPLIER_H
@@ -1,64 +1,85 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "MediaTrackAudioSupplier.h"
#include <new>
#include <algobase.h>
#include <stdio.h>
#include <string.h>
#include <MediaFile.h>
#include <MediaTrack.h>
using std::nothrow;
// constructor
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* track)
: AudioSupplier()
, fMediaTrack(track)
, fPerformanceTime(0)
, fDuration(0)
//#define TRACE_AUDIO_SUPPLIER
#ifdef TRACE_AUDIO_SUPPLIER
# define TRACE(x...) printf("MediaTrackAudioSupplier::"); printf(x)
#else
# define TRACE(x...)
#endif
// #pragma mark - Buffer
struct MediaTrackAudioSupplier::Buffer {
void* data;
int64 offset;
int64 size;
bigtime_t time_stamp;
static int CompareOffset(const void* a, const void* b);
};
int
MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
{
if (!fMediaTrack) {
printf("MediaTrackAudioSupplier() - no media track\n");
return;
}
fFormat.u.raw_audio = media_multi_audio_format::wildcard;
#ifdef __HAIKU__
fFormat.u.raw_audio.format = media_multi_audio_format::B_AUDIO_FLOAT;
#endif
status_t ret = fMediaTrack->DecodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackAudioSupplier() - "
"fMediaTrack->DecodedFormat(): %s\n", strerror(ret));
return;
}
fDuration = fMediaTrack->Duration();
//
// for (bigtime_t time = 0; time < fDuration; time += 10000) {
// bigtime_t keyFrameTime = time;
// fMediaTrack->FindKeyFrameForTime(&keyFrameTime,
// B_MEDIA_SEEK_CLOSEST_BACKWARD);
// printf("audio keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
// }
const Buffer* buffer1 = *(const Buffer**)a;
const Buffer* buffer2 = *(const Buffer**)b;
int result = 0;
if (buffer1->offset < buffer2->offset)
result = -1;
else if (buffer1->offset > buffer2->offset)
result = 1;
return result;
}
// destructor
// #pragma mark - MediaTrackAudioSupplier
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack)
: AudioTrackSupplier(),
fMediaTrack(mediaTrack),
fBuffer(NULL),
fBufferOffset(0),
fBufferSize(0),
fBuffers(10),
fHasKeyFrames(false),
fCountFrames(0),
fReportSeekError(true)
{
_InitFromTrack();
}
MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
{
_FreeBuffers();
delete[] fBuffer;
}
media_format
const media_format&
MediaTrackAudioSupplier::Format() const
{
return fFormat;
return AudioReader::Format();
}
@@ -80,53 +101,530 @@ MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
}
status_t
MediaTrackAudioSupplier::ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime)
bigtime_t
MediaTrackAudioSupplier::Duration() const
{
if (!fMediaTrack)
return B_NO_INIT;
if (!buffer || !framesRead)
return B_BAD_VALUE;
return 0;
media_header mediaHeader;
status_t ret = fMediaTrack->ReadFrames(buffer, framesRead, &mediaHeader);
return fMediaTrack->Duration();
}
if (ret < B_OK) {
// further analyse the error
if (fDuration == 0 || (double)fPerformanceTime / fDuration > 0.95) {
// some codecs don't behave well, or maybe somes files are bad,
// they don't report the end of the stream correctly
// NOTE: "more than 95% of the stream" is of course pure guess,
// but it fixed the problem I had with some files
ret = B_LAST_BUFFER_ERROR;
// #pragma mark - AudioReader
// Read
status_t
MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
{
TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
frames);
TRACE(" this: %p, fOutOffset: %lld\n", this, fOutOffset);
status_t error = InitCheck();
if (error != B_OK) {
TRACE("Read() done\n");
return error;
}
// convert pos according to our offset
pos += fOutOffset;
// Fill the frames after the end of the track with silence.
if (pos + frames > fCountFrames) {
int64 size = max(0LL, fCountFrames - pos);
ReadSilence(SkipFrames(buffer, size), frames - size);
frames = size;
}
TRACE(" after eliminating the frames after the track end: %p, %lld, %lld\n",
buffer, pos, frames);
// read the cached frames
bigtime_t time = system_time();
if (frames > 0)
_ReadCachedFrames(buffer, pos, frames, time);
TRACE(" after reading from cache: %p, %lld, %lld\n", buffer, pos, frames);
// read the remaining (uncached) frames
if (frames > 0)
_ReadUncachedFrames(buffer, pos, frames, time);
TRACE("Read() done\n");
return B_OK;
}
// InitCheck
status_t
MediaTrackAudioSupplier::InitCheck() const
{
status_t error = AudioReader::InitCheck();
if (error == B_OK && (!fMediaTrack || !fBuffer))
error = B_NO_INIT;
return error;
}
// #pragma mark -
// _InitFromTrack
void
MediaTrackAudioSupplier::_InitFromTrack()
{
if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK
&& fFormat.type == B_MEDIA_RAW_AUDIO) {
#ifdef TRACE_AUDIO_SUPPLIER
char formatString[256];
string_for_format(fFormat, formatString, 256);
TRACE("MediaTrackAudioSupplier: format is: %s\n", formatString);
#endif
fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
_AllocateBuffers();
// Find out, if the track has key frames: as a heuristic we
// check, if the first and the second frame have the same backward
// key frame.
// Note: It shouldn't harm that much, if we're wrong and the
// track has key frame although we found out that it has not.
int64 keyFrame0 = 0;
int64 keyFrame1 = 1;
fMediaTrack->FindKeyFrameForFrame(&keyFrame0,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
fMediaTrack->FindKeyFrameForFrame(&keyFrame1,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
fHasKeyFrames = (keyFrame0 == keyFrame1);
// get the length of the track
fCountFrames = fMediaTrack->CountFrames();
} else
fMediaTrack = NULL;
}
// _FramesPerBuffer
int64
MediaTrackAudioSupplier::_FramesPerBuffer() const
{
int64 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
return fFormat.u.raw_audio.buffer_size / frameSize;
}
// _CopyFrames
//
// Given two buffers starting at different frame offsets, this function
// copies /frames/ frames at position /position/ from the source to the
// target buffer.
// Note that no range checking is done.
void
MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset,
void* target, int64 targetOffset,
int64 position, int64 frames) const
{
int64 sampleSize = fFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
source = (char*)source + frameSize * (position - sourceOffset);
target = (char*)target + frameSize * (position - targetOffset);
memcpy(target, source, frames * frameSize);
}
// _CopyFrames
//
// Given two buffers starting at different frame offsets, this function
// copies /frames/ frames at position /position/ from the source to the
// target buffer. This version expects a cache buffer as source.
// Note that no range checking is done.
void
MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer,
void* target, int64 targetOffset,
int64 position, int64 frames) const
{
_CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
frames);
}
// _AllocateBuffers
//
// Allocates a set of buffers.
void
MediaTrackAudioSupplier::_AllocateBuffers()
{
int32 count = 10;
_FreeBuffers();
int32 bufferSize = fFormat.u.raw_audio.buffer_size;
char* data = new (nothrow) char[bufferSize * count];
for (; count > 0; count--) {
Buffer* buffer = new (nothrow) Buffer;
if (!buffer || !fBuffers.AddItem(buffer)) {
delete buffer;
return;
}
printf("MediaTrackAudioSupplier::ReadFrame() - "
"error while reading frames: %s\n", strerror(ret));
} else {
fPerformanceTime = mediaHeader.start_time;
buffer->data = data;
data += bufferSize;
buffer->offset = 0;
buffer->size = 0;
buffer->time_stamp = 0;
}
if (performanceTime)
*performanceTime = fPerformanceTime;
return ret;
}
status_t
MediaTrackAudioSupplier::SeekToTime(bigtime_t* performanceTime)
// _FreeBuffers
//
// Frees the allocated buffers.
void
MediaTrackAudioSupplier::_FreeBuffers()
{
if (!fMediaTrack)
return B_NO_INIT;
bigtime_t _performanceTime = *performanceTime;
status_t ret = fMediaTrack->SeekToTime(performanceTime);
if (ret == B_OK) {
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
fPerformanceTime = *performanceTime;
if (fBuffers.CountItems() > 0) {
delete[] (char*)_BufferAt(0)->data;
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
delete buffer;
fBuffers.MakeEmpty();
}
return ret;
}
// _BufferAt
//
// Returns the buffer at index /index/.
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_BufferAt(int32 index) const
{
return (Buffer*)fBuffers.ItemAt(index);
}
// _FindBufferAtFrame
//
// If any buffer starts at offset /frame/, it is returned, NULL otherwise.
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const
{
Buffer* buffer = NULL;
for (int32 i = 0;
((buffer = _BufferAt(i))) && buffer->offset != frame;
i++);
return buffer;
}
// _FindUnusedBuffer
//
// Returns the first unused buffer or NULL if all buffers are used.
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUnusedBuffer() const
{
Buffer* buffer = NULL;
for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++);
return buffer;
}
// _FindUsableBuffer
//
// Returns either an unused buffer or, if all buffers are used, the least
// recently used buffer.
// In every case a buffer is returned.
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUsableBuffer() const
{
Buffer* result = _FindUnusedBuffer();
if (!result) {
// find the least recently used buffer.
result = _BufferAt(0);
for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) {
if (buffer->time_stamp < result->time_stamp)
result = buffer;
}
}
return result;
}
// _FindUsableBufferFor
//
// In case there already exists a buffer that starts at position this
// one is returned. Otherwise the function returns either an unused
// buffer or, if all buffers are used, the least recently used buffer.
// In every case a buffer is returned.
MediaTrackAudioSupplier::Buffer*
MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
{
Buffer* buffer = _FindBufferAtFrame(position);
if (!buffer)
buffer = _FindUsableBuffer();
return buffer;
}
// _GetBuffersFor
//
// Adds pointers to all buffers to the list that contain data of the
// supplied interval.
void
MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position,
int64 frames) const
{
buffers.MakeEmpty();
for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) {
// Calculate the intersecting interval and add the buffer if it is
// not empty.
int32 startFrame = max(position, buffer->offset);
int32 endFrame = min(position + frames, buffer->offset + buffer->size);
if (startFrame < endFrame)
buffers.AddItem(buffer);
}
}
// _TouchBuffer
//
// Sets a buffer's time stamp to the current system time.
void
MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
{
buffer->time_stamp = system_time();
}
// _ReadBuffer
//
// Read a buffer from the current position (which is supplied in /position/)
// into /buffer/. The buffer's time stamp is set to the current system time.
status_t
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position)
{
return _ReadBuffer(buffer, position, system_time());
}
// _ReadBuffer
//
// Read a buffer from the current position (which is supplied in /position/)
// into /buffer/. The buffer's time stamp is set to the supplied time.
status_t
MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
bigtime_t time)
{
status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
TRACE("Read(%p, %lld): %s\n", buffer->data, buffer->size, strerror(error));
buffer->offset = position;
buffer->time_stamp = time;
if (error != B_OK)
buffer->size = 0;
return error;
}
// _ReadCachedFrames
//
// Tries to read as much as possible data from the cache. The supplied
// buffer pointer as well as position and number of frames are adjusted
// accordingly. The used cache buffers are stamped with the current
// system time.
void
MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
int64& frames)
{
_ReadCachedFrames(dest, pos, frames, system_time());
}
// _ReadCachedFrames
//
// Tries to read as much as possible data from the cache. The supplied
// buffer pointer as well as position and number of frames are adjusted
// accordingly. The used cache buffers are stamped with the supplied
// time.
void
MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
int64& frames, bigtime_t time)
{
// Get a list of all cache buffers that contain data of the interval,
// and sort it.
BList buffers(10);
_GetBuffersFor(buffers, pos, frames);
buffers.SortItems(Buffer::CompareOffset);
// Step forward through the list of cache buffers and try to read as
// much data from the beginning as possible.
for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) {
if (buffer->offset <= pos && buffer->offset + buffer->size > pos) {
// read from the beginning
int64 size = min(frames, buffer->offset + buffer->size - pos);
_CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size);
pos += size;
frames -= size;
dest = SkipFrames(dest, size);
}
buffer->time_stamp = time;
}
// Step backward through the list of cache buffers and try to read as
// much data from the end as possible.
for (int32 i = buffers.CountItems() - 1;
Buffer* buffer = (Buffer*)buffers.ItemAt(i);
i++) {
if (buffer->offset < pos + frames
&& buffer->offset + buffer->size >= pos + frames) {
// read from the end
int64 size = min(frames, pos + frames - buffer->offset);
_CopyFrames(buffer->data, buffer->offset, dest, pos,
pos + frames - size, size);
frames -= size;
}
}
}
// _ReadUncachedFrames
//
// Reads /frames/ frames from /position/ into /buffer/. The frames are not
// read from the cache, but read frames are cached, if possible.
// New cache buffers are stamped with the system time.
// If an error occurs, the untouched part of the buffer is set to 0.
status_t
MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
int64 frames)
{
return _ReadUncachedFrames(buffer, position, frames, system_time());
}
// _ReadUncachedFrames
//
// Reads /frames/ frames from /position/ into /buffer/. The frames are not
// read from the cache, but read frames are cached, if possible.
// New cache buffers are stamped with the supplied time.
// If an error occurs, the untouched part of the buffer is set to 0.
status_t
MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
int64 frames, bigtime_t time)
{
status_t error = B_OK;
// seek to the position
int64 currentPos = position;
if (frames > 0) {
error = _SeekToKeyFrameBackward(currentPos);
TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
}
// read the frames
while (error == B_OK && frames > 0) {
Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
TRACE("_ReadUncachedFrames() - usable buffer found: %p\n", cacheBuffer);
error = _ReadBuffer(cacheBuffer, currentPos, time);
if (error == B_OK) {
int64 size = min(position + frames,
cacheBuffer->offset + cacheBuffer->size)
- position;
if (size > 0) {
_CopyFrames(cacheBuffer, buffer, position, position, size);
buffer = SkipFrames(buffer, size);
position += size;
frames -= size;
}
currentPos += cacheBuffer->size;
}
}
// Ensure that all frames up to the next key frame are cached.
// This avoids, that each read
if (error == B_OK) {
int64 nextKeyFrame = currentPos;
if (_FindKeyFrameForward(nextKeyFrame) == B_OK) {
while (currentPos < nextKeyFrame) {
// Check, if data at this position are cache.
// If not read it.
Buffer* cacheBuffer = _FindBufferAtFrame(currentPos);
if (!cacheBuffer || cacheBuffer->size == 0) {
cacheBuffer = _FindUsableBufferFor(currentPos);
if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK)
break;
}
if (cacheBuffer)
currentPos += cacheBuffer->size;
}
}
}
// on error fill up the buffer with silence
if (error != B_OK && frames > 0)
ReadSilence(buffer, frames);
return error;
}
// _FindKeyFrameForward
status_t
MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
{
status_t error = B_OK;
// NOTE: the keyframe version confuses the Frauenhofer MP3 decoder,
// it works fine with the non-keyframe version, so let's hope this
// is the case for all other keyframe based BeOS codecs...
// if (fHasKeyFrames) {
// error = fMediaTrack->FindKeyFrameForFrame(
// &position, B_MEDIA_SEEK_CLOSEST_FORWARD);
// } else {
int64 framesPerBuffer = _FramesPerBuffer();
position += framesPerBuffer - 1;
position = position % framesPerBuffer;
// }
return error;
}
// _FindKeyFrameBackward
status_t
MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
{
status_t error = B_OK;
if (fHasKeyFrames) {
error = fMediaTrack->FindKeyFrameForFrame(
&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
} else
position -= position % _FramesPerBuffer();
return error;
}
// _SeekToKeyFrameForward
status_t
MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
{
if (position == fMediaTrack->CurrentFrame())
return B_OK;
status_t error = B_OK;
if (fHasKeyFrames) {
#ifdef TRACE_AUDIO_SUPPLIER
int64 oldPosition = position;
#endif
error = fMediaTrack->SeekToFrame(&position,
B_MEDIA_SEEK_CLOSEST_FORWARD);
TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
"%lld -> %lld (%lld)\n", oldPosition, position,
fMediaTrack->CurrentFrame());
} else {
_FindKeyFrameForward(position);
error = fMediaTrack->SeekToFrame(&position);
}
return error;
}
// _SeekToKeyFrameBackward
status_t
MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
{
if (position == fMediaTrack->CurrentFrame())
return B_OK;
status_t error = B_OK;
if (fHasKeyFrames) {
int64 oldPosition = position;
error = fMediaTrack->FindKeyFrameForFrame(&position,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (error >= B_OK)
error = fMediaTrack->SeekToFrame(&position, 0);
if (error < B_OK) {
position = fMediaTrack->CurrentFrame();
if (fReportSeekError) {
printf(" seek to key frame backward: %lld -> %lld (%lld) "
"- %s\n", oldPosition, position,
fMediaTrack->CurrentFrame(), strerror(error));
fReportSeekError = false;
}
} else {
fReportSeekError = true;
}
} else {
_FindKeyFrameBackward(position);
error = fMediaTrack->SeekToFrame(&position);
}
return error;
}
@@ -1,41 +1,89 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MEDIA_TRACK_AUDIO_SUPPLIER_H
#define MEDIA_TRACK_AUDIO_SUPPLIER_H
#include "AudioSupplier.h"
#include <List.h>
#include "AudioTrackSupplier.h"
class BMediaTrack;
struct media_codec_info;
struct media_format;
class MediaTrackAudioSupplier : public AudioSupplier {
class MediaTrackAudioSupplier : public AudioTrackSupplier {
public:
MediaTrackAudioSupplier(BMediaTrack* track);
virtual ~MediaTrackAudioSupplier();
virtual media_format Format() const;
virtual const media_format& Format() const;
virtual status_t GetEncodedFormat(media_format* format) const;
virtual status_t GetCodecInfo(media_codec_info* info) const;
virtual bigtime_t Duration() const;
virtual status_t ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime);
virtual status_t SeekToTime(bigtime_t* performanceTime);
// AudioReader interface
// (needed to reuse the class as AudioResampler input)
virtual status_t Read(void* buffer, int64 pos, int64 frames);
virtual status_t InitCheck() const;
private:
struct Buffer;
void _InitFromTrack();
int64 _FramesPerBuffer() const;
void _CopyFrames(void* source, int64 sourceOffset,
void* target, int64 targetOffset,
int64 position, int64 frames) const;
void _CopyFrames(Buffer* buffer, void* target,
int64 targetOffset, int64 position,
int64 frames) const;
void _AllocateBuffers();
void _FreeBuffers();
Buffer* _BufferAt(int32 index) const;
Buffer* _FindBufferAtFrame(int64 frame) const;
Buffer* _FindUnusedBuffer() const;
Buffer* _FindUsableBuffer() const;
Buffer* _FindUsableBufferFor(int64 position) const;
void _GetBuffersFor(BList& buffers, int64 position,
int64 frames) const;
void _TouchBuffer(Buffer* buffer);
status_t _ReadBuffer(Buffer* buffer, int64 position);
status_t _ReadBuffer(Buffer* buffer, int64 position,
bigtime_t time);
void _ReadCachedFrames(void*& buffer,
int64& position, int64& frames);
void _ReadCachedFrames(void*& buffer,
int64& position, int64& frames,
bigtime_t time);
status_t _ReadUncachedFrames(void* buffer,
int64 position, int64 frames);
status_t _ReadUncachedFrames(void* buffer,
int64 position, int64 frames,
bigtime_t time);
status_t _FindKeyFrameForward(int64& position);
status_t _FindKeyFrameBackward(int64& position);
status_t _SeekToKeyFrameForward(int64& position);
status_t _SeekToKeyFrameBackward(int64& position);
virtual bigtime_t Position() const
{ return fPerformanceTime; }
virtual bigtime_t Duration() const
{ return fDuration; }
private:
BMediaTrack* fMediaTrack;
media_format fFormat;
bigtime_t fPerformanceTime;
bigtime_t fDuration;
char* fBuffer;
int64 fBufferOffset;
int64 fBufferSize;
BList fBuffers;
bool fHasKeyFrames;
int64 fCountFrames;
bool fReportSeekError;
};
#endif // MEDIA_TRACK_AUDIO_SUPPLIER_H
#endif // MEDIA_TRACK_AUDIO_SUPPLIER_H
@@ -29,73 +29,19 @@ static const char* string_for_color_space(color_space format);
// constructor
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
color_space format)
: VideoSupplier()
: VideoTrackSupplier()
, fVideoTrack(track)
, fPerformanceTime(0)
, fDuration(0)
, fCurrentFrame(0)
{
if (!fVideoTrack) {
printf("MediaTrackVideoSupplier() - no video track\n");
return;
}
// get the encoded format
memset(&fFormat, 0, sizeof(media_format));
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::InitCheck() - "
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
return;
}
// get ouput video frame size
uint32 width = fFormat.u.encoded_video.output.display.line_width;
uint32 height = fFormat.u.encoded_video.output.display.line_count;
// specifiy the decoded format. we derive this information from
// the encoded format (width & height).
memset(&fFormat, 0, sizeof(media_format));
// fFormat.u.raw_video.last_active = height - 1;
// fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
// fFormat.u.raw_video.pixel_width_aspect = 1;
// fFormat.u.raw_video.pixel_height_aspect = 1;
fFormat.u.raw_video.display.format = format;
fFormat.u.raw_video.display.line_width = width;
fFormat.u.raw_video.display.line_count = height;
if (format == B_RGB32 || format == B_RGBA32)
fFormat.u.raw_video.display.bytes_per_row = width * 4;
else if (format == B_YCbCr422)
fFormat.u.raw_video.display.bytes_per_row = ((width * 2 + 3) / 4) * 4;
ret = fVideoTrack->DecodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier() - "
"fVideoTrack->DecodedFormat(): %s\n", strerror(ret));
return;
}
if (fFormat.u.raw_video.display.format != format) {
printf("MediaTrackVideoSupplier() - "
" codec changed colorspace of decoded format (%s -> %s)!\n"
" this is bad for performance, since colorspace conversion\n"
" needs to happen during playback.\n",
string_for_color_space(format),
string_for_color_space(fFormat.u.raw_video.display.format));
// check if the codec forgot to adjust bytes_per_row
uint32 minBPR;
format = fFormat.u.raw_video.display.format;
if (format == B_YCbCr422)
minBPR = ((width * 2 + 3) / 4) * 4;
else
minBPR = width * 4;
if (minBPR != fFormat.u.raw_video.display.bytes_per_row) {
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
fFormat.u.raw_video.display.bytes_per_row = minBPR;
fVideoTrack->DecodedFormat(&fFormat);
}
}
_SwitchFormat(format, 0);
fDuration = fVideoTrack->Duration();
@@ -113,7 +59,7 @@ MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
}
media_format
const media_format&
MediaTrackVideoSupplier::Format() const
{
return fFormat;
@@ -139,26 +85,44 @@ MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
status_t
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime)
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
const media_format* format, bool& wasCached)
{
if (!fVideoTrack)
return B_NO_INIT;
if (!buffer)
return B_BAD_VALUE;
status_t ret = B_OK;
if (format->u.raw_video.display.format
!= fFormat.u.raw_video.display.format
|| fFormat.u.raw_video.display.bytes_per_row
!= format->u.raw_video.display.bytes_per_row) {
ret = _SwitchFormat(format->u.raw_video.display.format,
format->u.raw_video.display.bytes_per_row);
if (ret < B_OK) {
fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
"unable to switch media format: %s\n", strerror(ret));
return ret;
}
}
// read a frame
int64 frameCount = 1;
// TODO: how does this work for interlaced video (field count > 1)?
media_header mediaHeader;
status_t ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::ReadFrame() - "
"error while reading frame of track: %s\n", strerror(ret));
if (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;
@@ -186,10 +150,52 @@ MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
return B_NO_INIT;
bigtime_t _performanceTime = *performanceTime;
status_t ret = fVideoTrack->SeekToTime(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) {
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
if (_performanceTime != *performanceTime)
printf("seeked by time: %lld -> %lld\n", _performanceTime, *performanceTime);
fPerformanceTime = *performanceTime;
fCurrentFrame = fVideoTrack->CurrentFrame();
}
return ret;
}
status_t
MediaTrackVideoSupplier::SeekToFrame(int64* frame)
{
if (!fVideoTrack)
return B_NO_INIT;
int64 wantFrame = *frame;
int64 currentFrame = fVideoTrack->CurrentFrame();
if (wantFrame == currentFrame)
return B_OK;
status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
B_MEDIA_SEEK_CLOSEST_BACKWARD);
if (ret < B_OK)
return ret;
if (*frame < currentFrame && wantFrame > currentFrame) {
*frame = currentFrame;
return B_OK;
}
if (wantFrame != *frame)
printf("seeked by frame: %lld -> %lld\n", wantFrame, *frame);
ret = fVideoTrack->SeekToFrame(frame);
if (ret == B_OK) {
fCurrentFrame = *frame;
fPerformanceTime = fVideoTrack->CurrentTime();
}
return ret;
@@ -297,3 +303,69 @@ string_for_color_space(color_space format)
}
return name;
}
status_t
MediaTrackVideoSupplier::_SwitchFormat(color_space format, int32 bytesPerRow)
{
// get the encoded format
memset(&fFormat, 0, sizeof(media_format));
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
return ret;
}
// get ouput video frame size
uint32 width = fFormat.u.encoded_video.output.display.line_width;
uint32 height = fFormat.u.encoded_video.output.display.line_count;
// specifiy the decoded format. we derive this information from
// the encoded format (width & height).
memset(&fFormat, 0, sizeof(media_format));
// fFormat.u.raw_video.last_active = height - 1;
// fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
// fFormat.u.raw_video.pixel_width_aspect = 1;
// fFormat.u.raw_video.pixel_height_aspect = 1;
fFormat.u.raw_video.display.format = format;
fFormat.u.raw_video.display.line_width = width;
fFormat.u.raw_video.display.line_count = height;
int32 minBytesPerRow;
if (format == B_RGB32 || format == B_RGBA32)
minBytesPerRow = width * 4;
else if (format == B_YCbCr422)
minBytesPerRow = ((width * 2 + 3) / 4) * 4;
fFormat.u.raw_video.display.bytes_per_row = max_c(minBytesPerRow,
bytesPerRow);
ret = fVideoTrack->DecodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::_SwitchFormat() - "
"fVideoTrack->DecodedFormat(): %s\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"
" this is bad for performance, since colorspace conversion\n"
" needs to happen during playback.\n",
string_for_color_space(format),
string_for_color_space(fFormat.u.raw_video.display.format));
// check if the codec forgot to adjust bytes_per_row
uint32 minBPR;
format = fFormat.u.raw_video.display.format;
if (format == B_YCbCr422)
minBPR = ((width * 2 + 3) / 4) * 4;
else
minBPR = width * 4;
if (minBPR != fFormat.u.raw_video.display.bytes_per_row) {
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
fFormat.u.raw_video.display.bytes_per_row = minBPR;
fVideoTrack->DecodedFormat(&fFormat);
}
}
return ret;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Copyright 2007-2008, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -8,40 +8,51 @@
#ifndef MEDIA_TRACK_VIDEO_SUPPLIER_H
#define MEDIA_TRACK_VIDEO_SUPPLIER_H
#include "VideoSupplier.h"
#include "VideoTrackSupplier.h"
#include <MediaFormats.h>
class BMediaTrack;
class MediaTrackVideoSupplier : public VideoSupplier {
class MediaTrackVideoSupplier : public VideoTrackSupplier {
public:
MediaTrackVideoSupplier(BMediaTrack* track,
color_space preferredFormat);
virtual ~MediaTrackVideoSupplier();
virtual media_format Format() const;
virtual const media_format& Format() const;
virtual status_t GetEncodedFormat(media_format* format) const;
virtual status_t GetCodecInfo(media_codec_info* info) const;
virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime);
bigtime_t* performanceTime,
const media_format* format,
bool& wasCached);
virtual status_t SeekToTime(bigtime_t* performanceTime);
virtual status_t SeekToFrame(int64* frame);
virtual bigtime_t Position() const
{ return fPerformanceTime; }
virtual bigtime_t Duration() const
{ return fDuration; }
virtual int64 CurrentFrame() const
{ return fCurrentFrame; }
virtual BRect Bounds() const;
virtual color_space ColorSpace() const;
virtual uint32 BytesPerRow() const;
private:
status_t _SwitchFormat(color_space format,
int32 bytesPerRow);
BMediaTrack* fVideoTrack;
media_format fFormat;
bigtime_t fPerformanceTime;
bigtime_t fDuration;
int64 fCurrentFrame;
};
#endif // MEDIA_TRACK_VIDEO_SUPPLIER_H
@@ -0,0 +1,291 @@
/*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "ProxyAudioSupplier.h"
#include <algobase.h>
#include <new>
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <List.h>
#include "AudioAdapter.h"
#include "AudioTrackSupplier.h"
#include "PlaybackManager.h"
using std::nothrow;
//#define TRACE_PROXY_AUDIO_SUPPLIER
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
# define TRACE(x...) printf("ProxyAudioSupplier::"); printf(x)
#else
# define TRACE(x...)
#endif
struct PlayingInterval {
PlayingInterval(bigtime_t startTime, bigtime_t endTime)
: start_time(startTime)
, end_time(endTime)
{
}
bigtime_t start_time;
bigtime_t end_time;
bigtime_t x_start_time;
bigtime_t x_end_time;
float speed;
};
ProxyAudioSupplier::ProxyAudioSupplier(PlaybackManager* playbackManager)
: fPlaybackManager(playbackManager)
, fVideoFrameRate(25.0)
, fSupplier(NULL)
, fAdapter(NULL)
, fAudioResampler()
{
TRACE("ProxyAudioSupplier()\n");
}
ProxyAudioSupplier::~ProxyAudioSupplier()
{
TRACE("~ProxyAudioSupplier()\n");
delete fAdapter;
}
status_t
ProxyAudioSupplier::GetFrames(void* buffer, int64 frameCount,
bigtime_t startTime, bigtime_t endTime)
{
TRACE("GetFrames(%p, %lld, %lld, %lld)\n", buffer, frameCount,
startTime, endTime);
// Create a list of playing intervals which compose the supplied
// performance time interval.
BList playingIntervals;
status_t error = fPlaybackManager->LockWithTimeout(10000);
if (error == B_OK) {
bigtime_t intervalStartTime = startTime;
while (intervalStartTime < endTime) {
PlayingInterval* interval
= new (nothrow) PlayingInterval(intervalStartTime, endTime);
if (!interval) {
error = B_NO_MEMORY;
break;
}
fPlaybackManager->GetPlaylistTimeInterval(
interval->start_time, interval->end_time,
interval->x_start_time, interval->x_end_time,
interval->speed);
if (!playingIntervals.AddItem(interval)) {
delete interval;
error = B_NO_MEMORY;
break;
}
intervalStartTime = interval->end_time;
}
fPlaybackManager->SetCurrentAudioTime(endTime);
fPlaybackManager->Unlock();
} else if (error == B_TIMED_OUT) {
TRACE("GetFrames() - LOCKING THE PLAYBACK MANAGER TIMED OUT!!!\n");
}
// retrieve the audio data for each interval.
int64 framesRead = 0;
while (!playingIntervals.IsEmpty()) {
PlayingInterval* interval
= (PlayingInterval*)playingIntervals.RemoveItem(0L);
if (error != B_OK) {
delete interval;
continue;
}
TRACE("GetFrames() - interval [%lld, %lld]: [%lld, %lld]\n",
interval->start_time, interval->end_time,
interval->x_start_time, interval->x_end_time);
// get playing direction
int32 playingDirection = 0;
if (interval->speed > 0)
playingDirection = 1;
else if (interval->speed < 0)
playingDirection = -1;
float absSpeed = interval->speed * playingDirection;
int64 framesToRead = _AudioFrameForTime(interval->end_time)
- _AudioFrameForTime(interval->start_time);
// not playing
if (absSpeed == 0)
_ReadSilence(buffer, framesToRead);
// playing
else {
fAudioResampler.SetInOffset(
_AudioFrameForTime(interval->x_start_time));
fAudioResampler.SetTimeScale(absSpeed);
error = fAudioResampler.Read(buffer, 0, framesToRead);
// backwards -> reverse frames
if (error == B_OK && interval->speed < 0)
_ReverseFrames(buffer, framesToRead);
}
// read silence on error
if (error != B_OK) {
_ReadSilence(buffer, framesToRead);
error = B_OK;
}
framesRead += framesToRead;
buffer = _SkipFrames(buffer, framesToRead);
delete interval;
}
// read silence on error
if (error != B_OK) {
_ReadSilence(buffer, frameCount);
error = B_OK;
}
TRACE("GetFrames() done\n");
return error;
}
void
ProxyAudioSupplier::SetFormat(const media_format& format)
{
//printf("ProxyAudioSupplier::SetFormat()\n");
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
char string[256];
string_for_format(format, string, 256);
TRACE("SetFormat(%s)\n", string);
#endif
fAudioResampler.SetFormat(format);
// In case SetSupplier was called before, we need
// to adapt to the new format, or maybe the format
// was still invalid.
SetSupplier(fSupplier, fVideoFrameRate);
}
const media_format&
ProxyAudioSupplier::Format() const
{
return fAudioResampler.Format();
}
status_t
ProxyAudioSupplier::InitCheck() const
{
status_t ret = AudioSupplier::InitCheck();
if (ret < B_OK)
return ret;
return B_OK;
}
void
ProxyAudioSupplier::SetSupplier(AudioTrackSupplier* supplier,
float videoFrameRate)
{
//printf("ProxyAudioSupplier::SetSupplier(%p, %.1f)\n", supplier,
//videoFrameRate);
TRACE("SetSupplier(%p, %.1f)\n", supplier, videoFrameRate);
fSupplier = supplier;
fVideoFrameRate = videoFrameRate;
delete fAdapter;
fAdapter = new AudioAdapter(fSupplier, Format());
fAudioResampler.SetSource(fAdapter);
}
// #pragma mark - audio/video/frame/time conversion
int64
ProxyAudioSupplier::_AudioFrameForVideoFrame(int64 frame) const
{
if (!fSupplier) {
return (int64)((double)frame * Format().u.raw_audio.frame_rate
/ fVideoFrameRate);
}
const media_format& format = fSupplier->Format();
return (int64)((double)frame * format.u.raw_audio.frame_rate
/ fVideoFrameRate);
}
int64
ProxyAudioSupplier::_VideoFrameForAudioFrame(int64 frame) const
{
if (!fSupplier) {
return (int64)((double)frame * fVideoFrameRate
/ Format().u.raw_audio.frame_rate);
}
const media_format& format = fSupplier->Format();
return (int64)((double)frame * fVideoFrameRate
/ format.u.raw_audio.frame_rate);
}
int64
ProxyAudioSupplier::_AudioFrameForTime(bigtime_t time) const
{
return (int64)((double)time * Format().u.raw_audio.frame_rate
/ 1000000.0);
}
int64
ProxyAudioSupplier::_VideoFrameForTime(bigtime_t time) const
{
return (int64)((double)time * fVideoFrameRate / 1000000.0);
}
// #pragma mark - utility
void
ProxyAudioSupplier::_ReadSilence(void* buffer, int64 frames) const
{
memset(buffer, 0, (char*)_SkipFrames(buffer, frames) - (char*)buffer);
}
void
ProxyAudioSupplier::_ReverseFrames(void* buffer, int64 frames) const
{
int32 sampleSize = Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
char* front = (char*)buffer;
char* back = (char*)buffer + (frames - 1) * frameSize;
while (front < back) {
for (int32 i = 0; i < frameSize; i++)
swap(front[i], back[i]);
front += frameSize;
back -= frameSize;
}
}
void*
ProxyAudioSupplier::_SkipFrames(void* buffer, int64 frames) const
{
int32 sampleSize = Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
return (char*)buffer + frames * frameSize;
}
@@ -0,0 +1,57 @@
/*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef PROXY_AUDIO_SUPPLIER_H
#define PROXY_AUDIO_SUPPLIER_H
#include "AudioResampler.h"
#include "AudioSupplier.h"
class AudioTrackSupplier;
class PlaybackManager;
class ProxyAudioSupplier : public AudioSupplier {
public:
ProxyAudioSupplier(
PlaybackManager* playbackManager);
virtual ~ProxyAudioSupplier();
// AudioSupplier interface
virtual status_t GetFrames(void* buffer, int64 frameCount,
bigtime_t startTime, bigtime_t endTime);
virtual void SetFormat(const media_format& format);
virtual const media_format& Format() const;
virtual status_t InitCheck() const;
// ProxyAudioSupplier
void SetSupplier(AudioTrackSupplier* supplier,
float videoFrameRate);
private:
int64 _AudioFrameForVideoFrame(int64 frame) const;
int64 _VideoFrameForAudioFrame(int64 frame) const;
int64 _AudioFrameForTime(bigtime_t time) const;
int64 _VideoFrameForTime(bigtime_t time) const;
void _ReadSilence(void* buffer, int64 frames) const;
void _ReverseFrames(void* buffer,
int64 frames) const;
void* _SkipFrames(void* buffer, int64 frames) const;
private:
PlaybackManager* fPlaybackManager;
float fVideoFrameRate;
AudioTrackSupplier* fSupplier;
AudioReader* fAdapter;
AudioResampler fAudioResampler;
};
#endif // PROXY_AUDIO_SUPPLIER_H
@@ -0,0 +1,65 @@
/*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#include "ProxyVideoSupplier.h"
#include <stdio.h>
#include <Autolock.h>
#include "VideoTrackSupplier.h"
ProxyVideoSupplier::ProxyVideoSupplier()
: fSupplier(NULL)
{
}
ProxyVideoSupplier::~ProxyVideoSupplier()
{
}
status_t
ProxyVideoSupplier::FillBuffer(int64 startFrame, void* buffer,
const media_format* format, bool& wasCached)
{
//printf("ProxyVideoSupplier::FillBuffer(%lld)\n", startFrame);
if (fSupplier == NULL)
return B_NO_INIT;
bigtime_t performanceTime = 0;
if (fSupplier->CurrentFrame() != startFrame) {
int64 frame = startFrame;
status_t ret = fSupplier->SeekToFrame(&frame);
if (ret != B_OK)
return ret;
while (frame < (startFrame - 1)) {
ret = fSupplier->ReadFrame(buffer, &performanceTime, format,
wasCached);
if (ret != B_OK)
return ret;
frame++;
}
}
// TODO: cache into intermediate buffer!
return fSupplier->ReadFrame(buffer, &performanceTime, format, wasCached);
}
void
ProxyVideoSupplier::DeleteCaches()
{
}
void
ProxyVideoSupplier::SetSupplier(VideoTrackSupplier* supplier)
{
fSupplier = supplier;
}
@@ -0,0 +1,32 @@
/*
* Copyright © 2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT licensce.
*/
#ifndef PROXY_VIDEO_SUPPLIER_H
#define PROXY_VIDEO_SUPPLIER_H
#include "VideoSupplier.h"
class VideoTrackSupplier;
class ProxyVideoSupplier : public VideoSupplier {
public:
ProxyVideoSupplier();
virtual ~ProxyVideoSupplier();
virtual status_t FillBuffer(int64 startFrame, void* buffer,
const media_format* format,
bool& wasCached);
virtual void DeleteCaches();
void SetSupplier(VideoTrackSupplier* supplier);
private:
VideoTrackSupplier* fSupplier;
};
#endif // PROXY_VIDEO_SUPPLIER_H
@@ -5,15 +5,15 @@
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "VideoSupplier.h"
#include "VideoTrackSupplier.h"
VideoSupplier::VideoSupplier()
VideoTrackSupplier::VideoTrackSupplier()
{
}
VideoSupplier::~VideoSupplier()
VideoTrackSupplier::~VideoTrackSupplier()
{
}
@@ -5,28 +5,32 @@
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef VIDEO_SUPPLIER_H
#define VIDEO_SUPPLIER_H
#ifndef VIDEO_TRACK_SUPPLIER_H
#define VIDEO_TRACK_SUPPLIER_H
#include <MediaDefs.h>
#include <MediaFormats.h>
class VideoSupplier {
class VideoTrackSupplier {
public:
VideoSupplier();
virtual ~VideoSupplier();
VideoTrackSupplier();
virtual ~VideoTrackSupplier();
virtual media_format Format() const = 0;
virtual const media_format& Format() const = 0;
virtual status_t GetEncodedFormat(media_format* format)
const = 0;
virtual status_t GetCodecInfo(media_codec_info* info) const = 0;
virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime) = 0;
bigtime_t* performanceTime,
const media_format* format,
bool& wasCached) = 0;
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
virtual status_t SeekToFrame(int64* frame) = 0;
virtual bigtime_t Position() const = 0;
virtual bigtime_t Duration() const = 0;
virtual int64 CurrentFrame() const = 0;
};
#endif // VIDEO_SUPPLIER_H
#endif // VIDEO_TRACK_SUPPLIER_H
-151
View File
@@ -1,151 +0,0 @@
/*
* Copyright 2004-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <bonefish@cs.tu-berlin.de>
*/
/** Scope-based automatic deletion of objects/arrays.
* ObjectDeleter - deletes an object
* ArrayDeleter - deletes an array
* MemoryDeleter - free()s malloc()ed memory
*/
#ifndef AUTO_LOCKER_H
#define AUTO_LOCKER_H
#include <SupportDefs.h>
// locking
// AutoLockerStandardLocking
template<typename Lockable>
class AutoLockerStandardLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->Lock();
}
inline void Unlock(Lockable *lockable)
{
lockable->Unlock();
}
};
// AutoLockerReadLocking
template<typename Lockable>
class AutoLockerReadLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->ReadLock();
}
inline void Unlock(Lockable *lockable)
{
lockable->ReadUnlock();
}
};
// AutoLockerWriteLocking
template<typename Lockable>
class AutoLockerWriteLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->WriteLock();
}
inline void Unlock(Lockable *lockable)
{
lockable->WriteUnlock();
}
};
// AutoLocker
template<typename Lockable,
typename Locking = AutoLockerStandardLocking<Lockable> >
class AutoLocker {
private:
typedef AutoLocker<Lockable, Locking> ThisClass;
public:
inline AutoLocker(Lockable *lockable, bool alreadyLocked = false)
: fLockable(lockable),
fLocked(fLockable && alreadyLocked)
{
if (!fLocked)
_Lock();
}
inline AutoLocker(Lockable &lockable, bool alreadyLocked = false)
: fLockable(&lockable),
fLocked(fLockable && alreadyLocked)
{
if (!fLocked)
_Lock();
}
inline ~AutoLocker()
{
Unlock();
}
inline void SetTo(Lockable *lockable, bool alreadyLocked)
{
Unlock();
fLockable = lockable;
fLocked = alreadyLocked;
if (!fLocked)
_Lock();
}
inline void SetTo(Lockable &lockable, bool alreadyLocked)
{
SetTo(&lockable, alreadyLocked);
}
inline void Unset()
{
Unlock();
}
inline AutoLocker<Lockable, Locking> &operator=(Lockable *lockable)
{
SetTo(lockable);
return *this;
}
inline AutoLocker<Lockable, Locking> &operator=(Lockable &lockable)
{
SetTo(&lockable);
return *this;
}
inline bool IsLocked() const { return fLocked; }
inline void Unlock()
{
if (fLockable && fLocked) {
fLocking.Unlock(fLockable);
fLocked = false;
}
}
inline operator bool() const { return fLocked; }
private:
inline void _Lock()
{
if (fLockable)
fLocked = fLocking.Lock(fLockable);
}
private:
Lockable *fLockable;
bool fLocked;
Locking fLocking;
};
#endif // AUTO_LOCKER_H
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include <stdio.h>
#include "Event.h"
Event::Event(bool autoDelete)
: fTime(0),
fAutoDelete(autoDelete)
{
}
Event::Event(bigtime_t time, bool autoDelete)
: fTime(time),
fAutoDelete(autoDelete)
{
}
Event::~Event()
{
}
void
Event::SetTime(bigtime_t time)
{
fTime = time;
}
bigtime_t
Event::Time() const
{
return fTime;
}
void
Event::SetAutoDelete(bool autoDelete)
{
fAutoDelete = autoDelete;
}
void
Event::Execute()
{
printf("Event::Execute() - %Ld\n", fTime);
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef EVENT_H
#define EVENT_H
#include <OS.h>
class Event {
public:
Event(bool autoDelete = true);
Event(bigtime_t time, bool autoDelete = true);
virtual ~Event();
void SetTime(bigtime_t time);
bigtime_t Time() const;
void SetAutoDelete(bool autoDelete);
bool AutoDelete() const
{ return fAutoDelete; }
virtual void Execute();
private:
bigtime_t fTime;
bool fAutoDelete;
};
#endif // EVENT_H
+210
View File
@@ -0,0 +1,210 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include <new>
#include <stdio.h>
#include "Event.h"
#include "EventQueue.h"
EventQueue::EventQueue()
: fEvents(100),
fEventExecutor(-1),
fThreadControl(-1),
fNextEventTime(0),
fStatus(B_ERROR)
{
fThreadControl = create_sem(0, "event queue control");
if (fThreadControl >= B_OK)
fStatus = B_OK;
else
fStatus = fThreadControl;
if (fStatus == B_OK) {
fEventExecutor = spawn_thread(_execute_events_, "event queue runner",
B_NORMAL_PRIORITY, this);
if (fEventExecutor >= B_OK) {
fStatus = B_OK;
resume_thread(fEventExecutor);
} else
fStatus = fEventExecutor;
}
}
EventQueue::~EventQueue()
{
if (delete_sem(fThreadControl) == B_OK)
wait_for_thread(fEventExecutor, &fEventExecutor);
while (Event *event = (Event*)fEvents.RemoveItem(0L)) {
if (event->AutoDelete())
delete event;
}
}
status_t
EventQueue::InitCheck()
{
return fStatus;
}
EventQueue*
EventQueue::CreateDefault()
{
if (!fDefaultQueue) {
fDefaultQueue = new(nothrow) EventQueue;
if (fDefaultQueue && fDefaultQueue->InitCheck() != B_OK)
DeleteDefault();
}
return fDefaultQueue;
}
void
EventQueue::DeleteDefault()
{
if (fDefaultQueue) {
delete fDefaultQueue;
fDefaultQueue = NULL;
}
}
EventQueue&
EventQueue::Default()
{
return *fDefaultQueue;
}
void
EventQueue::AddEvent(Event* event)
{
Lock();
_AddEvent(event);
_Reschedule();
Unlock();
}
bool
EventQueue::RemoveEvent(Event* event)
{
bool result = false;
Lock();
if ((result = fEvents.RemoveItem(event)))
_Reschedule();
Unlock();
return result;
}
void
EventQueue::ChangeEvent(Event* event, bigtime_t newTime)
{
Lock();
if (fEvents.RemoveItem(event)) {
event->SetTime(newTime);
_AddEvent(event);
_Reschedule();
}
Unlock();
}
// PRE: The object must be locked.
void
EventQueue::_AddEvent(Event* event)
{
// find the insertion index
int32 lower = 0;
int32 upper = fEvents.CountItems();
while (lower < upper) {
int32 mid = (lower + upper) / 2;
Event* midEvent = _EventAt(mid);
if (event->Time() < midEvent->Time())
upper = mid;
else
lower = mid + 1;
}
fEvents.AddItem(event, lower);
}
Event*
EventQueue::_EventAt(int32 index) const
{
return (Event*)fEvents.ItemAtFast(index);
}
int32
EventQueue::_execute_events_(void* cookie)
{
EventQueue *gc = (EventQueue*)cookie;
return gc->_ExecuteEvents();
}
int32
EventQueue::_ExecuteEvents()
{
bool running = true;
while (running) {
bigtime_t waitUntil = B_INFINITE_TIMEOUT;
if (Lock()) {
if (!fEvents.IsEmpty())
waitUntil = _EventAt(0)->Time();
fNextEventTime = waitUntil;
Unlock();
}
status_t err = acquire_sem_etc(fThreadControl, 1, B_ABSOLUTE_TIMEOUT,
waitUntil);
switch (err) {
case B_TIMED_OUT:
// execute events, that are supposed to go off
if (Lock()) {
while (!fEvents.IsEmpty()
&& system_time() >= _EventAt(0)->Time()) {
Event* event = (Event*)fEvents.RemoveItem(0L);
bool deleteEvent = event->AutoDelete();
event->Execute();
if (deleteEvent)
delete event;
}
Unlock();
}
break;
case B_BAD_SEM_ID:
running = false;
break;
case B_OK:
default:
break;
}
}
return 0;
}
// PRE: The object must be locked.
void
EventQueue::_Reschedule()
{
if (fStatus == B_OK) {
if (!fEvents.IsEmpty() && _EventAt(0)->Time() < fNextEventTime)
release_sem(fThreadControl);
}
}
// static variables
EventQueue* EventQueue::fDefaultQueue = NULL;
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef EVENT_QUEUE_H
#define EVENT_QUEUE_H
#include <List.h>
#include <Locker.h>
#include <OS.h>
class Event;
class EventQueue : public BLocker {
public:
EventQueue();
virtual ~EventQueue();
status_t InitCheck();
static EventQueue* CreateDefault();
static void DeleteDefault();
static EventQueue& Default();
void AddEvent(Event* event);
bool RemoveEvent(Event* event);
void ChangeEvent(Event* event,
bigtime_t newTime);
private:
void _AddEvent(Event* event);
Event* _EventAt(int32 index) const;
static int32 _execute_events_(void *cookie);
int32 _ExecuteEvents();
void _Reschedule();
BList fEvents;
thread_id fEventExecutor;
sem_id fThreadControl;
volatile bigtime_t fNextEventTime;
status_t fStatus;
static EventQueue* fDefaultQueue;
};
#endif // EVENT_QUEUE_H
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include <Message.h>
#include "MessageEvent.h"
MessageEvent::MessageEvent(bigtime_t time, BHandler* handler, uint32 command)
: Event(time),
AbstractLOAdapter(handler),
fCommand(command)
{
}
MessageEvent::MessageEvent(bigtime_t time, const BMessenger& messenger)
: Event(time),
AbstractLOAdapter(messenger)
{
}
MessageEvent::~MessageEvent()
{
}
void
MessageEvent::Execute()
{
BMessage msg(fCommand);
msg.AddInt64("time", Time());
DeliverMessage(msg);
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
* Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#ifndef MESSAGE_EVENT_H
#define MESSAGE_EVENT_H
#include "AbstractLOAdapter.h"
#include "Event.h"
enum {
MSG_EVENT = 'evnt',
};
class MessageEvent : public Event, public AbstractLOAdapter {
public:
MessageEvent(bigtime_t time,
BHandler* handler,
uint32 command = MSG_EVENT);
MessageEvent(bigtime_t time,
const BMessenger& messenger);
virtual ~MessageEvent();
virtual void Execute();
private:
uint32 fCommand;
};
#endif // MESSAGE_EVENT_H