Package org.apache.openmeetings.screen.webstart

Source Code of org.apache.openmeetings.screen.webstart.ScreenV1Encoder

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.
*/
package org.apache.openmeetings.screen.webstart;

import static org.apache.openmeetings.screen.webstart.gui.ScreenDimensions.resizeX;
import static org.apache.openmeetings.screen.webstart.gui.ScreenDimensions.resizeY;
import static org.red5.io.IoConstants.FLAG_CODEC_SCREEN;
import static org.red5.io.IoConstants.FLAG_FRAMETYPE_INTERFRAME;
import static org.red5.io.IoConstants.FLAG_FRAMETYPE_KEYFRAME;

import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.Deflater;

import org.apache.mina.core.buffer.IoBuffer;
import org.red5.server.net.rtmp.event.VideoData;

public class ScreenV1Encoder extends BaseScreenEncoder {
  private int[][] last = null;
  private static int KEY_FRAME_INDEX = 25;
  private static int DEFAULT_BLOCK_SIZE = 32;
  private static int DEFAULT_SCREEN_WIDTH = 1920;
  private static int DEFAULT_SCREEN_HEIGHT = 1080;
  private int keyFrameIndex;
  private int frameCount = 0;
  private int blockSize;
  private ByteArrayOutputStream ba = new ByteArrayOutputStream(50 + 3 * DEFAULT_SCREEN_WIDTH * DEFAULT_SCREEN_HEIGHT);
  private byte[] areaBuf = null;
  private Deflater d = new Deflater(Deflater.DEFAULT_COMPRESSION);
  private byte[] zipBuf = null;
  private VideoData unalteredFrame = null;
 
  public ScreenV1Encoder() {
    this(KEY_FRAME_INDEX, DEFAULT_BLOCK_SIZE);
  }
 
  public ScreenV1Encoder(int keyFrameIndex) {
    this(keyFrameIndex, DEFAULT_BLOCK_SIZE);
  }
 
  //will create square blocks
  public ScreenV1Encoder(int keyFrameIndex, int blockSize) {
    this.keyFrameIndex = keyFrameIndex;
    if (blockSize < 16 || blockSize > 256 || blockSize % 16 != 0) {
      throw new RuntimeException("Invalid block size passed: " + blockSize + " should be: 'from 16 to 256 in multiples of 16'");
    }
    this.blockSize = blockSize;

    areaBuf = new byte[3 * blockSize * blockSize];
    zipBuf = new byte[3 * blockSize * blockSize];
  }

  private VideoData getData(byte[] data) {
    IoBuffer buf = IoBuffer.allocate(data.length);
    buf.clear();
    buf.put(data);
    buf.flip();
    return new VideoData(buf);
  }
 
  public void createUnalteredFrame() throws IOException {
    if (last == null) {
      return;
    }
    if (unalteredFrame == null) {
      ByteArrayOutputStream ba = new ByteArrayOutputStream(200);
     
      Rectangle _area = new Rectangle(resizeX, resizeY);
      //header
      ba.write(getTag(FLAG_FRAMETYPE_INTERFRAME, FLAG_CODEC_SCREEN));
      writeShort(ba, _area.width + ((blockSize / 16 - 1) << 12));
      writeShort(ba, _area.height + ((blockSize / 16 - 1) << 12));
      Rectangle area = getNextBlock(_area, null);
      while (area.width > 0 && area.height > 0) {
        writeShort(ba, 0);
        area = getNextBlock(_area, area);
      }
      unalteredFrame = getData(ba.toByteArray());
    }
  }
 
  public VideoData getUnalteredFrame() {
    if (unalteredFrame != null && (frameCount % keyFrameIndex) != 0) {
      frameCount++;
    }
    return unalteredFrame;
  }
 
  public synchronized VideoData encode(int[][] img) throws IOException {
    ba.reset();
    Rectangle imgArea = new Rectangle(img.length, img[0].length);
    Rectangle area = getNextBlock(imgArea, null);
    boolean isKeyFrame = (frameCount++ % keyFrameIndex) == 0 || last == null;
   
    //header
    ba.write(getTag(isKeyFrame ? FLAG_FRAMETYPE_KEYFRAME : FLAG_FRAMETYPE_INTERFRAME, FLAG_CODEC_SCREEN));
    writeShort(ba, imgArea.width + ((blockSize / 16 - 1) << 12));
    writeShort(ba, imgArea.height + ((blockSize / 16 - 1) << 12));
   
    while (area.width > 0 && area.height > 0) {
      writeBytesIfChanged(ba, isKeyFrame, img, area);
      area = getNextBlock(imgArea, area);
    }
    last = img;
    return getData(ba.toByteArray());
  }
 
  public void reset() {
    last = null;
    unalteredFrame = null;
  }
 
  private Rectangle getNextBlock(Rectangle img, Rectangle _prev) {
    Rectangle prev;
    if (_prev == null) {
      prev = new Rectangle(0, Math.max(0, img.height - blockSize), blockSize, blockSize);
    } else {
      prev = new Rectangle(_prev);
      if (prev.x + prev.width == img.getWidth()) {
        if (prev.y == 0) return new Rectangle(); //the end of the image
        //next row
        prev.x = 0; //reset position
        prev.width = blockSize; //reset width
        prev.height = (prev.y > blockSize ? blockSize : prev.y);
        prev.y -= prev.height;
      } else {
        prev.x += blockSize;
      }
    }
    return img.intersection(prev);
  }

  private void writeBytesIfChanged(ByteArrayOutputStream ba, boolean isKeyFrame, int[][] img, Rectangle area) throws IOException {
    boolean changed = isKeyFrame;
    int count = 0;
    for (int y = area.y + area.height - 1; y >= area.y; --y) {
      for (int x = area.x; x < area.x + area.width; ++x) {
        int pixel = img[x][y];
        if (!changed && (last == null || pixel != last[x][y])) {
          changed = true;
        }
        areaBuf[count++] = (byte)(pixel & 0xFF);      // Blue component
        areaBuf[count++] = (byte)((pixel >> 8) & 0xFF);    // Green component
        areaBuf[count++] = (byte)((pixel >> 16) & 0xFF)// Red component
      }
    }
    if (changed) {
      d.reset();
      d.setInput(areaBuf, 0, count);
      d.finish();
      int written = d.deflate(zipBuf);
      writeShort(ba, written);
      ba.write(zipBuf, 0, written);
    } else {
      writeShort(ba, 0);
    }
  }

  public int getTag(final int frame, final int codec) {
    return ((frame & 0x0F) << 4) + ((codec & 0x0F) << 0);
  }
 
  private void writeShort(OutputStream os, final int n) throws IOException {
    os.write((n >> 8) & 0xFF);
    os.write((n >> 0) & 0xFF);
  }
 
  public static int[][] getImage(Rectangle screen, Robot robot) {
    int[][] buffer = new int[resizeX][resizeY];
    BufferedImage image = resize(robot.createScreenCapture(screen), new Rectangle(resizeX, resizeY));
    for (int x = 0; x < image.getWidth(); ++x) {
      for (int y = 0; y < image.getHeight(); ++y) {
        buffer[x][y] = image.getRGB(x, y);
      }
    }
    return buffer;
  }
}
TOP

Related Classes of org.apache.openmeetings.screen.webstart.ScreenV1Encoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.