📄 soundconsumer.cxx
字号:
return B_MEDIA_BAD_FORMAT;
}
#if !ACCEPT_ANY_FORMAT_CHANGE
// If we're already connected, and this format doesn't go with the
// format in effect, we dont' accept this new format.
if (!format_is_compatible(*format, m_input.format)) {
*format = m_input.format;
return B_MEDIA_BAD_FORMAT;
}
#endif
// I guess we're OK by now, because we have no particular needs as
// far as frame rate, sample format, etc go.
return B_OK;
}
status_t SoundConsumer::GetNextInput(
int32 * cookie,
media_input * out_input)
{
NODE(stderr, "SoundConsumer: GetNextInput()\n");
// The "next" is kind of misleading, since it's also used for
// getting the first (and only) input.
if (!*cookie) {
if (m_input.source == media_source::null) {
// If there's no current connection, make sure we return a
// reasonable format telling the world we accept any raw audio.
m_input.format.type = B_MEDIA_RAW_AUDIO;
m_input.format.u.raw_audio = media_raw_audio_format::wildcard;
m_input.node = Node();
m_input.destination.port = ControlPort();
m_input.destination.id = 1;
}
*out_input = m_input;
*cookie = 1;
return B_OK;
}
// There's only one input.
return B_BAD_INDEX;
}
void SoundConsumer::DisposeInputCookie(
int32 /* cookie */)
{
// We didn't allocate any memory or set any state in GetNextInput()
// so this function is a no-op.
}
void SoundConsumer::BufferReceived(
BBuffer * buffer)
{
NODE(stderr, "SoundConsumer::BufferReceived()\n");
// Whee, a buffer! Update the seek info, if necessary.
if (m_seeking && buffer->Header()->start_time >= m_tpSeekAt) {
m_delta = m_tpSeekAt - m_tmSeekTo;
m_seeking = false;
}
// If there is a record hook, let the interested party have at it!
if (m_recordHook) {
(*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
}
else {
Record(buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
}
// Buffers should ALWAYS be recycled, else whomever is producing them
// will starve.
buffer->Recycle();
}
void SoundConsumer::ProducerDataStatus(
const media_destination & for_whom,
int32 status,
bigtime_t at_media_time)
{
if (for_whom == m_input.destination) {
// Tell whomever is interested that the upstream producer will or won't
// send more data in the immediate future.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status, at_media_time);
}
else {
Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);
}
}
}
status_t SoundConsumer::GetLatencyFor(
const media_destination & for_whom,
bigtime_t * out_latency,
media_node_id * out_timesource)
{
// We only accept requests for the one-and-only input of our Node.
if (for_whom != m_input.destination) {
return B_MEDIA_BAD_DESTINATION;
}
// Tell the world about our latency information (overridable by user).
*out_latency = TotalLatency();
*out_timesource = TimeSource()->Node().node;
return B_OK;
}
status_t SoundConsumer::Connected(
const media_source & producer,
const media_destination & where,
const media_format & with_format,
media_input * out_input)
{
NODE(stderr, "SoundConsumer::Connected()\n");
// Only accept connection requests when we're not already connected.
if (m_input.source != media_source::null) {
return B_MEDIA_BAD_DESTINATION;
}
// Only accept connection requests on the one-and-only available input.
if (where != m_input.destination) {
return B_MEDIA_BAD_DESTINATION;
}
// Other than that, we accept pretty much anything. The format has been
// pre-cleared through AcceptFormat(), and we accept any format anyway.
m_input.source = producer;
m_input.format = with_format;
// Tell whomever is interested that there's now a connection.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name);
}
else {
Notify(B_CONNECTED, m_input.name);
}
// This is the most important line -- return our connection information
// to the world so it can use it!
*out_input = m_input;
return B_OK;
}
void SoundConsumer::Disconnected(
const media_source & producer,
const media_destination & where)
{
// We can't disconnect something which isn't us.
if (where != m_input.destination) {
return;
}
// We can't disconnect from someone who isn't connected to us.
if (producer != m_input.source) {
return;
}
// Tell the interested party that it's time to leave.
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_DISCONNECTED);
}
else {
Notify(B_DISCONNECTED);
}
// Mark ourselves as not-connected.
m_input.source = media_source::null;
}
status_t SoundConsumer::FormatChanged(
const media_source & producer,
const media_destination & consumer,
int32 from_change_count,
const media_format & format)
{
NODE(stderr, "SoundConsumer::Connected()\n");
// The up-stream guy feels like changing the format. If we can accept
// arbitrary format changes, we just say "OK". If, however, we're recording
// to a file, that's not such a good idea; we only accept format changes
// that are compatible with the format we're already using. You set this
// behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or 0.
status_t err = B_OK;
#if ACCEPT_ANY_FORMAT_CHANGE
media_format fmt(format);
err = AcceptFormat(m_input.destination, &fmt);
#else
if (m_input.source != media_source::null) {
err = format_is_compatible(format, m_input.format) ? B_OK : B_MEDIA_BAD_FORMAT;
}
#endif
if (err >= B_OK) {
m_input.format = format;
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
}
else {
Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
}
}
return err;
}
void
SoundConsumer::DoHookChange(
void * msg)
{
// Tell the old guy we're changing the hooks ...
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_HOOKS_CHANGED);
}
else {
Notify(B_HOOKS_CHANGED);
}
// ... and then do it.
set_hooks_q * ptr = (set_hooks_q *)msg;
m_recordHook = ptr->process;
m_notifyHook = ptr->notify;
m_cookie = ptr->cookie;
}
status_t
SoundConsumer::ThreadEntry(
void * obj)
{
((SoundConsumer *)obj)->ServiceThread();
return 0;
}
void
SoundConsumer::ServiceThread()
{
// The Big Bad ServiceThread receives messages aimed at this
// Node and dispatches them (typically to HandleMessage()).
// If we were a Producer, we might have to do finicky timing and
// queued Start()/Stop() processing in here. But we ain't.
// A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE.
// Avoid wasing stack space by dynamically allocating at start.
char * msg = new char[B_MEDIA_MESSAGE_SIZE];
// Make sure we clean up this data when we exit the function.
array_delete<char> msg_delete(msg);
int bad = 0;
while (true) {
// Call read_port_etc() with a timeout derived from a virtual function,
// to allow clients to do special processing if necessary.
bigtime_t timeout = Timeout();
int32 code = 0;
status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE,
B_TIMEOUT, timeout);
MESSAGE(stderr, "SoundConsumer::ServiceThread() port %ld message %#010lx\n", m_port, code);
// If we received a message, err will be the size of the message (including 0).
if (err >= 0) {
// Real messages reset the timeout time.
m_trTimeout = 0;
bad = 0;
// Check for our private stop message.
if (code == MSG_QUIT_NOW) {
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_NODE_DIES, 0);
}
else {
Notify(B_NODE_DIES, 0);
}
break;
}
// Else check for our private change-hooks message.
else if (code == MSG_CHANGE_HOOKS) {
DoHookChange(msg);
// Write acknowledge to waiting thread.
write_port(((set_hooks_q *)msg)->reply, 0, 0, 0);
}
// Else it has to be a regular media kit message; go ahead and
// dispatch it.
else {
HandleMessage(code, msg, err);
}
}
// Timing out means that there was no buffer. Tell the interested party.
else if (err == B_TIMED_OUT) {
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout);
}
else {
Notify(B_OP_TIMED_OUT, timeout);
}
}
// Other errors are bad.
else {
FPRINTF(stderr, "SoundConsumer: error %#010lx\n", err);
bad++;
// If we receive three bad reads with no good messages inbetween,
// things are probably not going to improve (like the port disappeared
// or something) so we call it a day.
if (bad > 3) {
if (m_notifyHook) {
(*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg);
}
else {
Notify(B_NODE_DIES, bad, err, code, msg);
}
break;
}
}
}
}
bigtime_t
SoundConsumer::Timeout()
{
// Timeout() is called for each call to read_port_etc() in the service
// thread to figure out a reasonable time-out value. The default behaviour
// we've picked is to exponentially back off from one second and upwards.
// While it's true that 44 back-offs will run us out of precision in a
// bigtime_t, the time to actually reach 44 consecutive back-offs is longer
// than the expected market longevity of just about any piece of real estate.
// Is that the sound of an impending year-fifteen-million software problem? :-)
m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2;
return m_trTimeout;
}
bigtime_t
SoundConsumer::ProcessingLatency()
{
// We're saying it takes us 500 us to process each buffer. If all we do is
// copy the data, it probably takes much less than that, but it doesn't
// hurt to be slightly conservative.
return 500LL;
}
bigtime_t
SoundConsumer::TotalLatency()
{
// Had we been a producer that passes buffers on, we'd have to
// include downstream latency in this value. But we are not.
return ProcessingLatency();
}
void
SoundConsumer::Record(
bigtime_t /* time */,
const void * /* data */,
size_t /* size */,
const media_raw_audio_format & /* format */)
{
// If there is no record hook installed, we instead call this function
// for received buffers.
}
void
SoundConsumer::Notify(
int32 /* cause */,
...)
{
// If there is no notification hook installed, we instead call this function
// for giving notification of various events.
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -