<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/linux/pulse_output.h"

#include "base/message_loop.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#include "media/audio/linux/audio_manager_linux.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"

static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
  switch (bits_per_sample) {
    // Unsupported sample formats shown for reference.  I am assuming we want
    // signed and little endian because that is what we gave to ALSA.
    case 8:
      return PA_SAMPLE_U8;
      // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
    case 16:
      return PA_SAMPLE_S16LE;
      // Also 16-bits: PA_SAMPLE_S16BE (big endian).
    case 24:
      return PA_SAMPLE_S24LE;
      // Also 24-bits: PA_SAMPLE_S24BE (big endian).
      // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
      //          and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
    case 32:
      return PA_SAMPLE_S32LE;
      // Also 32-bits: PA_SAMPLE_S32BE (big endian),
      //               PA_SAMPLE_FLOAT32LE (floating point little endian),
      //           and PA_SAMPLE_FLOAT32BE (floating point big endian).
    default:
      return PA_SAMPLE_INVALID;
  }
}

static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
  switch (channel) {
    // PulseAudio does not differentiate between left/right and
    // stereo-left/stereo-right, both translate to front-left/front-right.
    case LEFT:
    case STEREO_LEFT:
      return PA_CHANNEL_POSITION_FRONT_LEFT;
    case RIGHT:
    case STEREO_RIGHT:
      return PA_CHANNEL_POSITION_FRONT_RIGHT;
    case CENTER:
      return PA_CHANNEL_POSITION_FRONT_CENTER;
    case LFE:
      return PA_CHANNEL_POSITION_LFE;
    case BACK_LEFT:
      return PA_CHANNEL_POSITION_REAR_LEFT;
    case BACK_RIGHT:
      return PA_CHANNEL_POSITION_REAR_RIGHT;
    case LEFT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
    case RIGHT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
    case BACK_CENTER:
      return PA_CHANNEL_POSITION_REAR_CENTER;
    case SIDE_LEFT:
      return PA_CHANNEL_POSITION_SIDE_LEFT;
    case SIDE_RIGHT:
      return PA_CHANNEL_POSITION_SIDE_RIGHT;
    case CHANNELS_MAX:
      return PA_CHANNEL_POSITION_INVALID;
  }
  NOTREACHED();
}

static pa_channel_map ChannelLayoutToPAChannelMap(
    ChannelLayout channel_layout) {
  // Initialize channel map.
  pa_channel_map channel_map;
  pa_channel_map_init(&channel_map);

  channel_map.channels = ChannelLayoutToChannelCount(channel_layout);

  // All channel maps have the same size array of channel positions.
  for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) {
    int channel_position = kChannelOrderings[channel_layout][channel];
    if (channel_position > -1) {
      channel_map.map[channel_position] = ChromiumToPAChannelPosition(
          static_cast<Channels>(channel));
    } else {
      // PulseAudio expects unused channels in channel maps to be filled with
      // PA_CHANNEL_POSITION_MONO.
      channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO;
    }
  }

  // Fill in the rest of the unused channels.
  for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX;
       ++channel) {
    channel_map.map[channel] = PA_CHANNEL_POSITION_MONO;
  }

  return channel_map;
}

static size_t MicrosecondsToBytes(
    uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) {
  return microseconds * sample_rate * bytes_per_frame /
      base::Time::kMicrosecondsPerSecond;
}

void PulseAudioOutputStream::ContextStateCallback(pa_context* context,
                                                  void* state_addr) {
  pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr);
  *state = pa_context_get_state(context);
}

void PulseAudioOutputStream::WriteRequestCallback(
    pa_stream* playback_handle, size_t length, void* stream_addr) {
  PulseAudioOutputStream* stream =
      static_cast<PulseAudioOutputStream*>(stream_addr);

  DCHECK_EQ(stream->message_loop_, MessageLoop::current());

  stream->write_callback_handled_ = true;

  // Fulfill write request.
  stream->FulfillWriteRequest(length);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerLinux* manager,
                                               MessageLoop* message_loop)
    : channel_layout_(params.channel_layout),
      channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
      sample_format_(BitsToPASampleFormat(params.bits_per_sample)),
      sample_rate_(params.sample_rate),
      bytes_per_frame_(params.channels * params.bits_per_sample / 8),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      playback_handle_(NULL),
      packet_size_(params.GetPacketSize()),
      frames_per_packet_(packet_size_ / bytes_per_frame_),
      client_buffer_(NULL),
      volume_(1.0f),
      stream_stopped_(true),
      write_callback_handled_(false),
      message_loop_(message_loop),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
      source_callback_(NULL) {
  DCHECK_EQ(message_loop_, MessageLoop::current());
  DCHECK(manager_);

  // TODO(slock): Sanity check input values.
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(),
  // which calls AudioManagerLinux::Release which deletes this object.
  DCHECK(!playback_handle_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

bool PulseAudioOutputStream::Open() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  // TODO(slock): Possibly move most of this to an OpenPlaybackDevice function
  // in a new class 'pulse_util', like alsa_util.

  // Create a mainloop API and connect to the default server.
  pa_mainloop_ = pa_mainloop_new();
  pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED;
  pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL);

  // Wait until PulseAudio is ready.
  pa_context_set_state_callback(pa_context_, &ContextStateCallback,
                                &pa_context_state);
  while (pa_context_state != PA_CONTEXT_READY) {
    pa_mainloop_iterate(pa_mainloop_, 1, NULL);
    if (pa_context_state == PA_CONTEXT_FAILED ||
        pa_context_state == PA_CONTEXT_TERMINATED) {
      Reset();
      return false;
    }
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = sample_format_;
  pa_sample_specifications.rate = sample_rate_;
  pa_sample_specifications.channels = channel_count_;

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
      channel_layout_);
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  playback_handle_ = pa_stream_new(pa_context_, "Playback",
                                   &pa_sample_specifications, map);

  // Initialize client buffer.
  uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_;
  client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size));

  // Set write callback.
  pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback, this);

  // Set server-side buffer attributes.
  // (uint32_t)-1 is the default and recommended value from PulseAudio's
  // documentation, found at:
  // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
  pa_buffer_attributes.tlength = output_packet_size;
  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
  pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.
  pa_stream_connect_playback(playback_handle_, NULL,
                             &pa_buffer_attributes,
                             (pa_stream_flags_t)
                             (PA_STREAM_INTERPOLATE_TIMING |
                             PA_STREAM_ADJUST_LATENCY |
                             PA_STREAM_AUTO_TIMING_UPDATE),
                             NULL, NULL);

  if (!playback_handle_) {
    Reset();
    return false;
  }

  return true;
}

void PulseAudioOutputStream::Reset() {
  stream_stopped_ = true;

  // Close the stream.
  if (playback_handle_) {
    pa_stream_flush(playback_handle_, NULL, NULL);
    pa_stream_disconnect(playback_handle_);

    // Release PulseAudio structures.
    pa_stream_unref(playback_handle_);
    playback_handle_ = NULL;
  }
  if (pa_context_) {
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  if (pa_mainloop_) {
    pa_mainloop_free(pa_mainloop_);
    pa_mainloop_ = NULL;
  }

  // Release internal buffer.
  client_buffer_.reset();
}

void PulseAudioOutputStream::Close() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::WaitForWriteRequest() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  if (stream_stopped_)
    return;

  // Iterate the PulseAudio mainloop.  If PulseAudio doesn't request a write,
  // post a task to iterate the mainloop again.
  write_callback_handled_ = false;
  pa_mainloop_iterate(pa_mainloop_, 1, NULL);
  if (!write_callback_handled_) {
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

bool PulseAudioOutputStream::BufferPacketFromSource() {
  uint32 buffer_delay = client_buffer_->forward_bytes();
  pa_usec_t pa_latency_micros;
  int negative;
  pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
  uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
                                              sample_rate_,
                                              bytes_per_frame_);
  // TODO(slock): Deal with negative latency (negative == 1).  This has yet
  // to happen in practice though.
  scoped_refptr<media::DataBuffer> packet =
      new media::DataBuffer(packet_size_);
  size_t packet_size = RunDataCallback(packet->GetWritableData(),
                                       packet->GetBufferSize(),
                                       AudioBuffersState(buffer_delay,
                                                         hardware_delay));

  if (packet_size == 0)
    return false;

  media::AdjustVolume(packet->GetWritableData(),
                      packet_size,
                      channel_count_,
                      bytes_per_frame_ / channel_count_,
                      volume_);
  packet->SetDataSize(packet_size);
  // Add the packet to the buffer.
  client_buffer_->Append(packet);
  return true;
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  // If we have enough data to fulfill the request, we can finish the write.
  if (stream_stopped_)
    return;

  // Request more data from the source until we can fulfill the request or
  // fail to receive anymore data.
  bool buffering_successful = true;
  while (client_buffer_->forward_bytes() < requested_bytes &&
         buffering_successful) {
    buffering_successful = BufferPacketFromSource();
  }

  size_t bytes_written = 0;
  if (client_buffer_->forward_bytes() > 0) {
    // Try to fulfill the request by writing as many of the requested bytes to
    // the stream as we can.
    WriteToStream(requested_bytes, &bytes_written);
  }

  if (bytes_written < requested_bytes) {
    // We weren't able to buffer enough data to fulfill the request.  Try to
    // fulfill the rest of the request later.
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::FulfillWriteRequest,
        weak_factory_.GetWeakPtr(),
        requested_bytes - bytes_written));
  } else {
    // Continue playback.
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write,
                                           size_t* bytes_written) {
  *bytes_written = 0;
  while (*bytes_written < bytes_to_write) {
    const uint8* chunk;
    size_t chunk_size;

    // Stop writing if there is no more data available.
    if (!client_buffer_->GetCurrentChunk(&chunk, &chunk_size))
      break;

    // Write data to stream.
    pa_stream_write(playback_handle_, chunk, chunk_size,
                    NULL, 0LL, PA_SEEK_RELATIVE);
    client_buffer_->Seek(chunk_size);
    *bytes_written += chunk_size;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  CHECK(callback);
  source_callback_ = callback;

  // Clear buffer, it might still have data in it.
  client_buffer_->Clear();
  stream_stopped_ = false;

  // Start playback.
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &PulseAudioOutputStream::WaitForWriteRequest,
      weak_factory_.GetWeakPtr()));
}

void PulseAudioOutputStream::Stop() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  stream_stopped_ = true;
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  *volume = volume_;
}

uint32 PulseAudioOutputStream::RunDataCallback(
    uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
  if (source_callback_)
    return source_callback_->OnMoreData(this, dest, max_size, buffers_state);

  return 0;
}
<commit_msg>fixing the compilation problem when enable pulse for chrome. One compiling error is "media/audio/linux/pulse_output.cc:347:40: error: ‘Bind’ is not a member of 'base'" Another is "media/audio/linux/pulse_output.cc:72: error: control reaches end of non-void function"<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/linux/pulse_output.h"

#include "base/bind.h"
#include "base/message_loop.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#include "media/audio/linux/audio_manager_linux.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"

static pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
  switch (bits_per_sample) {
    // Unsupported sample formats shown for reference.  I am assuming we want
    // signed and little endian because that is what we gave to ALSA.
    case 8:
      return PA_SAMPLE_U8;
      // Also 8-bits: PA_SAMPLE_ALAW and PA_SAMPLE_ULAW
    case 16:
      return PA_SAMPLE_S16LE;
      // Also 16-bits: PA_SAMPLE_S16BE (big endian).
    case 24:
      return PA_SAMPLE_S24LE;
      // Also 24-bits: PA_SAMPLE_S24BE (big endian).
      // Other cases: PA_SAMPLE_24_32LE (in LSB of 32-bit field, little endian),
      //          and PA_SAMPLE_24_32BE (in LSB of 32-bit field, big endian),
    case 32:
      return PA_SAMPLE_S32LE;
      // Also 32-bits: PA_SAMPLE_S32BE (big endian),
      //               PA_SAMPLE_FLOAT32LE (floating point little endian),
      //           and PA_SAMPLE_FLOAT32BE (floating point big endian).
    default:
      return PA_SAMPLE_INVALID;
  }
}

static pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
  switch (channel) {
    // PulseAudio does not differentiate between left/right and
    // stereo-left/stereo-right, both translate to front-left/front-right.
    case LEFT:
    case STEREO_LEFT:
      return PA_CHANNEL_POSITION_FRONT_LEFT;
    case RIGHT:
    case STEREO_RIGHT:
      return PA_CHANNEL_POSITION_FRONT_RIGHT;
    case CENTER:
      return PA_CHANNEL_POSITION_FRONT_CENTER;
    case LFE:
      return PA_CHANNEL_POSITION_LFE;
    case BACK_LEFT:
      return PA_CHANNEL_POSITION_REAR_LEFT;
    case BACK_RIGHT:
      return PA_CHANNEL_POSITION_REAR_RIGHT;
    case LEFT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
    case RIGHT_OF_CENTER:
      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
    case BACK_CENTER:
      return PA_CHANNEL_POSITION_REAR_CENTER;
    case SIDE_LEFT:
      return PA_CHANNEL_POSITION_SIDE_LEFT;
    case SIDE_RIGHT:
      return PA_CHANNEL_POSITION_SIDE_RIGHT;
    case CHANNELS_MAX:
      return PA_CHANNEL_POSITION_INVALID;
  }
  NOTREACHED() << "Invalid channel " << channel;
  return PA_CHANNEL_POSITION_INVALID;
}

static pa_channel_map ChannelLayoutToPAChannelMap(
    ChannelLayout channel_layout) {
  // Initialize channel map.
  pa_channel_map channel_map;
  pa_channel_map_init(&channel_map);

  channel_map.channels = ChannelLayoutToChannelCount(channel_layout);

  // All channel maps have the same size array of channel positions.
  for (unsigned int channel = 0; channel != CHANNELS_MAX; ++channel) {
    int channel_position = kChannelOrderings[channel_layout][channel];
    if (channel_position > -1) {
      channel_map.map[channel_position] = ChromiumToPAChannelPosition(
          static_cast<Channels>(channel));
    } else {
      // PulseAudio expects unused channels in channel maps to be filled with
      // PA_CHANNEL_POSITION_MONO.
      channel_map.map[channel_position] = PA_CHANNEL_POSITION_MONO;
    }
  }

  // Fill in the rest of the unused channels.
  for (unsigned int channel = CHANNELS_MAX; channel != PA_CHANNELS_MAX;
       ++channel) {
    channel_map.map[channel] = PA_CHANNEL_POSITION_MONO;
  }

  return channel_map;
}

static size_t MicrosecondsToBytes(
    uint32 microseconds, uint32 sample_rate, size_t bytes_per_frame) {
  return microseconds * sample_rate * bytes_per_frame /
      base::Time::kMicrosecondsPerSecond;
}

void PulseAudioOutputStream::ContextStateCallback(pa_context* context,
                                                  void* state_addr) {
  pa_context_state_t* state = static_cast<pa_context_state_t*>(state_addr);
  *state = pa_context_get_state(context);
}

void PulseAudioOutputStream::WriteRequestCallback(
    pa_stream* playback_handle, size_t length, void* stream_addr) {
  PulseAudioOutputStream* stream =
      static_cast<PulseAudioOutputStream*>(stream_addr);

  DCHECK_EQ(stream->message_loop_, MessageLoop::current());

  stream->write_callback_handled_ = true;

  // Fulfill write request.
  stream->FulfillWriteRequest(length);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerLinux* manager,
                                               MessageLoop* message_loop)
    : channel_layout_(params.channel_layout),
      channel_count_(ChannelLayoutToChannelCount(channel_layout_)),
      sample_format_(BitsToPASampleFormat(params.bits_per_sample)),
      sample_rate_(params.sample_rate),
      bytes_per_frame_(params.channels * params.bits_per_sample / 8),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      playback_handle_(NULL),
      packet_size_(params.GetPacketSize()),
      frames_per_packet_(packet_size_ / bytes_per_frame_),
      client_buffer_(NULL),
      volume_(1.0f),
      stream_stopped_(true),
      write_callback_handled_(false),
      message_loop_(message_loop),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
      source_callback_(NULL) {
  DCHECK_EQ(message_loop_, MessageLoop::current());
  DCHECK(manager_);

  // TODO(slock): Sanity check input values.
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(),
  // which calls AudioManagerLinux::Release which deletes this object.
  DCHECK(!playback_handle_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

bool PulseAudioOutputStream::Open() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  // TODO(slock): Possibly move most of this to an OpenPlaybackDevice function
  // in a new class 'pulse_util', like alsa_util.

  // Create a mainloop API and connect to the default server.
  pa_mainloop_ = pa_mainloop_new();
  pa_mainloop_api* pa_mainloop_api = pa_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  pa_context_state_t pa_context_state = PA_CONTEXT_UNCONNECTED;
  pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOFLAGS, NULL);

  // Wait until PulseAudio is ready.
  pa_context_set_state_callback(pa_context_, &ContextStateCallback,
                                &pa_context_state);
  while (pa_context_state != PA_CONTEXT_READY) {
    pa_mainloop_iterate(pa_mainloop_, 1, NULL);
    if (pa_context_state == PA_CONTEXT_FAILED ||
        pa_context_state == PA_CONTEXT_TERMINATED) {
      Reset();
      return false;
    }
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = sample_format_;
  pa_sample_specifications.rate = sample_rate_;
  pa_sample_specifications.channels = channel_count_;

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
      channel_layout_);
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  playback_handle_ = pa_stream_new(pa_context_, "Playback",
                                   &pa_sample_specifications, map);

  // Initialize client buffer.
  uint32 output_packet_size = frames_per_packet_ * bytes_per_frame_;
  client_buffer_.reset(new media::SeekableBuffer(0, output_packet_size));

  // Set write callback.
  pa_stream_set_write_callback(playback_handle_, &WriteRequestCallback, this);

  // Set server-side buffer attributes.
  // (uint32_t)-1 is the default and recommended value from PulseAudio's
  // documentation, found at:
  // http://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
  pa_buffer_attributes.tlength = output_packet_size;
  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
  pa_buffer_attributes.minreq = static_cast<uint32_t>(-1);
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.
  pa_stream_connect_playback(playback_handle_, NULL,
                             &pa_buffer_attributes,
                             (pa_stream_flags_t)
                             (PA_STREAM_INTERPOLATE_TIMING |
                             PA_STREAM_ADJUST_LATENCY |
                             PA_STREAM_AUTO_TIMING_UPDATE),
                             NULL, NULL);

  if (!playback_handle_) {
    Reset();
    return false;
  }

  return true;
}

void PulseAudioOutputStream::Reset() {
  stream_stopped_ = true;

  // Close the stream.
  if (playback_handle_) {
    pa_stream_flush(playback_handle_, NULL, NULL);
    pa_stream_disconnect(playback_handle_);

    // Release PulseAudio structures.
    pa_stream_unref(playback_handle_);
    playback_handle_ = NULL;
  }
  if (pa_context_) {
    pa_context_unref(pa_context_);
    pa_context_ = NULL;
  }
  if (pa_mainloop_) {
    pa_mainloop_free(pa_mainloop_);
    pa_mainloop_ = NULL;
  }

  // Release internal buffer.
  client_buffer_.reset();
}

void PulseAudioOutputStream::Close() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::WaitForWriteRequest() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  if (stream_stopped_)
    return;

  // Iterate the PulseAudio mainloop.  If PulseAudio doesn't request a write,
  // post a task to iterate the mainloop again.
  write_callback_handled_ = false;
  pa_mainloop_iterate(pa_mainloop_, 1, NULL);
  if (!write_callback_handled_) {
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

bool PulseAudioOutputStream::BufferPacketFromSource() {
  uint32 buffer_delay = client_buffer_->forward_bytes();
  pa_usec_t pa_latency_micros;
  int negative;
  pa_stream_get_latency(playback_handle_, &pa_latency_micros, &negative);
  uint32 hardware_delay = MicrosecondsToBytes(pa_latency_micros,
                                              sample_rate_,
                                              bytes_per_frame_);
  // TODO(slock): Deal with negative latency (negative == 1).  This has yet
  // to happen in practice though.
  scoped_refptr<media::DataBuffer> packet =
      new media::DataBuffer(packet_size_);
  size_t packet_size = RunDataCallback(packet->GetWritableData(),
                                       packet->GetBufferSize(),
                                       AudioBuffersState(buffer_delay,
                                                         hardware_delay));

  if (packet_size == 0)
    return false;

  media::AdjustVolume(packet->GetWritableData(),
                      packet_size,
                      channel_count_,
                      bytes_per_frame_ / channel_count_,
                      volume_);
  packet->SetDataSize(packet_size);
  // Add the packet to the buffer.
  client_buffer_->Append(packet);
  return true;
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  // If we have enough data to fulfill the request, we can finish the write.
  if (stream_stopped_)
    return;

  // Request more data from the source until we can fulfill the request or
  // fail to receive anymore data.
  bool buffering_successful = true;
  while (client_buffer_->forward_bytes() < requested_bytes &&
         buffering_successful) {
    buffering_successful = BufferPacketFromSource();
  }

  size_t bytes_written = 0;
  if (client_buffer_->forward_bytes() > 0) {
    // Try to fulfill the request by writing as many of the requested bytes to
    // the stream as we can.
    WriteToStream(requested_bytes, &bytes_written);
  }

  if (bytes_written < requested_bytes) {
    // We weren't able to buffer enough data to fulfill the request.  Try to
    // fulfill the rest of the request later.
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::FulfillWriteRequest,
        weak_factory_.GetWeakPtr(),
        requested_bytes - bytes_written));
  } else {
    // Continue playback.
    message_loop_->PostTask(FROM_HERE, base::Bind(
        &PulseAudioOutputStream::WaitForWriteRequest,
        weak_factory_.GetWeakPtr()));
  }
}

void PulseAudioOutputStream::WriteToStream(size_t bytes_to_write,
                                           size_t* bytes_written) {
  *bytes_written = 0;
  while (*bytes_written < bytes_to_write) {
    const uint8* chunk;
    size_t chunk_size;

    // Stop writing if there is no more data available.
    if (!client_buffer_->GetCurrentChunk(&chunk, &chunk_size))
      break;

    // Write data to stream.
    pa_stream_write(playback_handle_, chunk, chunk_size,
                    NULL, 0LL, PA_SEEK_RELATIVE);
    client_buffer_->Seek(chunk_size);
    *bytes_written += chunk_size;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  CHECK(callback);
  source_callback_ = callback;

  // Clear buffer, it might still have data in it.
  client_buffer_->Clear();
  stream_stopped_ = false;

  // Start playback.
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &PulseAudioOutputStream::WaitForWriteRequest,
      weak_factory_.GetWeakPtr()));
}

void PulseAudioOutputStream::Stop() {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  stream_stopped_ = true;
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK_EQ(message_loop_, MessageLoop::current());

  *volume = volume_;
}

uint32 PulseAudioOutputStream::RunDataCallback(
    uint8* dest, uint32 max_size, AudioBuffersState buffers_state) {
  if (source_callback_)
    return source_callback_->OnMoreData(this, dest, max_size, buffers_state);

  return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "media/filters/adaptive_demuxer.h"

namespace media {

//
// AdaptiveDemuxerStream
//
AdaptiveDemuxerStream::AdaptiveDemuxerStream(
    StreamVector const& streams, int initial_stream)
    : streams_(streams), current_stream_index_(initial_stream),
      bitstream_converter_enabled_(false) {
  DCheckSanity();
}

void AdaptiveDemuxerStream::DCheckSanity() {
  // We only carry out sanity checks in debug mode.
  if (!logging::DEBUG_MODE)
    return;
  DCHECK(streams_[current_stream_index_].get());

  bool non_null_stream_seen = false;
  Type type;
  const MediaFormat* media_format = NULL;
  for (size_t i = 0; i < streams_.size(); ++i) {
    if (!streams_[i])
      continue;

    if (!non_null_stream_seen) {
      non_null_stream_seen = true;
      type = streams_[i]->type();
      media_format = &streams_[i]->media_format();
    } else {
      DCHECK_EQ(streams_[i]->type(), type);
      DCHECK(streams_[i]->media_format() == *media_format);
    }
  }
}

AdaptiveDemuxerStream::~AdaptiveDemuxerStream() {
  base::AutoLock auto_lock(lock_);
  current_stream_index_ = -1;
  streams_.clear();
}

DemuxerStream* AdaptiveDemuxerStream::current_stream() {
  base::AutoLock auto_lock(lock_);
  return streams_[current_stream_index_];
}

void AdaptiveDemuxerStream::Read(Callback1<Buffer*>::Type* read_callback) {
  current_stream()->Read(read_callback);
}

AVStream* AdaptiveDemuxerStream::GetAVStream() {
  return current_stream()->GetAVStream();
}

DemuxerStream::Type AdaptiveDemuxerStream::type() {
  return current_stream()->type();
}

const MediaFormat& AdaptiveDemuxerStream::media_format() {
  return current_stream()->media_format();
}

void AdaptiveDemuxerStream::EnableBitstreamConverter() {
  {
    base::AutoLock auto_lock(lock_);
    bitstream_converter_enabled_ = true;
  }
  current_stream()->EnableBitstreamConverter();
}

void AdaptiveDemuxerStream::ChangeCurrentStream(int index) {
  bool needs_bitstream_converter_enabled;
  {
    base::AutoLock auto_lock(lock_);
    current_stream_index_ = index;
    DCHECK(streams_[current_stream_index_]);
    needs_bitstream_converter_enabled = bitstream_converter_enabled_;
  }
  if (needs_bitstream_converter_enabled)
    EnableBitstreamConverter();
}

//
// AdaptiveDemuxer
//

AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers,
                                 int initial_audio_demuxer_index,
                                 int initial_video_demuxer_index)
    : demuxers_(demuxers),
      current_audio_demuxer_index_(initial_audio_demuxer_index),
      current_video_demuxer_index_(initial_video_demuxer_index) {
  DCHECK(!demuxers_.empty());
  DCHECK_GE(current_audio_demuxer_index_, -1);
  DCHECK_GE(current_video_demuxer_index_, -1);
  DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size()));
  DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size()));
  DCHECK(current_audio_demuxer_index_ != -1 ||
         current_video_demuxer_index_ != -1);
  AdaptiveDemuxerStream::StreamVector audio_streams, video_streams;
  for (size_t i = 0; i < demuxers_.size(); ++i) {
    audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO));
    video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO));
  }
  if (current_audio_demuxer_index_ >= 0) {
    audio_stream_ = new AdaptiveDemuxerStream(
        audio_streams, current_audio_demuxer_index_);
  }
  if (current_video_demuxer_index_ >= 0) {
    video_stream_ = new AdaptiveDemuxerStream(
        video_streams, current_video_demuxer_index_);
  }

  // TODO(fischman): any streams in the underlying demuxers that aren't being
  // consumed currently need to be sent to /dev/null or else FFmpegDemuxer will
  // hold data for them in memory, waiting for them to get drained by a
  // non-existent decoder.
}

AdaptiveDemuxer::~AdaptiveDemuxer() {}

void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) {
  // TODO(fischman): this is currently broken because when a new Demuxer is to
  // be used we need to set_host(host()) it, and we need to set_host(NULL) the
  // current Demuxer if it's no longer being used.
  // TODO(fischman): remember to Stop active demuxers that are being abandoned.
  base::AutoLock auto_lock(lock_);
  current_audio_demuxer_index_ = audio_index;
  current_video_demuxer_index_ = video_index;
  if (audio_stream_)
    audio_stream_->ChangeCurrentStream(audio_index);
  if (video_stream_)
    video_stream_->ChangeCurrentStream(video_index);
}

Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) {
  base::AutoLock auto_lock(lock_);
  switch (type) {
    case DemuxerStream::AUDIO:
      return (current_audio_demuxer_index_ < 0) ? NULL :
          demuxers_[current_audio_demuxer_index_];
    case DemuxerStream::VIDEO:
      return (current_video_demuxer_index_ < 0) ? NULL :
          demuxers_[current_video_demuxer_index_];
    default:
      LOG(DFATAL) << "Unexpected type: " << type;
      return NULL;
  }
}

// Helper class that wraps a FilterCallback and expects to get called a set
// number of times, after which the wrapped callback is fired (and deleted).
class CountingCallback {
 public:
  CountingCallback(int count, FilterCallback* orig_cb)
      : remaining_count_(count), orig_cb_(orig_cb) {
    DCHECK_GT(remaining_count_, 0);
    DCHECK(orig_cb);
  }

  FilterCallback* GetACallback() {
    return NewCallback(this, &CountingCallback::OnChildCallbackDone);
  }

 private:
  void OnChildCallbackDone() {
    bool fire_orig_cb = false;
    {
      base::AutoLock auto_lock(lock_);
      if (--remaining_count_ == 0)
        fire_orig_cb = true;
    }
    if (fire_orig_cb) {
      orig_cb_->Run();
      delete this;
    }
  }

  base::Lock lock_;
  int remaining_count_;
  scoped_ptr<FilterCallback> orig_cb_;
};

void AdaptiveDemuxer::Stop(FilterCallback* callback) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
  CountingCallback* wrapper = new CountingCallback(count, callback);
  if (audio)
    audio->Stop(wrapper->GetACallback());
  if (video && audio != video)
    video->Stop(wrapper->GetACallback());
}

void AdaptiveDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
  CountingCallback* wrapper = new CountingCallback(count, callback);
  if (audio)
    audio->Seek(time, wrapper->GetACallback());
  if (video && audio != video)
    video->Seek(time, wrapper->GetACallback());
}

void AdaptiveDemuxer::OnAudioRendererDisabled() {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  if (audio) audio->OnAudioRendererDisabled();
  if (video && audio != video) video->OnAudioRendererDisabled();
  // TODO(fischman): propagate to other demuxers if/when they're selected.
}

void AdaptiveDemuxer::set_host(FilterHost* filter_host) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  if (audio) audio->set_host(filter_host);
  if (video && audio != video) video->set_host(filter_host);
}

scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream(
    DemuxerStream::Type type) {
  switch (type) {
    case DemuxerStream::AUDIO:
      return audio_stream_;
    case DemuxerStream::VIDEO:
      return video_stream_;
    default:
      LOG(DFATAL) << "Unexpected type " << type;
      return NULL;
  }
}

//
// AdaptiveDemuxerFactory
//

AdaptiveDemuxerFactory::AdaptiveDemuxerFactory(
    DemuxerFactory* delegate_factory)
    : delegate_factory_(delegate_factory) {
  DCHECK(delegate_factory);
}

AdaptiveDemuxerFactory::~AdaptiveDemuxerFactory() {}

DemuxerFactory* AdaptiveDemuxerFactory::Clone() const {
  return new AdaptiveDemuxerFactory(delegate_factory_->Clone());
}

// See AdaptiveDemuxerFactory's class-level comment for |url|'s format.
bool ParseAdaptiveUrl(
    const std::string& url, int* audio_index, int* video_index,
    std::vector<std::string>* urls) {
  urls->clear();

  if (url.empty())
    return false;
  if (!StartsWithASCII(url, "x-adaptive:", false)) {
    return ParseAdaptiveUrl(
        "x-adaptive:0:0:" + url, audio_index, video_index, urls);
  }

  std::vector<std::string> parts;
  base::SplitStringDontTrim(url, ':', &parts);
  if (parts.size() < 4 ||
      parts[0] != "x-adaptive" ||
      !base::StringToInt(parts[1], audio_index) ||
      !base::StringToInt(parts[2], video_index) ||
      *audio_index < -1 || *video_index < -1) {
    return false;
  }

  std::string::size_type first_url_pos =
      parts[0].size() + 1 + parts[1].size() + 1 + parts[2].size() + 1;
  std::string urls_str = url.substr(first_url_pos);
  if (urls_str.empty())
    return false;
  base::SplitStringDontTrim(urls_str, '^', urls);
  if (urls->empty() ||
      *audio_index >= static_cast<int>(urls->size()) ||
      *video_index >= static_cast<int>(urls->size())) {
    return false;
  }

  return true;
}

// Wrapper for a BuildCallback which accumulates the Demuxer's returned by a
// number of DemuxerFactory::Build() calls and eventually constructs an
// AdaptiveDemuxer and returns it to the |orig_cb| (or errors out if any
// individual Demuxer fails construction).
class DemuxerAccumulator {
 public:
  // Takes ownership of |orig_cb|.
  DemuxerAccumulator(int audio_index, int video_index,
                     int count, DemuxerFactory::BuildCallback* orig_cb)
      : audio_index_(audio_index), video_index_(video_index),
        remaining_count_(count), orig_cb_(orig_cb),
        demuxers_(count, static_cast<Demuxer*>(NULL)),
        statuses_(count, PIPELINE_OK) {
    DCHECK_GT(remaining_count_, 0);
    DCHECK(orig_cb_.get());
  }

  DemuxerFactory::BuildCallback* GetNthCallback(int n) {
    return new IndividualCallback(this, n);
  }

 private:
  // Wrapper for a BuildCallback that can carry one extra piece of data: the
  // index of this callback in the original list of outstanding requests.
  struct IndividualCallback : public DemuxerFactory::BuildCallback {
    IndividualCallback(DemuxerAccumulator* accumulator, int index)
        : accumulator_(accumulator), index_(index) {}

    virtual void RunWithParams(const Tuple2<PipelineStatus, Demuxer*>& params) {
      accumulator_->Run(index_, params.a, params.b);
    }
    DemuxerAccumulator* accumulator_;
    int index_;
  };

  // When an individual callback is fired, it calls this method.
  void Run(int index, PipelineStatus status, Demuxer* demuxer) {
    bool fire_orig_cb = false;
    {
      base::AutoLock auto_lock(lock_);
      DCHECK(!demuxers_[index]);
      demuxers_[index] = demuxer;
      statuses_[index] = status;
      if (--remaining_count_ == 0)
        fire_orig_cb = true;
    }
    if (fire_orig_cb)
      DoneAccumulating();
  }

  void DoneAccumulating() {
    PipelineStatus overall_status = PIPELINE_OK;
    for (size_t i = 0; i < statuses_.size(); ++i) {
      if (statuses_[i] != PIPELINE_OK) {
        overall_status = statuses_[i];
        break;
      }
    }
    if (overall_status == PIPELINE_OK) {
      orig_cb_->Run(
          PIPELINE_OK,
          new AdaptiveDemuxer(demuxers_, audio_index_, video_index_));
    } else {
      orig_cb_->Run(overall_status, static_cast<Demuxer*>(NULL));
    }

    delete this;
  }

  // Self-delete in DoneAccumulating() only.
  ~DemuxerAccumulator() {}

  int audio_index_;
  int video_index_;
  int remaining_count_;
  scoped_ptr<DemuxerFactory::BuildCallback> orig_cb_;
  base::Lock lock_;  // Guards vectors of results below.
  AdaptiveDemuxer::DemuxerVector demuxers_;
  std::vector<PipelineStatus> statuses_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(DemuxerAccumulator);
};

void AdaptiveDemuxerFactory::Build(const std::string& url, BuildCallback* cb) {
  std::vector<std::string> urls;
  int audio_index, video_index;
  if (!ParseAdaptiveUrl(url, &audio_index, &video_index, &urls)) {
    cb->Run(Tuple2<PipelineStatus, Demuxer*>(
        DEMUXER_ERROR_COULD_NOT_OPEN, NULL));
    delete cb;
    return;
  }
  DemuxerAccumulator* accumulator = new DemuxerAccumulator(
      audio_index, video_index, urls.size(), cb);
  for (size_t i = 0; i < urls.size(); ++i)
    delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i));
}

}  // namespace media
<commit_msg>Fix an uninitialized variable compiler warning on some systems. BUG=none TEST=none Review URL: http://codereview.chromium.org/6677123<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "media/filters/adaptive_demuxer.h"

namespace media {

//
// AdaptiveDemuxerStream
//
AdaptiveDemuxerStream::AdaptiveDemuxerStream(
    StreamVector const& streams, int initial_stream)
    : streams_(streams), current_stream_index_(initial_stream),
      bitstream_converter_enabled_(false) {
  DCheckSanity();
}

void AdaptiveDemuxerStream::DCheckSanity() {
  // We only carry out sanity checks in debug mode.
  if (!logging::DEBUG_MODE)
    return;
  DCHECK(streams_[current_stream_index_].get());

  bool non_null_stream_seen = false;
  Type type = DemuxerStream::UNKNOWN;
  const MediaFormat* media_format = NULL;
  for (size_t i = 0; i < streams_.size(); ++i) {
    if (!streams_[i])
      continue;

    if (!non_null_stream_seen) {
      non_null_stream_seen = true;
      type = streams_[i]->type();
      media_format = &streams_[i]->media_format();
    } else {
      DCHECK_EQ(streams_[i]->type(), type);
      DCHECK(streams_[i]->media_format() == *media_format);
    }
  }
}

AdaptiveDemuxerStream::~AdaptiveDemuxerStream() {
  base::AutoLock auto_lock(lock_);
  current_stream_index_ = -1;
  streams_.clear();
}

DemuxerStream* AdaptiveDemuxerStream::current_stream() {
  base::AutoLock auto_lock(lock_);
  return streams_[current_stream_index_];
}

void AdaptiveDemuxerStream::Read(Callback1<Buffer*>::Type* read_callback) {
  current_stream()->Read(read_callback);
}

AVStream* AdaptiveDemuxerStream::GetAVStream() {
  return current_stream()->GetAVStream();
}

DemuxerStream::Type AdaptiveDemuxerStream::type() {
  return current_stream()->type();
}

const MediaFormat& AdaptiveDemuxerStream::media_format() {
  return current_stream()->media_format();
}

void AdaptiveDemuxerStream::EnableBitstreamConverter() {
  {
    base::AutoLock auto_lock(lock_);
    bitstream_converter_enabled_ = true;
  }
  current_stream()->EnableBitstreamConverter();
}

void AdaptiveDemuxerStream::ChangeCurrentStream(int index) {
  bool needs_bitstream_converter_enabled;
  {
    base::AutoLock auto_lock(lock_);
    current_stream_index_ = index;
    DCHECK(streams_[current_stream_index_]);
    needs_bitstream_converter_enabled = bitstream_converter_enabled_;
  }
  if (needs_bitstream_converter_enabled)
    EnableBitstreamConverter();
}

//
// AdaptiveDemuxer
//

AdaptiveDemuxer::AdaptiveDemuxer(DemuxerVector const& demuxers,
                                 int initial_audio_demuxer_index,
                                 int initial_video_demuxer_index)
    : demuxers_(demuxers),
      current_audio_demuxer_index_(initial_audio_demuxer_index),
      current_video_demuxer_index_(initial_video_demuxer_index) {
  DCHECK(!demuxers_.empty());
  DCHECK_GE(current_audio_demuxer_index_, -1);
  DCHECK_GE(current_video_demuxer_index_, -1);
  DCHECK_LT(current_audio_demuxer_index_, static_cast<int>(demuxers_.size()));
  DCHECK_LT(current_video_demuxer_index_, static_cast<int>(demuxers_.size()));
  DCHECK(current_audio_demuxer_index_ != -1 ||
         current_video_demuxer_index_ != -1);
  AdaptiveDemuxerStream::StreamVector audio_streams, video_streams;
  for (size_t i = 0; i < demuxers_.size(); ++i) {
    audio_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::AUDIO));
    video_streams.push_back(demuxers_[i]->GetStream(DemuxerStream::VIDEO));
  }
  if (current_audio_demuxer_index_ >= 0) {
    audio_stream_ = new AdaptiveDemuxerStream(
        audio_streams, current_audio_demuxer_index_);
  }
  if (current_video_demuxer_index_ >= 0) {
    video_stream_ = new AdaptiveDemuxerStream(
        video_streams, current_video_demuxer_index_);
  }

  // TODO(fischman): any streams in the underlying demuxers that aren't being
  // consumed currently need to be sent to /dev/null or else FFmpegDemuxer will
  // hold data for them in memory, waiting for them to get drained by a
  // non-existent decoder.
}

AdaptiveDemuxer::~AdaptiveDemuxer() {}

void AdaptiveDemuxer::ChangeCurrentDemuxer(int audio_index, int video_index) {
  // TODO(fischman): this is currently broken because when a new Demuxer is to
  // be used we need to set_host(host()) it, and we need to set_host(NULL) the
  // current Demuxer if it's no longer being used.
  // TODO(fischman): remember to Stop active demuxers that are being abandoned.
  base::AutoLock auto_lock(lock_);
  current_audio_demuxer_index_ = audio_index;
  current_video_demuxer_index_ = video_index;
  if (audio_stream_)
    audio_stream_->ChangeCurrentStream(audio_index);
  if (video_stream_)
    video_stream_->ChangeCurrentStream(video_index);
}

Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) {
  base::AutoLock auto_lock(lock_);
  switch (type) {
    case DemuxerStream::AUDIO:
      return (current_audio_demuxer_index_ < 0) ? NULL :
          demuxers_[current_audio_demuxer_index_];
    case DemuxerStream::VIDEO:
      return (current_video_demuxer_index_ < 0) ? NULL :
          demuxers_[current_video_demuxer_index_];
    default:
      LOG(DFATAL) << "Unexpected type: " << type;
      return NULL;
  }
}

// Helper class that wraps a FilterCallback and expects to get called a set
// number of times, after which the wrapped callback is fired (and deleted).
class CountingCallback {
 public:
  CountingCallback(int count, FilterCallback* orig_cb)
      : remaining_count_(count), orig_cb_(orig_cb) {
    DCHECK_GT(remaining_count_, 0);
    DCHECK(orig_cb);
  }

  FilterCallback* GetACallback() {
    return NewCallback(this, &CountingCallback::OnChildCallbackDone);
  }

 private:
  void OnChildCallbackDone() {
    bool fire_orig_cb = false;
    {
      base::AutoLock auto_lock(lock_);
      if (--remaining_count_ == 0)
        fire_orig_cb = true;
    }
    if (fire_orig_cb) {
      orig_cb_->Run();
      delete this;
    }
  }

  base::Lock lock_;
  int remaining_count_;
  scoped_ptr<FilterCallback> orig_cb_;
};

void AdaptiveDemuxer::Stop(FilterCallback* callback) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
  CountingCallback* wrapper = new CountingCallback(count, callback);
  if (audio)
    audio->Stop(wrapper->GetACallback());
  if (video && audio != video)
    video->Stop(wrapper->GetACallback());
}

void AdaptiveDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0);
  CountingCallback* wrapper = new CountingCallback(count, callback);
  if (audio)
    audio->Seek(time, wrapper->GetACallback());
  if (video && audio != video)
    video->Seek(time, wrapper->GetACallback());
}

void AdaptiveDemuxer::OnAudioRendererDisabled() {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  if (audio) audio->OnAudioRendererDisabled();
  if (video && audio != video) video->OnAudioRendererDisabled();
  // TODO(fischman): propagate to other demuxers if/when they're selected.
}

void AdaptiveDemuxer::set_host(FilterHost* filter_host) {
  Demuxer* audio = current_demuxer(DemuxerStream::AUDIO);
  Demuxer* video = current_demuxer(DemuxerStream::VIDEO);
  if (audio) audio->set_host(filter_host);
  if (video && audio != video) video->set_host(filter_host);
}

scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream(
    DemuxerStream::Type type) {
  switch (type) {
    case DemuxerStream::AUDIO:
      return audio_stream_;
    case DemuxerStream::VIDEO:
      return video_stream_;
    default:
      LOG(DFATAL) << "Unexpected type " << type;
      return NULL;
  }
}

//
// AdaptiveDemuxerFactory
//

AdaptiveDemuxerFactory::AdaptiveDemuxerFactory(
    DemuxerFactory* delegate_factory)
    : delegate_factory_(delegate_factory) {
  DCHECK(delegate_factory);
}

AdaptiveDemuxerFactory::~AdaptiveDemuxerFactory() {}

DemuxerFactory* AdaptiveDemuxerFactory::Clone() const {
  return new AdaptiveDemuxerFactory(delegate_factory_->Clone());
}

// See AdaptiveDemuxerFactory's class-level comment for |url|'s format.
bool ParseAdaptiveUrl(
    const std::string& url, int* audio_index, int* video_index,
    std::vector<std::string>* urls) {
  urls->clear();

  if (url.empty())
    return false;
  if (!StartsWithASCII(url, "x-adaptive:", false)) {
    return ParseAdaptiveUrl(
        "x-adaptive:0:0:" + url, audio_index, video_index, urls);
  }

  std::vector<std::string> parts;
  base::SplitStringDontTrim(url, ':', &parts);
  if (parts.size() < 4 ||
      parts[0] != "x-adaptive" ||
      !base::StringToInt(parts[1], audio_index) ||
      !base::StringToInt(parts[2], video_index) ||
      *audio_index < -1 || *video_index < -1) {
    return false;
  }

  std::string::size_type first_url_pos =
      parts[0].size() + 1 + parts[1].size() + 1 + parts[2].size() + 1;
  std::string urls_str = url.substr(first_url_pos);
  if (urls_str.empty())
    return false;
  base::SplitStringDontTrim(urls_str, '^', urls);
  if (urls->empty() ||
      *audio_index >= static_cast<int>(urls->size()) ||
      *video_index >= static_cast<int>(urls->size())) {
    return false;
  }

  return true;
}

// Wrapper for a BuildCallback which accumulates the Demuxer's returned by a
// number of DemuxerFactory::Build() calls and eventually constructs an
// AdaptiveDemuxer and returns it to the |orig_cb| (or errors out if any
// individual Demuxer fails construction).
class DemuxerAccumulator {
 public:
  // Takes ownership of |orig_cb|.
  DemuxerAccumulator(int audio_index, int video_index,
                     int count, DemuxerFactory::BuildCallback* orig_cb)
      : audio_index_(audio_index), video_index_(video_index),
        remaining_count_(count), orig_cb_(orig_cb),
        demuxers_(count, static_cast<Demuxer*>(NULL)),
        statuses_(count, PIPELINE_OK) {
    DCHECK_GT(remaining_count_, 0);
    DCHECK(orig_cb_.get());
  }

  DemuxerFactory::BuildCallback* GetNthCallback(int n) {
    return new IndividualCallback(this, n);
  }

 private:
  // Wrapper for a BuildCallback that can carry one extra piece of data: the
  // index of this callback in the original list of outstanding requests.
  struct IndividualCallback : public DemuxerFactory::BuildCallback {
    IndividualCallback(DemuxerAccumulator* accumulator, int index)
        : accumulator_(accumulator), index_(index) {}

    virtual void RunWithParams(const Tuple2<PipelineStatus, Demuxer*>& params) {
      accumulator_->Run(index_, params.a, params.b);
    }
    DemuxerAccumulator* accumulator_;
    int index_;
  };

  // When an individual callback is fired, it calls this method.
  void Run(int index, PipelineStatus status, Demuxer* demuxer) {
    bool fire_orig_cb = false;
    {
      base::AutoLock auto_lock(lock_);
      DCHECK(!demuxers_[index]);
      demuxers_[index] = demuxer;
      statuses_[index] = status;
      if (--remaining_count_ == 0)
        fire_orig_cb = true;
    }
    if (fire_orig_cb)
      DoneAccumulating();
  }

  void DoneAccumulating() {
    PipelineStatus overall_status = PIPELINE_OK;
    for (size_t i = 0; i < statuses_.size(); ++i) {
      if (statuses_[i] != PIPELINE_OK) {
        overall_status = statuses_[i];
        break;
      }
    }
    if (overall_status == PIPELINE_OK) {
      orig_cb_->Run(
          PIPELINE_OK,
          new AdaptiveDemuxer(demuxers_, audio_index_, video_index_));
    } else {
      orig_cb_->Run(overall_status, static_cast<Demuxer*>(NULL));
    }

    delete this;
  }

  // Self-delete in DoneAccumulating() only.
  ~DemuxerAccumulator() {}

  int audio_index_;
  int video_index_;
  int remaining_count_;
  scoped_ptr<DemuxerFactory::BuildCallback> orig_cb_;
  base::Lock lock_;  // Guards vectors of results below.
  AdaptiveDemuxer::DemuxerVector demuxers_;
  std::vector<PipelineStatus> statuses_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(DemuxerAccumulator);
};

void AdaptiveDemuxerFactory::Build(const std::string& url, BuildCallback* cb) {
  std::vector<std::string> urls;
  int audio_index, video_index;
  if (!ParseAdaptiveUrl(url, &audio_index, &video_index, &urls)) {
    cb->Run(Tuple2<PipelineStatus, Demuxer*>(
        DEMUXER_ERROR_COULD_NOT_OPEN, NULL));
    delete cb;
    return;
  }
  DemuxerAccumulator* accumulator = new DemuxerAccumulator(
      audio_index, video_index, urls.size(), cb);
  for (size_t i = 0; i < urls.size(); ++i)
    delegate_factory_->Build(urls[i], accumulator->GetNthCallback(i));
}

}  // namespace media
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: gcach_ftyp.hxx,v $
 *
 *  $Revision: 1.39 $
 *
 *  last change: $Author: hr $ $Date: 2007-07-31 16:09:30 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_GCACHFTYP_HXX
#define _SV_GCACHFTYP_HXX

#include <vcl/glyphcache.hxx>
#include <rtl/textcvt.h>

#include <ft2build.h>
#include FT_FREETYPE_H
class FreetypeServerFont;
struct FT_GlyphRec_;

// -----------------------------------------------------------------------

// FtFontFile has the responsibility that a font file is only mapped once.
// (#86621#) the old directly ft-managed solution caused it to be mapped
// in up to nTTC*nSizes*nOrientation*nSynthetic times
class FtFontFile
{
public:
    static FtFontFile*      FindFontFile( const ::rtl::OString& rNativeFileName );

    bool                    Map();
    void                    Unmap();

    const unsigned char*    GetBuffer() const { return mpFileMap; }
    int                     GetFileSize() const { return mnFileSize; }
    const ::rtl::OString*   GetFileName() const { return &maNativeFileName; }
    int                     GetLangBoost() const { return mnLangBoost; }

private:
                            FtFontFile( const ::rtl::OString& rNativeFileName );

    const ::rtl::OString    maNativeFileName;
    const unsigned char*    mpFileMap;
    int                     mnFileSize;
    int                     mnRefCount;
    int                     mnLangBoost;
};

// -----------------------------------------------------------------------

// FtFontInfo corresponds to an unscaled font face
class FtFontInfo
{
public:
                           FtFontInfo( const ImplDevFontAttributes&,
                               const ::rtl::OString& rNativeFileName,
                               int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
                                const ExtraKernInfo* );
                          ~FtFontInfo();

    const unsigned char*  GetTable( const char*, ULONG* pLength=0 ) const;

    FT_FaceRec_*          GetFaceFT();
    void                  ReleaseFaceFT( FT_FaceRec_* );

    const ::rtl::OString* GetFontFileName() const   { return mpFontFile->GetFileName(); }
    int                   GetFaceNum() const        { return mnFaceNum; }
    int                   GetSynthetic() const      { return mnSynthetic; }
    sal_IntPtr            GetFontId() const         { return mnFontId; }
    bool                  DontUseAntiAlias() const
        { return maDevFontAttributes.UseAntiAlias() == ANTIALIAS_FALSE; }
    bool                  DontUseEmbeddedBitmaps() const
        { return maDevFontAttributes.UseEmbeddedBitmap() == EMBEDDEDBITMAP_FALSE; }
    bool                  IsSymbolFont() const      { return maDevFontAttributes.IsSymbolFont(); }
    const ImplFontAttributes& GetFontAttributes() const { return maDevFontAttributes; }

    void                  AnnounceFont( ImplDevFontList* );

    int                   GetGlyphIndex( sal_UCS4 cChar ) const;
    void                  CacheGlyphIndex( sal_UCS4 cChar, int nGI ) const;

    bool                  HasExtraKerning() const;
    int                   GetExtraKernPairs( ImplKernPairData** ) const;
    int                   GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;

private:
    FT_FaceRec_*    maFaceFT;
    FtFontFile*     mpFontFile;
    const int       mnFaceNum;
    int             mnRefCount;
    const int       mnSynthetic;

    sal_IntPtr      mnFontId;
    ImplDevFontAttributes maDevFontAttributes;

    // cache unicode->glyphid mapping because looking it up is expensive
    // TODO: change to hash_multimap when a use case requires a m:n mapping
    typedef ::std::hash_map<int,int> Int2IntMap;
    mutable Int2IntMap* mpChar2Glyph;
    mutable Int2IntMap* mpGlyph2Char;
    void InitHashes() const;

    const ExtraKernInfo* mpExtraKernInfo;
};

// these two inlines are very important for performance

inline int FtFontInfo::GetGlyphIndex( sal_UCS4 cChar ) const
{
    if( !mpChar2Glyph )
        return -1;
    Int2IntMap::const_iterator it = mpChar2Glyph->find( cChar );
    if( it == mpChar2Glyph->end() )
        return -1;
    return it->second;
}

inline void FtFontInfo::CacheGlyphIndex( sal_UCS4 cChar, int nIndex ) const
{
    if( !mpChar2Glyph )
        InitHashes();
    (*mpChar2Glyph)[ cChar ] = nIndex;
    (*mpGlyph2Char)[ nIndex ] = cChar;
}

// -----------------------------------------------------------------------

class FreetypeManager
{
public:
                        FreetypeManager();
                        ~FreetypeManager();

    long                AddFontDir( const String& rUrlName );
    void                AddFontFile( const rtl::OString& rNormalizedName,
                            int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes&,
                            const ExtraKernInfo* );
    void                AnnounceFonts( ImplDevFontList* ) const;
    void                ClearFontList();

    FreetypeServerFont* CreateFont( const ImplFontSelectData& );

private:
    typedef ::std::hash_map<sal_IntPtr,FtFontInfo*> FontList;
    FontList            maFontList;

    sal_IntPtr          mnMaxFontId;
    sal_IntPtr          mnNextFontId;
};

// -----------------------------------------------------------------------

class FreetypeServerFont : public ServerFont
{
public:
                                FreetypeServerFont( const ImplFontSelectData&, FtFontInfo* );
    virtual                     ~FreetypeServerFont();

    virtual const ::rtl::OString* GetFontFileName() const { return mpFontInfo->GetFontFileName(); }
    virtual int                 GetFontFaceNum() const { return mpFontInfo->GetFaceNum(); }
    virtual bool                TestFont() const;

    virtual void                FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;

    virtual int                 GetGlyphIndex( sal_UCS4 ) const;
    int                         GetRawGlyphIndex( sal_UCS4 ) const;
    int                         FixupGlyphIndex( int nGlyphIndex, sal_UCS4 ) const;

    virtual bool                GetAntialiasAdvice( void ) const;
    virtual bool                GetGlyphBitmap1( int nGlyphIndex, RawBitmap& ) const;
    virtual bool                GetGlyphBitmap8( int nGlyphIndex, RawBitmap& ) const;
    virtual bool                GetGlyphOutline( int nGlyphIndex, ::basegfx::B2DPolyPolygon& ) const;
    virtual int                 GetGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;
    virtual ULONG               GetKernPairs( ImplKernPairData** ) const;

    const unsigned char*        GetTable( const char* pName, ULONG* pLength )
                                { return mpFontInfo->GetTable( pName, pLength ); }
    int                         GetEmUnits() const;
    const FT_Size_Metrics&      GetMetricsFT() const { return maSizeFT->metrics; }

protected:
    friend class GlyphCache;

    int                         ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
    virtual void                InitGlyphData( int nGlyphIndex, GlyphData& ) const;
    virtual ULONG               GetFontCodeRanges( sal_uInt32* pCodes ) const;
    bool                        ApplyGSUB( const ImplFontSelectData& );
    virtual ServerFontLayoutEngine* GetLayoutEngine();

private:
    int                         mnWidth;
    int                         mnPrioEmbedded;
    int                         mnPrioAntiAlias;
    FtFontInfo*                 mpFontInfo;
    FT_Int                      mnLoadFlags;
    double                      mfStretch;
    FT_FaceRec_*                maFaceFT;
    FT_SizeRec_*                maSizeFT;

    bool                        mbFaceOk;
    bool            mbArtItalic;
    bool            mbArtBold;
    bool            mbUseGamma;

    typedef ::std::hash_map<int,int> GlyphSubstitution;
    GlyphSubstitution           maGlyphSubstitution;
    rtl_UnicodeToTextConverter  maRecodeConverter;

    ServerFontLayoutEngine*     mpLayoutEngine;
};

// -----------------------------------------------------------------------

class ImplFTSFontData : public ImplFontData
{
private:
    FtFontInfo*             mpFtFontInfo;
    enum { IFTSFONT_MAGIC = 0x1F150A1C };

public:
                            ImplFTSFontData( FtFontInfo*, const ImplDevFontAttributes& );

    FtFontInfo*             GetFtFontInfo() const { return mpFtFontInfo; }

    virtual ImplFontEntry*  CreateFontInstance( ImplFontSelectData& ) const;
    virtual ImplFontData*   Clone() const   { return new ImplFTSFontData( *this ); }
    virtual sal_IntPtr      GetFontId() const { return mpFtFontInfo->GetFontId(); }

    static bool             CheckFontData( const ImplFontData& r ) { return r.CheckMagic( IFTSFONT_MAGIC ); }
};

// -----------------------------------------------------------------------

#endif // _SV_GCACHFTYP_HXX
<commit_msg>INTEGRATION: CWS cairotext01_DEV300 (1.39.180); FILE MERGED 2008/02/05 16:22:27 cmc 1.39.180.2: #i85470# need to have a consistent size set 2008/01/25 08:53:46 cmc 1.39.180.1: #i85470# render text with cairo when available<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: gcach_ftyp.hxx,v $
 *
 *  $Revision: 1.40 $
 *
 *  last change: $Author: obo $ $Date: 2008-03-26 08:26:40 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SV_GCACHFTYP_HXX
#define _SV_GCACHFTYP_HXX

#include <vcl/glyphcache.hxx>
#include <rtl/textcvt.h>

#include <ft2build.h>
#include FT_FREETYPE_H
class FreetypeServerFont;
struct FT_GlyphRec_;

// -----------------------------------------------------------------------

// FtFontFile has the responsibility that a font file is only mapped once.
// (#86621#) the old directly ft-managed solution caused it to be mapped
// in up to nTTC*nSizes*nOrientation*nSynthetic times
class FtFontFile
{
public:
    static FtFontFile*      FindFontFile( const ::rtl::OString& rNativeFileName );

    bool                    Map();
    void                    Unmap();

    const unsigned char*    GetBuffer() const { return mpFileMap; }
    int                     GetFileSize() const { return mnFileSize; }
    const ::rtl::OString*   GetFileName() const { return &maNativeFileName; }
    int                     GetLangBoost() const { return mnLangBoost; }

private:
                            FtFontFile( const ::rtl::OString& rNativeFileName );

    const ::rtl::OString    maNativeFileName;
    const unsigned char*    mpFileMap;
    int                     mnFileSize;
    int                     mnRefCount;
    int                     mnLangBoost;
};

// -----------------------------------------------------------------------

// FtFontInfo corresponds to an unscaled font face
class FtFontInfo
{
public:
                           FtFontInfo( const ImplDevFontAttributes&,
                               const ::rtl::OString& rNativeFileName,
                               int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
                                const ExtraKernInfo* );
                          ~FtFontInfo();

    const unsigned char*  GetTable( const char*, ULONG* pLength=0 ) const;

    FT_FaceRec_*          GetFaceFT();
    void                  ReleaseFaceFT( FT_FaceRec_* );

    const ::rtl::OString* GetFontFileName() const   { return mpFontFile->GetFileName(); }
    int                   GetFaceNum() const        { return mnFaceNum; }
    int                   GetSynthetic() const      { return mnSynthetic; }
    sal_IntPtr            GetFontId() const         { return mnFontId; }
    bool                  DontUseAntiAlias() const
        { return maDevFontAttributes.UseAntiAlias() == ANTIALIAS_FALSE; }
    bool                  DontUseEmbeddedBitmaps() const
        { return maDevFontAttributes.UseEmbeddedBitmap() == EMBEDDEDBITMAP_FALSE; }
    bool                  IsSymbolFont() const      { return maDevFontAttributes.IsSymbolFont(); }
    const ImplFontAttributes& GetFontAttributes() const { return maDevFontAttributes; }

    void                  AnnounceFont( ImplDevFontList* );

    int                   GetGlyphIndex( sal_UCS4 cChar ) const;
    void                  CacheGlyphIndex( sal_UCS4 cChar, int nGI ) const;

    bool                  HasExtraKerning() const;
    int                   GetExtraKernPairs( ImplKernPairData** ) const;
    int                   GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;

private:
    FT_FaceRec_*    maFaceFT;
    FtFontFile*     mpFontFile;
    const int       mnFaceNum;
    int             mnRefCount;
    const int       mnSynthetic;

    sal_IntPtr      mnFontId;
    ImplDevFontAttributes maDevFontAttributes;

    // cache unicode->glyphid mapping because looking it up is expensive
    // TODO: change to hash_multimap when a use case requires a m:n mapping
    typedef ::std::hash_map<int,int> Int2IntMap;
    mutable Int2IntMap* mpChar2Glyph;
    mutable Int2IntMap* mpGlyph2Char;
    void InitHashes() const;

    const ExtraKernInfo* mpExtraKernInfo;
};

// these two inlines are very important for performance

inline int FtFontInfo::GetGlyphIndex( sal_UCS4 cChar ) const
{
    if( !mpChar2Glyph )
        return -1;
    Int2IntMap::const_iterator it = mpChar2Glyph->find( cChar );
    if( it == mpChar2Glyph->end() )
        return -1;
    return it->second;
}

inline void FtFontInfo::CacheGlyphIndex( sal_UCS4 cChar, int nIndex ) const
{
    if( !mpChar2Glyph )
        InitHashes();
    (*mpChar2Glyph)[ cChar ] = nIndex;
    (*mpGlyph2Char)[ nIndex ] = cChar;
}

// -----------------------------------------------------------------------

class FreetypeManager
{
public:
                        FreetypeManager();
                        ~FreetypeManager();

    long                AddFontDir( const String& rUrlName );
    void                AddFontFile( const rtl::OString& rNormalizedName,
                            int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes&,
                            const ExtraKernInfo* );
    void                AnnounceFonts( ImplDevFontList* ) const;
    void                ClearFontList();

    FreetypeServerFont* CreateFont( const ImplFontSelectData& );

private:
    typedef ::std::hash_map<sal_IntPtr,FtFontInfo*> FontList;
    FontList            maFontList;

    sal_IntPtr          mnMaxFontId;
    sal_IntPtr          mnNextFontId;
};

// -----------------------------------------------------------------------

class FreetypeServerFont : public ServerFont
{
public:
                                FreetypeServerFont( const ImplFontSelectData&, FtFontInfo* );
    virtual                     ~FreetypeServerFont();

    virtual const ::rtl::OString* GetFontFileName() const { return mpFontInfo->GetFontFileName(); }
    virtual int                 GetFontFaceNum() const { return mpFontInfo->GetFaceNum(); }
    virtual bool                TestFont() const;
    virtual void*               GetFtFace() const;
    virtual int                 GetLoadFlags() const { return mnLoadFlags; }
    virtual bool                NeedsArtificialBold() const { return mbArtBold; }
    virtual bool                NeedsArtificialItalic() const { return mbArtItalic; }

    virtual void                FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;

    virtual int                 GetGlyphIndex( sal_UCS4 ) const;
    int                         GetRawGlyphIndex( sal_UCS4 ) const;
    int                         FixupGlyphIndex( int nGlyphIndex, sal_UCS4 ) const;

    virtual bool                GetAntialiasAdvice( void ) const;
    virtual bool                GetGlyphBitmap1( int nGlyphIndex, RawBitmap& ) const;
    virtual bool                GetGlyphBitmap8( int nGlyphIndex, RawBitmap& ) const;
    virtual bool                GetGlyphOutline( int nGlyphIndex, ::basegfx::B2DPolyPolygon& ) const;
    virtual int                 GetGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;
    virtual ULONG               GetKernPairs( ImplKernPairData** ) const;

    const unsigned char*        GetTable( const char* pName, ULONG* pLength )
                                { return mpFontInfo->GetTable( pName, pLength ); }
    int                         GetEmUnits() const;
    const FT_Size_Metrics&      GetMetricsFT() const { return maSizeFT->metrics; }

protected:
    friend class GlyphCache;

    int                         ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
    virtual void                InitGlyphData( int nGlyphIndex, GlyphData& ) const;
    virtual ULONG               GetFontCodeRanges( sal_uInt32* pCodes ) const;
    bool                        ApplyGSUB( const ImplFontSelectData& );
    virtual ServerFontLayoutEngine* GetLayoutEngine();

private:
    int                         mnWidth;
    int                         mnPrioEmbedded;
    int                         mnPrioAntiAlias;
    FtFontInfo*                 mpFontInfo;
    FT_Int                      mnLoadFlags;
    double                      mfStretch;
    FT_FaceRec_*                maFaceFT;
    FT_SizeRec_*                maSizeFT;

    bool                        mbFaceOk;
    bool            mbArtItalic;
    bool            mbArtBold;
    bool            mbUseGamma;

    typedef ::std::hash_map<int,int> GlyphSubstitution;
    GlyphSubstitution           maGlyphSubstitution;
    rtl_UnicodeToTextConverter  maRecodeConverter;

    ServerFontLayoutEngine*     mpLayoutEngine;
};

// -----------------------------------------------------------------------

class ImplFTSFontData : public ImplFontData
{
private:
    FtFontInfo*             mpFtFontInfo;
    enum { IFTSFONT_MAGIC = 0x1F150A1C };

public:
                            ImplFTSFontData( FtFontInfo*, const ImplDevFontAttributes& );

    FtFontInfo*             GetFtFontInfo() const { return mpFtFontInfo; }

    virtual ImplFontEntry*  CreateFontInstance( ImplFontSelectData& ) const;
    virtual ImplFontData*   Clone() const   { return new ImplFTSFontData( *this ); }
    virtual sal_IntPtr      GetFontId() const { return mpFtFontInfo->GetFontId(); }

    static bool             CheckFontData( const ImplFontData& r ) { return r.CheckMagic( IFTSFONT_MAGIC ); }
};

// -----------------------------------------------------------------------

#endif // _SV_GCACHFTYP_HXX
<|endoftext|>


<commit_before>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "open_spiel/algorithms/evaluate_bots.h"

#include "open_spiel/spiel_bots.h"

namespace open_spiel {
namespace {

void BotTest_RandomVsRandom() {
  auto game = LoadGame("kuhn_poker");
  auto bot0 = MakeUniformRandomBot(*game, Player{0}, /*seed=*/1234);
  auto bot1 = MakeUniformRandomBot(*game, Player{1}, /*seed=*/4321);
  constexpr int num_players = 2;
  std::vector<double> average_results(num_players);
  constexpr int num_iters = 100000;
  for (int iteration = 0; iteration < num_iters; ++iteration) {
    auto this_results =
        EvaluateBots(game->NewInitialState().get(), {bot0.get(), bot1.get()},
                     /*seed=*/iteration);
    for (auto p = Player{0}; p < num_players; ++p) {
      average_results[p] += this_results[p];
    }
  }
  for (auto p = Player{0}; p < num_players; ++p) {
    average_results[p] /= num_iters;
  }

  SPIEL_CHECK_FLOAT_NEAR(average_results[0], 0.125, 0.01);
  SPIEL_CHECK_FLOAT_NEAR(average_results[1], -0.125, 0.01);
}  // namespace

}  // namespace
}  // namespace open_spiel

int main(int argc, char** argv) { open_spiel::BotTest_RandomVsRandom(); }
<commit_msg>Remove a stray comment.<commit_after>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "open_spiel/algorithms/evaluate_bots.h"

#include "open_spiel/spiel_bots.h"

namespace open_spiel {
namespace {

void BotTest_RandomVsRandom() {
  auto game = LoadGame("kuhn_poker");
  auto bot0 = MakeUniformRandomBot(*game, Player{0}, /*seed=*/1234);
  auto bot1 = MakeUniformRandomBot(*game, Player{1}, /*seed=*/4321);
  constexpr int num_players = 2;
  std::vector<double> average_results(num_players);
  constexpr int num_iters = 100000;
  for (int iteration = 0; iteration < num_iters; ++iteration) {
    auto this_results =
        EvaluateBots(game->NewInitialState().get(), {bot0.get(), bot1.get()},
                     /*seed=*/iteration);
    for (auto p = Player{0}; p < num_players; ++p) {
      average_results[p] += this_results[p];
    }
  }
  for (auto p = Player{0}; p < num_players; ++p) {
    average_results[p] /= num_iters;
  }

  SPIEL_CHECK_FLOAT_NEAR(average_results[0], 0.125, 0.01);
  SPIEL_CHECK_FLOAT_NEAR(average_results[1], -0.125, 0.01);
}

}  // namespace
}  // namespace open_spiel

int main(int argc, char** argv) { open_spiel::BotTest_RandomVsRandom(); }
<|endoftext|>


<commit_before>/** iostream_combo.cc ---
 *
 * Copyright (C) 2012 Nil Geisweiller
 *
 * Authors: Nil Geisweiller <ngeiswei@gmail.com>
 *          Matt Chapman <Matt@NinjitsuWeb.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "iostream_combo.h"
#include "procedure_call.h"
#include <boost/range/algorithm/find.hpp>

namespace opencog { namespace combo {

using namespace std;
using namespace boost;

// uncomment this to output a negative literal !$n instead of not($n)
#define ABBREVIATE_NEGATIVE_LITERAL

ostream& ostream_builtin(ostream& out, const builtin& h, format f)
{
    switch(f) {
    case fmt::python:
        switch (h) {
        case id::null_vertex:
            return out << "null_vertex";
        case id::logical_and:
            return out << "ands";
        case id::logical_or:
            return out << "ors";
        case id::logical_not:
            return out << "not";
        case id::logical_true:
            return out << "True";
        case id::logical_false:
            return out << "False";
        default:
            return out << "Builtin: " << h << " unknown";
        }
    case fmt::combo:
        switch (h) {
        case id::null_vertex:
            return out << "null_vertex";
        case id::logical_and:
            return out << "and";
        case id::logical_or:
            return out << "or";
        case id::logical_not:
            return out << "not";
        case id::logical_true:
            return out << "true";
        case id::logical_false:
            return out << "false";
        case id::contin_if:
            return out << "contin_if";
        case id::boolean_if:
            return out << "boolean_if";
        case id::plus:
            return out << "+";
        case id::times:
            return out << "*";
        case id::div:
            return out << "/";
        case id::log:
            return out << "log";
        case id::exp:
            return out << "exp";
        case id::sin:
            return out << "sin";
        case id::greater_than_zero:
            return out << "0<";
        case id::impulse:
            return out << "impulse";
        case id::rand:
            return out << "rand";
        default:
            return out << "Builtin " << h << " unknown";
        }
    default:
        return out << "Format " << f << " unknown";
    }
}

ostream& ostream_argument(ostream& out, const argument& a, format f)
{
    switch(f) {
    case fmt::python:
        if (a.is_negated())
            return out << "not(i[" << -a.idx - 1 << "]),";
        return out << "i[" << a.idx - 1 << "],";
    case fmt::combo:
#ifdef ABBREVIATE_NEGATIVE_LITERAL
        return ostream_abbreviate_literal(out, a);
#else
        if (a.is_negated())
            return out << "not($" << -a.idx << ")";
        return out << "$" << a.idx << vm;
#endif
    default:
        return out << "Format " << f << "unknown";
    }
}

ostream& ostream_vertex(ostream& out, const vertex& v, format f)
{
    if (const ann_type* z = get<ann_type>(&v))
        return out << (*z);
    if (const argument* a = get<argument>(&v))
        return ostream_argument(out, *a, f);
    if (const builtin* h = get<builtin>(&v))
        return ostream_builtin(out, *h, f);
    if (const wild_card* w = get<wild_card>(&v))
        return out << (*w);
    if (const action* act = get<action>(&v))
        return out << (*act);
    if (const builtin_action* aact = get<builtin_action>(&v))
        return out << (*aact);
    if (const perception* per = get<perception>(&v))
        return out << (*per);
    if (const indefinite_object*
            iot = get<indefinite_object>(&v))
        return out << (*iot);
    if (const message* m = get<message>(&v))
        return out << (*m);
    if (const definite_object* dot = get<definite_object>(&v))
        return out << (*dot);
    if (const action_symbol* as = get<action_symbol>(&v))
        return out << (*as);
    if (const procedure_call* cp = get<procedure_call>(&v))
        return out << (*cp);

    if (const enum_t* m = get<enum_t>(&v))
        return out << (*m);

    if (double x =  get<contin_t>(v))
        return out << x;

    OC_ASSERT(false, "Don't know how to print this type");
    return out;
}

std::ostream& ostream_combo_tree(std::ostream& out, const combo_tree ct, format f) {
    for (combo_tree::iterator it=ct.begin(); it!=ct.end(); ++it) {
        ostream_combo_it(out, it, f);
        it.skip_children();
        out << " ";
    }
    return out;
}

string ph2l(const string& ce, const vector<string>& labels)
{
    /// @todo the implementation could be done in 2 lines with
    /// boost.regex with boost version 1.42 or above because then we
    /// can use Formatter as callback, but we're stuck with boost 1.38
    /// :-(
    string res;
    string match;
    bool matching = false;
    foreach(char c, ce) {
        if(!matching) {
            res += c;
            if(c == '$') // matching starts
                matching = true;
        } else {
            if(c == ' ' || c == ')' || c == '\n') { //matching ends
                res += labels[lexical_cast<arity_t>(match) - 1] + c;
                match.clear();
                matching = false;
            } else // matching goes
                match += c;
        }
    }
    // if a matching is going on flush to the result
    if(matching)
        res += labels[lexical_cast<arity_t>(match) - 1];
    return res;
}

string l2ph(const string& ce, const vector<string>& labels)
{
    /// @todo the implementation could be done in 2 lines with
    /// boost.regex with boost version 1.42 or above because then we
    /// can use Formatter as callback, but we're stuck with boost 1.38
    /// :-(
    string res;
    string match;
    bool matching = false;
    foreach(char c, ce) {
        if(!matching) {
            res += c;
            if(c == '$') // matching starts
                matching = true;
        } else {
            if(c == ' ' || c == ')' || c == '\n') { //matching ends
                auto found_it = find(labels, match);
                OC_ASSERT(found_it != labels.end(), "No label %s matching",
                          match.c_str());
                arity_t idx = distance(labels.begin(), found_it) + 1;
                res += lexical_cast<string>(idx) + c;
                match.clear();
                matching = false;
            } else // matching goes
                match += c;
        }
    }
    // if a matching is going on flush to the result
    if(matching) {
        auto found_it = boost::find(labels, match);
        OC_ASSERT(found_it != labels.end(), "No label %s matching",
                  match.c_str());
        arity_t idx = distance(labels.begin(), found_it) + 1;
        res += lexical_cast<string>(idx);
    }
    return res;
}

ostream& operator<<(ostream& out, const ann_type& h)
{
    switch (h.id) {
    case id::ann:
        return out << "ann";
    case id::ann_input:
        return out << "$I" << h.idx;
    case id::ann_node:
        return out << "$N" << h.idx;
    default:
        return out << "ANN : UNKNOWN_HANDLE";
   }
}

ostream& operator<<(ostream& out, const builtin& h)
{
    return ostream_builtin(out, h, fmt::combo);
}

ostream& operator<<(ostream& out, const wild_card& w)
{
    switch (w) {
    case id::asterisk:
        return out << "_*_";
    default:
        return out << "WILD_CARD: UNKNOWN_HANDLE";
    }
}

ostream& ostream_abbreviate_literal(ostream& out, const argument& a) {
    if(a.is_negated()) {
        return out << "!$" << -a.idx;
    }
    return out << "$" << a.idx;
}

ostream& operator<<(ostream& out, const argument& a)
{
    return ostream_argument(out, a, fmt::combo);
}

ostream& operator<<(ostream& out, const vertex& v)
{
    return ostream_vertex(out, v, fmt::combo);
}

}} // ~namespaces combo opencog
<commit_msg>combo: fix infinite recursion when printing vertexes.<commit_after>/** iostream_combo.cc ---
 *
 * Copyright (C) 2012 Nil Geisweiller
 *
 * Authors: Nil Geisweiller <ngeiswei@gmail.com>
 *          Matt Chapman <Matt@NinjitsuWeb.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "iostream_combo.h"
#include "procedure_call.h"
#include <boost/range/algorithm/find.hpp>

namespace opencog { namespace combo {

using namespace std;
using namespace boost;

// uncomment this to output a negative literal !$n instead of not($n)
#define ABBREVIATE_NEGATIVE_LITERAL

ostream& ostream_builtin(ostream& out, const builtin& h, format f)
{
    switch(f) {
    case fmt::python:
        switch (h) {
        case id::null_vertex:
            return out << "null_vertex";
        case id::logical_and:
            return out << "ands";
        case id::logical_or:
            return out << "ors";
        case id::logical_not:
            return out << "not";
        case id::logical_true:
            return out << "True";
        case id::logical_false:
            return out << "False";
        default:
            return out << "Builtin: " << (unsigned) h << " unknown";
        }
    case fmt::combo:
        switch (h) {
        case id::null_vertex:
            return out << "null_vertex";
        case id::logical_and:
            return out << "and";
        case id::logical_or:
            return out << "or";
        case id::logical_not:
            return out << "not";
        case id::logical_true:
            return out << "true";
        case id::logical_false:
            return out << "false";
        case id::plus:
            return out << "+";
        case id::times:
            return out << "*";
        case id::div:
            return out << "/";
        case id::log:
            return out << "log";
        case id::exp:
            return out << "exp";
        case id::sin:
            return out << "sin";
        case id::greater_than_zero:
            return out << "0<";
        case id::impulse:
            return out << "impulse";
        case id::rand:
            return out << "rand";

        case id::contin_if:
            return out << "contin_if";
        case id::boolean_if:
            return out << "boolean_if";
        case id::cond:
            return out << "cond";
        case id::equ:
            return out << "equ";
        default:
            return out << "Builtin " << (unsigned) h << " unknown";
        }
    default:
        return out << "Format " << f << " unknown";
    }
}

ostream& ostream_argument(ostream& out, const argument& a, format f)
{
    switch(f) {
    case fmt::python:
        if (a.is_negated())
            return out << "not(i[" << -a.idx - 1 << "]),";
        return out << "i[" << a.idx - 1 << "],";
    case fmt::combo:
#ifdef ABBREVIATE_NEGATIVE_LITERAL
        return ostream_abbreviate_literal(out, a);
#else
        if (a.is_negated())
            return out << "not($" << -a.idx << ")";
        return out << "$" << a.idx << vm;
#endif
    default:
        return out << "Format " << f << "unknown";
    }
}

ostream& ostream_vertex(ostream& out, const vertex& v, format f)
{
    // Handle the most likely types first.
    if (const argument* a = get<argument>(&v))
        return ostream_argument(out, *a, f);
    if (const builtin* h = get<builtin>(&v))
        return ostream_builtin(out, *h, f);
    if (const enum_t* m = get<enum_t>(&v))
        return out << m->getContent();

    // XXX ?? Ahem, won't calling out<<(*m) just lead to infinite
    // recursion ??
    if (const ann_type* z = get<ann_type>(&v))
        return out << (*z);
    if (const wild_card* w = get<wild_card>(&v))
        return out << (*w);
    if (const action* act = get<action>(&v))
        return out << (*act);
    if (const builtin_action* aact = get<builtin_action>(&v))
        return out << (*aact);
    if (const perception* per = get<perception>(&v))
        return out << (*per);
    if (const indefinite_object* iot = get<indefinite_object>(&v))
        return out << (*iot);
    if (const message* m = get<message>(&v))
        return out << m->getContent();
    if (const definite_object* dot = get<definite_object>(&v))
        return out << (*dot);
    if (const action_symbol* as = get<action_symbol>(&v))
        return out << (*as);
    if (const procedure_call* cp = get<procedure_call>(&v))
        return out << (*cp);

    if (double x =  get<contin_t>(v))
        return out << x;

    OC_ASSERT(false, "Don't know how to print this type");
    return out;
}

std::ostream& ostream_combo_tree(std::ostream& out, const combo_tree ct, format f) {
    for (combo_tree::iterator it=ct.begin(); it!=ct.end(); ++it) {
        ostream_combo_it(out, it, f);
        it.skip_children();
        out << " ";
    }
    return out;
}

string ph2l(const string& ce, const vector<string>& labels)
{
    /// @todo the implementation could be done in 2 lines with
    /// boost.regex with boost version 1.42 or above because then we
    /// can use Formatter as callback, but we're stuck with boost 1.38
    /// :-(
    string res;
    string match;
    bool matching = false;
    foreach(char c, ce) {
        if(!matching) {
            res += c;
            if(c == '$') // matching starts
                matching = true;
        } else {
            if(c == ' ' || c == ')' || c == '\n') { //matching ends
                res += labels[lexical_cast<arity_t>(match) - 1] + c;
                match.clear();
                matching = false;
            } else // matching goes
                match += c;
        }
    }
    // if a matching is going on flush to the result
    if(matching)
        res += labels[lexical_cast<arity_t>(match) - 1];
    return res;
}

string l2ph(const string& ce, const vector<string>& labels)
{
    /// @todo the implementation could be done in 2 lines with
    /// boost.regex with boost version 1.42 or above because then we
    /// can use Formatter as callback, but we're stuck with boost 1.38
    /// :-(
    string res;
    string match;
    bool matching = false;
    foreach(char c, ce) {
        if(!matching) {
            res += c;
            if(c == '$') // matching starts
                matching = true;
        } else {
            if(c == ' ' || c == ')' || c == '\n') { //matching ends
                auto found_it = find(labels, match);
                OC_ASSERT(found_it != labels.end(), "No label %s matching",
                          match.c_str());
                arity_t idx = distance(labels.begin(), found_it) + 1;
                res += lexical_cast<string>(idx) + c;
                match.clear();
                matching = false;
            } else // matching goes
                match += c;
        }
    }
    // if a matching is going on flush to the result
    if(matching) {
        auto found_it = boost::find(labels, match);
        OC_ASSERT(found_it != labels.end(), "No label %s matching",
                  match.c_str());
        arity_t idx = distance(labels.begin(), found_it) + 1;
        res += lexical_cast<string>(idx);
    }
    return res;
}

ostream& operator<<(ostream& out, const ann_type& h)
{
    switch (h.id) {
    case id::ann:
        return out << "ann";
    case id::ann_input:
        return out << "$I" << h.idx;
    case id::ann_node:
        return out << "$N" << h.idx;
    default:
        return out << "ANN : UNKNOWN_HANDLE";
   }
}

ostream& operator<<(ostream& out, const builtin& h)
{
    return ostream_builtin(out, h, fmt::combo);
}

ostream& operator<<(ostream& out, const wild_card& w)
{
    switch (w) {
    case id::asterisk:
        return out << "_*_";
    default:
        return out << "WILD_CARD: UNKNOWN_HANDLE";
    }
}

ostream& ostream_abbreviate_literal(ostream& out, const argument& a) {
    if(a.is_negated()) {
        return out << "!$" << -a.idx;
    }
    return out << "$" << a.idx;
}

ostream& operator<<(ostream& out, const argument& a)
{
    return ostream_argument(out, a, fmt::combo);
}

ostream& operator<<(ostream& out, const vertex& v)
{
    return ostream_vertex(out, v, fmt::combo);
}

}} // ~namespaces combo opencog
<|endoftext|>