示例#1
0
  /**
   * 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++;
  }
示例#2
0
  /** decode the buffer * */
  public int process(Buffer inputBuffer, Buffer outputBuffer) {

    if (!checkInputBuffer(inputBuffer)) {
      return BUFFER_PROCESSED_FAILED;
    }

    if (isEOM(inputBuffer)) {
      propagateEOM(outputBuffer);
      return BUFFER_PROCESSED_OK;
    }

    Object outData = outputBuffer.getData();
    outputBuffer.setData(inputBuffer.getData());
    inputBuffer.setData(outData);
    outputBuffer.setLength(inputBuffer.getLength());
    outputBuffer.setFormat(outputFormat);
    outputBuffer.setOffset(inputBuffer.getOffset());
    return BUFFER_PROCESSED_OK;
  }
示例#3
0
  @Override
  public int process(Buffer input, Buffer output) {
    if (!checkInputBuffer(input)) {
      return BUFFER_PROCESSED_FAILED;
    }

    if (isEOM(input)) {
      propagateEOM(output); // TODO: what about data? can there be any?
      return BUFFER_PROCESSED_OK;
    }

    try {
      // TODO: this is very inefficient - it allocates a new byte array
      // (or more) every time
      final ByteArrayInputStream is =
          new ByteArrayInputStream((byte[]) input.getData(), input.getOffset(), input.getLength());
      final BufferedImage image = ImageIO.read(is);
      is.close();
      final Buffer b =
          ImageToBuffer.createBuffer(image, ((VideoFormat) outputFormat).getFrameRate());

      output.setData(b.getData());
      output.setOffset(b.getOffset());
      output.setLength(b.getLength());
      output.setFormat(b.getFormat()); // TODO: this is a bit hacky, this
      // format will be more specific
      // than the actual set output
      // format, because now we know what
      // ImageIO gave us for a
      // BufferedImage as far as pixel
      // masks, etc.

      return BUFFER_PROCESSED_OK;

    } catch (IOException e) {
      output.setDiscard(true);
      output.setLength(0);
      return BUFFER_PROCESSED_FAILED;
    }
  }
    @Override
    public void readFrame(Buffer buffer) {
      // example data:
      // --ssBoundary8345
      // Content-Type: image/jpeg
      // Content-Length: 114587

      try {
        String line;
        // eat leading blank lines
        while (true) {
          line = readLine(MAX_LINE_LENGTH);
          if (line == null) {
            buffer.setEOM(true);
            buffer.setLength(0);
            return;
          }

          if (!line.trim().equals("")) break; // end of header
        }

        if (boundary == null) {
          boundary = line.trim(); // TODO: we should be able to get
          // this from the content type, but
          // the content type has this
          // stripped out. So we'll just take
          // the first nonblank line to be the
          // boundary.
          // System.out.println("boundary: " + boundary);
        } else {
          if (!line.trim().equals(boundary)) {
            // throw new IOException("Expected boundary: " +
            // toPrintable(line));
            // TODO: why do we seem to get these when playing back
            // mmr files recorded using FmjTranscode?
            logger.warning("Expected boundary (frame " + framesRead + "): " + toPrintable(line));

            // handle streams that are truncated in the middle of a
            // frame:
            final int eatResult = eatUntil(boundary); // TODO: no
            // need to
            // store the
            // data

            logger.info(
                "Ignored bytes (eom after="
                    + (eatResult < 0)
                    + "): "
                    + (eatResult < 0 ? (-1 * eatResult - 1) : eatResult));
            if (eatResult < 0) {
              buffer.setEOM(true);
              buffer.setLength(0);
              return;
            }

            // now read boundary
            line = readLine(MAX_LINE_LENGTH);
            if (!line.trim().equals(boundary)) {
              throw new RuntimeException("No boundary found after eatUntil(boundary)"); // should
              // never
              // happen
            }
          }
        }

        final Properties properties = new Properties();

        while (true) {
          line = readLine(MAX_LINE_LENGTH);
          if (line == null) {
            buffer.setEOM(true);
            buffer.setLength(0);
            return;
          }

          if (line.trim().equals("")) break; // end of header

          if (!parseProperty(line, properties))
            throw new IOException("Expected property: " + toPrintable(line));
        }

        final String contentType = properties.getProperty("Content-Type".toUpperCase());
        if (contentType == null) {
          logger.warning("Header properties: " + properties);
          throw new IOException("Expected Content-Type in header");
        }

        // check supported content types:
        if (!isSupportedFrameContentType(contentType)) {
          throw new IOException("Unsupported Content-Type: " + contentType);
        }

        if (frameContentType == null) {
          frameContentType = contentType;
        } else {
          if (!contentType.equals(frameContentType))
            throw new IOException(
                "Content type changed during stream from "
                    + frameContentType
                    + " to "
                    + contentType);
        }

        // TODO: check that size doesn't change throughout

        final byte[] data;

        final String contentLenStr = properties.getProperty("Content-Length".toUpperCase());
        if (contentLenStr != null) { // if we know the content length, use it
          final int contentLen;
          try {
            contentLen = Integer.parseInt(contentLenStr);
          } catch (NumberFormatException e) {
            throw new IOException("Invalid content length: " + contentLenStr);
          }

          // now, read the content-length bytes
          data = readFully(contentLen); // TODO: don't realloc each
          // time
        } else {
          // if we don't know the content length, just read until we
          // find the boundary.
          // Some IP cameras don't specify it, like
          // http://webcam-1.duesseldorf.it-on.net/cgi-bin/nph-update.cgi
          data = readUntil(boundary);
        }

        // ext
        final String timestampStr = properties.getProperty(TIMESTAMP_KEY.toUpperCase());
        if (timestampStr != null) {
          try {
            final long timestamp = Long.parseLong(timestampStr);
            buffer.setTimeStamp(timestamp);

          } catch (NumberFormatException e) {
            logger.log(Level.WARNING, "" + e, e);
          }
        }

        if (data == null) {
          buffer.setEOM(true);
          buffer.setLength(0);
          return;
        }

        buffer.setData(data);
        buffer.setOffset(0);
        buffer.setLength(data.length);
        ++framesRead;

      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
示例#5
0
  /** {@inheritDoc} */
  @Override
  protected int doProcess(Buffer inBuffer, Buffer outBuffer) {
    byte[] inData = (byte[]) inBuffer.getData();
    int inOffset = inBuffer.getOffset();

    if (!VP8PayloadDescriptor.isValid(inData, inOffset)) {
      logger.warn("Invalid RTP/VP8 packet discarded.");
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_FAILED; // XXX: FAILED or OK?
    }

    long inSeq = inBuffer.getSequenceNumber();
    long inRtpTimestamp = inBuffer.getRtpTimeStamp();
    int inPictureId = VP8PayloadDescriptor.getPictureId(inData, inOffset);
    boolean inMarker = (inBuffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0;
    boolean inIsStartOfFrame = VP8PayloadDescriptor.isStartOfFrame(inData, inOffset);
    int inLength = inBuffer.getLength();
    int inPdSize = VP8PayloadDescriptor.getSize(inData, inOffset);
    int inPayloadLength = inLength - inPdSize;

    if (empty && lastSentSeq != -1 && seqNumComparator.compare(inSeq, lastSentSeq) != 1) {
      if (logger.isInfoEnabled()) logger.info("Discarding old packet (while empty) " + inSeq);
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_OK;
    }

    if (!empty) {
      // if the incoming packet has a different PictureID or timestamp
      // than those of the current frame, then it belongs to a different
      // frame.
      if ((inPictureId != -1 && pictureId != -1 && inPictureId != pictureId)
          | (timestamp != -1 && inRtpTimestamp != -1 && inRtpTimestamp != timestamp)) {
        if (seqNumComparator.compare(inSeq, firstSeq) != 1) // inSeq <= firstSeq
        {
          // the packet belongs to a previous frame. discard it
          if (logger.isInfoEnabled()) logger.info("Discarding old packet " + inSeq);
          outBuffer.setDiscard(true);
          return BUFFER_PROCESSED_OK;
        } else // inSeq > firstSeq (and also presumably isSeq > lastSeq)
        {
          // the packet belongs to a subsequent frame (to the one
          // currently being held). Drop the current frame.

          if (logger.isInfoEnabled())
            logger.info(
                "Discarding saved packets on arrival of"
                    + " a packet for a subsequent frame: "
                    + inSeq);

          // TODO: this would be the place to complain about the
          // not-well-received PictureID by sending a RTCP SLI or NACK.
          reinit();
        }
      }
    }

    // a whole frame in a single packet. avoid the extra copy to
    // this.data and output it immediately.
    if (empty && inMarker && inIsStartOfFrame) {
      byte[] outData = validateByteArraySize(outBuffer, inPayloadLength, false);
      System.arraycopy(inData, inOffset + inPdSize, outData, 0, inPayloadLength);
      outBuffer.setOffset(0);
      outBuffer.setLength(inPayloadLength);
      outBuffer.setRtpTimeStamp(inBuffer.getRtpTimeStamp());

      if (TRACE) logger.trace("Out PictureID=" + inPictureId);

      lastSentSeq = inSeq;

      return BUFFER_PROCESSED_OK;
    }

    // add to this.data
    Container container = free.poll();
    if (container == null) container = new Container();
    if (container.buf == null || container.buf.length < inPayloadLength)
      container.buf = new byte[inPayloadLength];

    if (data.get(inSeq) != null) {
      if (logger.isInfoEnabled())
        logger.info("(Probable) duplicate packet detected, discarding " + inSeq);
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_OK;
    }

    System.arraycopy(inData, inOffset + inPdSize, container.buf, 0, inPayloadLength);
    container.len = inPayloadLength;
    data.put(inSeq, container);

    // update fields
    frameLength += inPayloadLength;
    if (firstSeq == -1 || (seqNumComparator.compare(firstSeq, inSeq) == 1)) firstSeq = inSeq;
    if (lastSeq == -1 || (seqNumComparator.compare(inSeq, lastSeq) == 1)) lastSeq = inSeq;

    if (empty) {
      // the first received packet for the current frame was just added
      empty = false;
      timestamp = inRtpTimestamp;
      pictureId = inPictureId;
    }

    if (inMarker) haveEnd = true;
    if (inIsStartOfFrame) haveStart = true;

    // check if we have a full frame
    if (frameComplete()) {
      byte[] outData = validateByteArraySize(outBuffer, frameLength, false);
      int ptr = 0;
      Container b;
      for (Map.Entry<Long, Container> entry : data.entrySet()) {
        b = entry.getValue();
        System.arraycopy(b.buf, 0, outData, ptr, b.len);
        ptr += b.len;
      }

      outBuffer.setOffset(0);
      outBuffer.setLength(frameLength);
      outBuffer.setRtpTimeStamp(inBuffer.getRtpTimeStamp());

      if (TRACE) logger.trace("Out PictureID=" + inPictureId);
      lastSentSeq = lastSeq;

      // prepare for the next frame
      reinit();

      return BUFFER_PROCESSED_OK;
    } else {
      // frame not complete yet
      outBuffer.setDiscard(true);
      return OUTPUT_BUFFER_NOT_FILLED;
    }
  }