rainbowcreatures / FlashyWrappers

AIR / Flash video recording SDK
17 stars 10 forks source link

Video screen trembling at the start of recording #52

Closed koreaflash closed 5 years ago

koreaflash commented 5 years ago

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;

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;
        }
    } 

}

}

rainbowcreatures commented 5 years ago

Resolved over e-mail, closing

koreaflash commented 5 years ago

Thank you.