/**
     * Reads media data from this <tt>PullBufferStream</tt> into a specific <tt>Buffer</tt> with
     * blocking.
     *
     * @param buffer the <tt>Buffer</tt> in which media data is to be read from this
     *     <tt>PullBufferStream</tt>
     * @throws IOException if anything goes wrong while reading media data from this
     *     <tt>PullBufferStream</tt> into the specified <tt>buffer</tt>
     * @see javax.media.protocol.PullBufferStream#read(javax.media.Buffer)
     */
    public void read(Buffer buffer) throws IOException {
      if (setThreadPriority) {
        setThreadPriority = false;
        setThreadPriority();
      }

      Object data = buffer.getData();
      int length = this.length;

      if (data instanceof byte[]) {
        if (((byte[]) data).length < length) data = null;
      } else data = null;
      if (data == null) {
        data = new byte[length];
        buffer.setData(data);
      }

      int toRead = length;
      byte[] bytes = (byte[]) data;
      int offset = 0;

      buffer.setLength(0);
      while (toRead > 0) {
        int read;

        synchronized (this) {
          if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING)
            read = audioRecord.read(bytes, offset, toRead);
          else break;
        }

        if (read < 0) {
          throw new IOException(
              AudioRecord.class.getName() + "#read(byte[], int, int) returned " + read);
        } else {
          buffer.setLength(buffer.getLength() + read);
          offset += read;
          toRead -= read;
        }
      }
      buffer.setOffset(0);

      // Apply software gain.
      if (gainControl != null) {
        BasicVolumeControl.applyGain(gainControl, bytes, buffer.getOffset(), buffer.getLength());
      }
    }
  /**
   * Blocks and reads into a <tt>Buffer</tt> from this <tt>PullBufferStream</tt>.
   *
   * @param buffer the <tt>Buffer</tt> this <tt>PullBufferStream</tt> is to read into
   * @throws IOException if an I/O error occurs while this <tt>PullBufferStream</tt> reads into the
   *     specified <tt>Buffer</tt>
   * @see AbstractVideoPullBufferStream#doRead(Buffer)
   */
  @Override
  protected void doRead(Buffer buffer) throws IOException {
    /*
     * Determine the Format in which we're expected to output. We cannot
     * rely on the Format always being specified in the Buffer because it is
     * not its responsibility, the DataSource of this ImageStream knows the
     * output Format.
     */
    Format format = buffer.getFormat();

    if (format == null) {
      format = getFormat();
      if (format != null) buffer.setFormat(format);
    }

    if (format instanceof AVFrameFormat) {
      Object o = buffer.getData();
      AVFrame frame;

      if (o instanceof AVFrame) frame = (AVFrame) o;
      else {
        frame = new AVFrame();
        buffer.setData(frame);
      }

      AVFrameFormat avFrameFormat = (AVFrameFormat) format;
      Dimension size = avFrameFormat.getSize();
      ByteBuffer data = readScreenNative(size);

      if (data != null) {
        if (frame.avpicture_fill(data, avFrameFormat) < 0) {
          data.free();
          throw new IOException("avpicture_fill");
        }
      } else {
        /*
         * This can happen when we disconnect a monitor from computer
         * before or during grabbing.
         */
        throw new IOException("Failed to grab screen.");
      }
    } else {
      byte[] bytes = (byte[]) buffer.getData();
      Dimension size = ((VideoFormat) format).getSize();

      bytes = readScreen(bytes, size);

      buffer.setData(bytes);
      buffer.setOffset(0);
      buffer.setLength(bytes.length);
    }

    buffer.setHeader(null);
    buffer.setTimeStamp(System.nanoTime());
    buffer.setSequenceNumber(seqNo);
    buffer.setFlags(Buffer.FLAG_SYSTEM_TIME | Buffer.FLAG_LIVE_DATA);
    seqNo++;
  }