import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.PermissionEvent;
import flash.events.StatusEvent;
import flash.filesystem.File;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.CameraPosition;
import flash.media.Video;
import flash.net.FileReference;
import flash.text.TextField;
import flash.utils.ByteArray;
import flash.utils.getTimer;
public class VideoCapture_AS extends Sprite
{
public function VideoCapture_AS()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
addEventListener(Event.ADDED_TO_STAGE, app_addedToStageHandler);
}
import com.rainbowcreatures.FWVideoEncoder;
import flash.permissions.PermissionStatus;
private var _mode: String = "record";
private var _state: String = "none";
private var _extension: String = "mp4";
private var _videoContainer:Sprite;
private var _video:Video;
private var _cam:Camera;
private const _VIDEO_W:int = 800;
private const _VIDEO_H: int = 600;
private var _myEncoder:FWVideoEncoder;
private var lb_fps:TextField;
private var btn_save:LabelButton;
protected function app_addedToStageHandler(event:Event):void
{
makeUI();
init();
}
private function init():void
{
addEventListener(Event.ENTER_FRAME, app_enterFrame);
_myEncoder = FWVideoEncoder.getInstance(this);
_myEncoder.addEventListener(StatusEvent.STATUS, onStatus);
_video = new Video(_VIDEO_W, _VIDEO_H);
_videoContainer = new Sprite();
_videoContainer.addChild(_video);
//_videoContainer.scaleX = _videoContainer.scaleY = 0.8;
addChild(_videoContainer);
var rect:Rectangle = _videoContainer.getRect(this);
_myEncoder.setCaptureRectangle(rect.x, rect.y, rect.width, rect.height);
addChild(lb_fps);
addChild(btn_save);
btn_save.x = stage.stageWidth - btn_save.width;
btn_save.y = stage.stageHeight - btn_save.height;
btn_save.addEventListener(MouseEvent.CLICK, onSaveClick);
if (CONFIG::cfg_isair) {
if (_myEncoder.platform_type == FWVideoEncoder.PLATFORM_TYPE_MOBILE) {
getCameraPermission();
} else {
connectCamera();
}
} else {
connectCamera();
}
}
private function makeUI():void
{
lb_fps = new TextField();
btn_save = new LabelButton();
}
private function getCameraPermission():Camera
{
CONFIG::cfg_isair {
PermissionCheckManager.instance.checkPermission(PermissionCheckManager.TYPE_CAMERA, connectCamera, function():void { trace("Camera permissions not granted"); });
}//CONFIG::cfg_isair
return null;
}
// shortcut for connecting the camera
private function connectCamera():void
{
_cam = getCameraByPosition(CameraPosition.FRONT);
if (_cam != null)
{
_cam.setMode(_VIDEO_W, _VIDEO_H, 25);
_cam.setQuality(0, 100);
_video.attachCamera(_cam);
}
}
private function getCameraByPosition(position:String):Camera
{
var numCameras: uint = (Camera.isSupported) ? Camera.names.length : 0;
CONFIG::cfg_isair {
for (var i: uint = 0; i < numCameras; i++)
{
var cam:Camera = Camera.getCamera(String(i));
trace(">>>", i, cam.position, position);
if (cam && cam.position == position )
{
return cam;
}
}
}
return Camera.getCamera();
}
protected function app_enterFrame(event:Event):void
{
getFps();
if (_state == "record")
{
// capture frame
// on desktop, we can capture a target displayObject (if no argument is specified, it will just capture the whole stage)
if (_myEncoder.platform_type == FWVideoEncoder.PLATFORM_TYPE_DESKTOP)
{
_myEncoder.capture();
}
else
{
// on mobile, without argument we capture fullscreen in OpenGL accelerated mode
// you can still use the desktop mode and capture a target DisplayObject, but it will be slower
//_myEncoder.capture();
_myEncoder.capture();
}
// Display video encoding progress for our users (doesn't work precisely on iOS and Android)
if (_myEncoder.getEncodingProgress() > 0) {
//ProgressBar(_UI.encoding_status.progress).setProgress(myEncoder.getEncodingProgress(), 1);
}
}
// trying to finish - this is important, we must ask the encoder if its finished and can give us the data
// don't forget in this example the encoder is running in background on another thread, so it can be delayed behind the capture phase
if (_state == "tryFinish")
{
// during this phase we also need to update the progress bar (for iOS)
//ProgressBar(_UI.encoding_status.progress).setProgress(myEncoder.getEncodingProgress(), 1);
// finish
_myEncoder.finish();
_state = "encoding";
}
// show encoding progress
if (_state == "encoding")
{
//ProgressBar(_UI.encoding_status.progress).setProgress(myEncoder.getEncodingProgress(), 1);
}
}
// FlashyWrappers status event handler
private function onStatus(e: StatusEvent): void
{
// the encoder class is ready for frames
if (e.code == "ready") {
// For Flash, scale down the video dimensions by half for faster recoding. Keep in mind that encoding videos in Flash
// is really slow, in fact multiple times slower than recoding on mobile devices with HW accelerated video encoding.
// If you don't need to be in web browser, always target AIR Windows / AIR Mac which can take advantage of FW native
// extensions (ANE's).
if (_myEncoder.platform == FWVideoEncoder.PLATFORM_FLASH)
{
_myEncoder.setDimensions(_VIDEO_W, _VIDEO_H);
}
CONFIG::cfg_isair {
PermissionCheckManager.instance.checkPermission(PermissionCheckManager.TYPE_MIC, startRecording, function():void { trace("Microphone permissions not granted"); });
}//CONFIG::cfg_isair
}
// FW was started
if (e.code == FWVideoEncoder.STATUS_STARTED)
{
// add frame listener - start sending frames
//addEventListener(Event.ENTER_FRAME, app_enterFrame);
}
// video is ready
if (e.code == FWVideoEncoder.STATUS_FINISHED)
{
// enable the button
// set the button to "save" mode because we can save the video now
_state = "none";
_mode = "save"
btn_save.enabled = true;
}
// encoding was cancelled for some reason, mobile only
if (e.code == FWVideoEncoder.STATUS_STOPPED)
{
trace("Encoding was forced to stop. Please try again.");
// prepare for another recording
_state = "none";
_mode = "record";
btn_save.label = "Start recording";
btn_save.enabled = true;
}
// mobile only
if (e.code == FWVideoEncoder.STATUS_GALLERY_SAVED)
{
trace("Saved successfully to media gallery");
prepareForRecording();
}
// mobile only
if (e.code == FWVideoEncoder.STATUS_GALLERY_FAILED)
{
trace("Saving to media gallery failed.");
prepareForRecording();
}
}
// reinitialize recording (notice the myEncoder.init is there)
private function initRecording(): void
{
// here we load the encoder
if (CONFIG::cfg_container == 'mp4')
{
_myEncoder.load("../../../lib/FlashPlayer/mp4/");
}
else
{
_myEncoder.load("../../../lib/FlashPlayer/ogg/");
// save proper extension
if (_myEncoder.platform == FWVideoEncoder.PLATFORM_FLASH) _extension = 'ogv';
}
}
private function startRecording(): void
{
// On mobile, this automatically uses GPU realtime screen recording - the fastest possible method
_myEncoder.start(20, FWVideoEncoder.AUDIO_MICROPHONE, true);
// Use workaround CPU realtime sreen recording in case you see any visual glitches (most likely on Android)
// Do not be fooled by the 3rd argument(realtime) set to "false", the other two method calls force recording to behave like realtime.
/* myEncoder.start(20, FWVideoEncoder.AUDIO_MICROPHONE, false, 0, 0, 1000000);
myEncoder.forcePTSMode(FWVideoEncoder.PTS_REALTIME);
myEncoder.forceFramedropMode(FWVideoEncoder.FRAMEDROP_ON);*/
}
private function prepareForRecording(): void
{
_mode = "record";
btn_save.label = "Start recording";
btn_save.enabled = true;
}
// file was saved
private function saveCompleteHandler(e: Event): void
{
trace("File was saved!");
}
private function saveIOErrorHandler(e: IOErrorEvent): void
{
trace("Error while saving the video!");
btn_save.enabled = false;
}
// save button click handler
private function onSaveClick(e:MouseEvent): void
{
if (_mode == "record")
{
initRecording();
_state = "record";
_mode = "stop";
btn_save.label = "Stop recording";
}
else
{
if (_mode == "stop")
{
_state = "tryFinish";
btn_save.label = "Save video";
btn_save.enabled = false;
}
if (_mode == "save")
{
// get the final video
var bo: ByteArray = _myEncoder.getVideo();
trace("Saving " + bo.length + " bytes...");
if (_myEncoder.platform != FWVideoEncoder.PLATFORM_IOS && _myEncoder.platform != FWVideoEncoder.PLATFORM_ANDROID)
{
var saveFile: FileReference = new FileReference();
saveFile.addEventListener(Event.COMPLETE, saveCompleteHandler);
saveFile.addEventListener(IOErrorEvent.IO_ERROR, saveIOErrorHandler);
saveFile.save(bo, "video." + _extension);
}
else
{
// AIR only
CONFIG::cfg_isair {
var file: File;
if (_myEncoder.platform == FWVideoEncoder.PLATFORM_IOS || _myEncoder.platform == FWVideoEncoder.PLATFORM_ANDROID)
{
_myEncoder.saveToGallery("video.mp4", "FW camera test");
}
btn_save.enabled = false;
}
}
// on mobile we saved to gallery and so there we'll need to wait for the event telling us its done
// otherwise we can proceed
if (_myEncoder.platform != FWVideoEncoder.PLATFORM_IOS && _myEncoder.platform != FWVideoEncoder.PLATFORM_ANDROID)
{
prepareForRecording();
}
}
}
}
private var ticks:uint = 0;
private var last:uint = getTimer();
private function getFps():void
{
ticks++;
var now:uint = getTimer();
var delta:uint = now - last;
if (delta >= 1000)
{
//trace(ticks / delta * 1000+" ticks:"+ticks+" delta:"+delta);
var fps:Number = ticks / delta * 1000;
lb_fps.text = fps.toFixed(1) + " fps";
ticks = 0;
last = now;
}
}
}
Hi? I am testing Free version of FlashyWrappers SDK 2.6. I have a question during the test.
Start recording screen video screen shakes. However, the actual recorded video is not trembling. Is there a way to solve this?
Testing was done with the code provided by default.
Test device 1: Samsung SM-T380 OS: Android 8.1.0 Test device 2: Samsung SM-T536 OS: Android 6.0.1
Note that the following devices operate normally.
Test Device 3: LG V700n OS: Android 4.4.2
Thank you.
2019.04.29
//------------------------------------------------------------------------------------------------- package { import com.kdmt.lsh.permission.PermissionCheckManager;
}