Flash update 12 broke my video recorder - actionscript-3

When flash pushed the 12.0.0.70 version to Chrome it broke my video recorder.
According to the patch notes here, one thing was changed that might have broken my flash-based recorder
[3689061] [Video] Resolves an issue injected in Flash Player
11.9.900.170 that caused the video buffer to no longer be filled if the buffer was emptied while playing an RTMP stream
My recorder breaks when it's time to stop the stream and save the video to the Adobe Media Server.
I tried debugging it with the 12.0.0.70 flash debugger, but it doesn't crash when I'm using the debugger. Only when using the non-debugger Chrome version does it crash.
I can't debug it and get any useful information out of my swf, apart from making a bunch of external calls to console.log to see where it fails.
If someone also ran into a similar issue with flash-based, media-server-connected webcam recorders, and can guess at what might fix my problem, I'd be grateful.
I'm building this swf with Flex 4.6.0
Here's the function that stops the video recorder.
public function doStop():void{
if(status=="paused"){
doResume();
}
rectColor.color=0x000000;
rectColor.alpha=1;
var timer:Timer=new Timer(1 * 10);
timer.addEventListener(TimerEvent.TIMER,function(e:TimerEvent):void{
timer.stop();
timer.reset();
myns.close();
myTimer.stop();
if(!thumbBeginning){
if(status=="recording"){
takeScreenShot();
}
}else{
if(status=="recording"){
recordingTime = formatTime(realTime);
recordingLength = myTimer.currentCount;
if(!redoFlag){
ExternalInterface.call("CTRecorder.stopOk");
myTime.text = formatTime(0);
VD1.attachCamera(myCam);
setState("ready");
status = "stopped, ready"
playbackTimer.reset();
msg(recordingTime);
recording=false;
pauseTime=0;
}else{
pauseTime=0;
myTime.text = formatTime(0);
VD1.attachCamera(myCam);
playbackTimer.reset();
msg(recordingTime);
recording=false;
}
}
if(shutterGroup.visible){
toggleShutter();
}
myTimer.stop();
myTimer.reset();
if(redoFlag){
doRecord();
redoFlag=false;
trace("redoFlag turned off");
}
}
rectColor.alpha=.5;
});
timer.start();
}

This isn't really an answer, but it's too long for a comment.
"I can't debug it" - it only breaks in the release version? Is the release version the pepper plugin (i.e. Chrome's version of Flash), and the debug is the NPAPI plugin (i.e. Adobe's version)?
A likely candidate for where it's breaking is the ExternalInterface.call("CTRecorder.stopOk"); call. Are you testing this locally, or remotely? If locally, then you might be running into this bug: https://code.google.com/p/chromium/issues/detail?id=137734 where the Flash <-> JS communication is broken because of Trusted locations being ignored in PPAPI flash. In any case, try installing the release NPAPI version of Flash and see does it still crash (you can verify which one is running by visiting chrome://plugins/)
To help debugging the release version, you need a logging system - instead of making trace() calls, you call a custom log() function, that, as well as trace()ing, also stores the message somewhere, like in an Array. Then, in your SWF, when you hit a certain key, show a TextField on the screen, and populate it with your log() messages. That way, you'll be able to see trace() statements in release mode.
Also, don't forget to listen to any relative error events and thrown exceptions - ExternalInterface.call() will throw an Error and SecurityError for example. You can also set the marshallExceptions property, which will pass ActionScript exceptions to the browser and JavaScript exceptions to the player: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html#marshallExceptions
Finally, add a listener for the UncaughtErrorEvent.UNCAUGHT_ERROR event on your main class, which will catch any uncaught thrown errors (funnily enough), which will at least mean that your app doesn't collapse:
mainClass.loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, this._onUncaughtErrorEvent );
private function _onUncaughtErrorEvent( e:UncaughtErrorEvent ):void
{
var message:String = null;
var stackTrace:String = null;
// get the message
if ( e.error is Error )
{
message = ( e.error as Error ).message;
try { stackTrace = ( e.error as Error ).getStackTrace(); }
catch ( error:Error ) { stackTrace = "No stack trace"; }
}
else if ( e.error is ErrorEvent )
message = ( e.error as ErrorEvent ).text;
else
message = e.error.toString();
// show an alert
trace( "An uncaught exception has occurred: " + e.errorID + ": " + e.type + ": " + message + ", stack:\n" + stackTrace );
e.preventDefault();
}

Related

AMS doesn't receive unpublish command SOMETIMES over rtmpt

This one has had me going for a week at least. I am trying to record a video file to AMS. It works great almost all of the time, except about 1 in 10 or 15 recording sessions, I never receive 'NetStream.Unpublish.Success' on my netstream from AMS when I close the stream. I am connecting to AMS using rtmpt when this happens, it seems to work fine over rtmp. Also, it seems like this only happens in safari on mac, but since its so intermittent I don't really trust that. Here is my basic flow:
// just a way to use promises with netStatusEvents
private function netListener(code:String, netObject:*):Promise {
var deferred:Deferred = new Deferred();
var netStatusHandler:Function = function (event:NetStatusEvent):void {
if (event.info.level == 'error') {
deferred.reject(event);
} else if (event.info.code == code) {
deferred.resolve(netObject);
// we want this to be a one time listener since the connection can swap between record/playback
netObject.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
}
};
netObject.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
return deferred.promise;
}
// set up for recording
private function initRecord():void {
Settings.recordFile = Settings.uniquePrefix + (new Date()).getTime();
// detach any existing NetStream from the video
_view.video.attachNetStream(null);
// dispose of existing NetStream
if (_videoStream) {
_videoStream.dispose();
_videoStream = null;
}
// disconnect before connecting anew
(_nc.connected ? netListener('NetConnection.Connect.Closed', _nc) : Promise.when(_nc))
.then(function (nc:NetConnection):void {
netListener('NetConnection.Connect.Success', _nc)
.then(function (nc:NetConnection):void {
_view.video.attachCamera(_webcam);
// get new NetStream
_videoStream = getNetStream(_nc);
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecordReady", true);
}, function(error:NetStatusEvent):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onError", error.info);
});
_nc.connect(Settings.recordServer);
}); // end ncClose
if (_nc.connected) _nc.close();
}
// stop recording
private function stop():void {
netListener('NetStream.Unpublish.Success', _videoStream)
.then(function (ns:NetStream):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecordStop", Settings.recordFile);
});
_videoStream.attachCamera(null);
_videoStream.attachAudio(null);
_videoStream.close();
}
// start recording
private function record():void {
netListener('NetStream.Publish.Start', _videoStream)
.then(function (ns:NetStream):void {
ExternalInterface.call("CTplayer." + Settings.instanceName + ".onRecording");
});
_videoStream.attachCamera(_webcam);
_videoStream.attachAudio(_microphone);
_videoStream.publish(Settings.recordFile, "record"); // fires NetStream.Publish.Success
}
Update
I am now using a new NetConnection per connection attempt and also not forcing port 80 (see my 'answer' below). This has not solved my connection woes, only made the instances more infrequent. Now like every week or so I still have some random failure of ams or flash. Most recently someone made a recording and then flash player was unable to load the video for playback. The ams logs show a connection attempt and then nothing. There should at least be a play event logged for when i load the metadata. This is quite frustrating and impossible to debug.
I would try 2 distinct NetConnection objects, one for record and one for replay. This will remove your complexities around listeners adding/removing and connect/reconnect/disconnect logic and would IMO be cleaner.
NetConnections are cheap, and I've always used one per task at hand. The other advantage is that you can connect both at startup so the replay connection is ready instantly.
I've not seen a Promise used here before, but I'm not qualified to comment if that may cause a problem or not.
I think my issue was connecting over port 80. I originally thought I had to use port 80 with rtmpt, so I set my Settings.recordServer variable to rtmpt://myamsserver.net:80/app. I'm now using a shotgun approach where I try a bunch of port/protocol combos at once and pick the first one to connect. It is almost always picking port 443 over rtmpt, which seems much faster and more stable all around than 80, and I haven't had this issue since. It could also be due to not reusing the same NetConnection object like Stefan suggested, its hard to say.

sendToURL not working in flashplayer 14

This piece of code is working in flashplayer 11 but it's not working in flashplayer 14.
AS3 code :
private function savePDF(pdfBinary:ByteArray, urlString:String):void{
try{
//result comes back as binary, create a new URL request and pass it back to the server
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var sendRequest:URLRequest = new URLRequest(urlString);
sendRequest.requestHeaders.push(header);
sendRequest.method = URLRequestMethod.POST;
sendRequest.data = pdfBinary;
sendToURL(sendRequest);
} catch (e:Error) {
// handle error here
trace("Error in savePDF "+e.message);
trace("StackTrace : "+e.getStackTrace());
}
}
and these are the errors I got :
Error in savePDF Error #3769: Security sandbox violation: Only simple headers can be used with navigateToUrl() or sendToUrl().
StackTrace : SecurityError: Error #3769: Security sandbox violation: Only simple headers can be used with navigateToUrl() or sendToUrl().
at global/flash.net::sendToURL()
at Export2Publish/savePDF()[my_project_dir\src\Export2Publish.mxml:158]
at Export2Publish/GeneratePDF()[my_project_dir\src\Export2Publish.mxml:386]
at Export2Publish/getUrl()[my_project_dir\src\Export2Publish.mxml:138]
at Export2Publish/___Export2Publish_Application1_creationComplete()[my_project_dir\src\Export2Publish.mxml:3]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[my_framework_dir\src\mx\core\UIComponent.as:9051]
at mx.core::UIComponent/set initialized()[my_framework_dir\src\mx\core\UIComponent.as:1167]
at mx.managers::LayoutManager/doPhasedInstantiation()[my_framework_dir\src\mx\managers\LayoutManager.as:698]
at Function/http://adobe.com/AS3/2006/builtin::apply()
at mx.core::UIComponent/callLaterDispatcher2()[my_framework_dir\src\mx\core\UIComponent.as:8460]
at mx.core::UIComponent/callLaterDispatcher()[my_framework_dir\src\mx\core\UIComponent.as:8403]
Any fix for this issue ?
You can start by using a try and catch like this :
try {
sendToURL(request);
}
catch (e:Error) {
// handle error here
}
If the problem is not visible on dev environment, I recommand you to install a flash debug player which you can download here : Flash Player Downloads to see what kind of error your code will fire.
If your code is fine, in dev and prod environment, you should debug your server side script.

System.UnauthorizedAccessException with text-to-speech in Windows Phone 8

I have the following piece of code for using text to speech feature in Windows Phone 8. I am using ssml, with bookmarks. But when changing any UI element in the Bookmark event called function, raises Unauthorized Exception.
private void Initialise_synthesizer()
{
this.synthesizer = new SpeechSynthesizer();
synthesizer.BookmarkReached += new TypedEventHandler<SpeechSynthesizer, SpeechBookmarkReachedEventArgs>
(BookmarkReached);
}
void BookmarkReached(object sender, SpeechBookmarkReachedEventArgs e)
{
Debugger.Log(1, "Info", e.Bookmark + " mark reached\n");
switch (e.Bookmark)
{
case "START":
cur = start;
break;
case "LINE_BREAK":
cur++;
break;
}
**error here** t1.Text = cur.ToString();
}
But on running it gives the following error
A first chance exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
An exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll and wasn't handled before a managed/native boundary
Invalid cross-thread access.
Any idea how to fix this error, or any work around.
Just got the answer.
Since the synthesizer.SpeakSsmlAsync() is an async function, to perform UI operations Dispatcher has to be used, something like this -
Dispatcher.BeginInvoke(() =>
t1.Text = cur.ToString());
It's pretty much unrelated to the speech recognition. It seems that it's related to accessing elements which are on the UI thread from a different thread.
Try this:
Dispatcher.BeginInvoke(() =>
{
t1.Text = cur.ToString();
}
);
From AppManifest.xml turn on capability ID_CAP_SPEECH_RECOGNITION.

How to browse mobile directory in flex?

I have captured 3 videos on my mobile which is by default stored on the phone gallery (Gallery/videos/). I have to play these 3 videos in one of my flex mobile application. How can I get the videos to the flex project? if I need to browse the mobile directory means kindly help me with some code to do so.
I too am looking for an answer to this question. Right now, based on other Stackoverflow discussions, exhaustive perusal of tutorials and Adobe documentation, and comments to both (often the more useful resource), I'm coming to the conclusion that it's not possible.
you can use CameraRoll.browseForImage() and open the iOS gallery of photos to see all entities of MediaType.IMAGE, but it will not show you MediaType.VIDEO
you can use CameraUI to launch the system camera by delegation and that returns a MediaPromise, but as far as I can tell, it does not save the video you capture anywhere, and I cannot find a way to access the captured video using the MediaPromise (at least using the Loader class)
Here's my code as a hint in that direction. The second code block is using the CameraRoll to browseForImage() but there is no browseForVideo() in the API.
if(CameraUI.isSupported)
{
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.addEventListener(Event.CANCEL, cameraCanceled);
camera.addEventListener(ErrorEvent.ERROR, cameraError);
camera.launch(MediaType.VIDEO);
}
else
{
statusText.text = "Camera not supported on this device.";
startTimer();
}
if (CameraRoll.supportsBrowseForImage)
{
roll = new CameraRoll();
roll.addEventListener(MediaEvent.SELECT, cameraRollEventComplete);
roll.addEventListener(Event.CANCEL, cameraCanceled);
roll.addEventListener(ErrorEvent.ERROR, cameraError);
roll.browseForImage();
}
else
{
statusText.text = "Camera roll not supported on this device.";
startTimer();
}
I've since found that Videos captured using the delegated system camera are stored in a temporary storage location that iOS -DOES!- allow access to. (I was pleasantly shocked.)
The Captured video is not added to the device's Camera Roll as other videos captured using the iOS System Camera app, so it's not enough to capture video and expect to be able to access it later (if, for instance, CameraRoll.browseForVideo() is ever added to the API.
Therefore, you have to 'get while the getting is good' and move the file from the temporary storage location to some non-volatile location such as ApplicationStorageDirectory or the user's Documents directory (The only options in iOS I think).
The MediaPromise... I think... is completely useless for accessing the video via any direct progressive loader/streamer method, but still provides the location/url/path/filename of the temporary file so you can perform File operations on it.
Ironic that there are tutorials for getting around the lack of a file location/url/path/filename in the MediaPromise when using CameraRoll.browseForImage()... and that method is to use a loader class to load the image content (which you can then write out to a file), but when taking video, the video content is not accessible, and instead a file location/url/path/filename is provided. Ironic that there are nearly no resources I was able to find to help with this also. grumble
I'm going to include some code chunks w/o really editing them to strip out extraneous bits because it's way past when I need to be in bed, but I wanted you to have this. I may come clean it up later.
This section is in a Spark SkinnablePopUpContainer and I use the same click event for several buttons, thus the below 'case' is in the switch-case in that event handler function.
In case you are not familiar, the 'close(true, data)' is the method to close the SkinnablePopUpContainer, tell the parent/owner that the container was closed purposefully and that it should look for the data object being shared back (i.e., there are changes to be 'commit'ed).
case "cameraVideo":
{
if(CameraUI.isSupported)
{
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.addEventListener(Event.CANCEL, cameraCanceled);
camera.addEventListener(ErrorEvent.ERROR, cameraError);
camera.launch(MediaType.VIDEO);
}
else
{
statusText.text = "Camera not supported on this device.";
startTimer();
}
break;
}
protected function cameraCanceled(event:Event):void
{
statusText.text = "Camera access canceled by user.";
startTimer();
}
protected function cameraError(event:ErrorEvent):void
{
statusText.text = "There was an error while trying to use the camera.";
startTimer();
}
protected function videoMediaEventComplete(event:MediaEvent):void
{
statusText.text="Preparing captured video...";
camera.removeEventListener(MediaEvent.COMPLETE, videoMediaEventComplete);
camera.removeEventListener(Event.CANCEL, cameraCanceled);
camera.removeEventListener(ErrorEvent.ERROR, cameraError);
var media:MediaPromise = event.data;
data.MediaType = MediaType.VIDEO;
data.MediaPromise = media;
data.source = "camera video";
close(true,data)
}
This section is the Actionscript in the close handler of the parent/owner of the SkinnablePopUpContainer (truncated once the useful code is included)
private function choosePictureLightboxClosed(event:PopUpEvent):void
{
imageButtonsActive = false;
if(event.commit)
{
this.data = event.data as Object;
filters = new Array();
selection = true;
switch(data.MediaType)
{
case MediaType.VIDEO:
{
mediaType = "video";
trace(data.MediaPromise.file.url + " - " + data.MediaPromise.relativePath + " - " +data.MediaPromise.mediaType);
var sourceFile:File = new File(data.MediaPromise.file.url);
var destinationFile:File = File.applicationStorageDirectory.resolvePath("User" +parentApplication.userid);
if(destinationFile.exists && !destinationFile.isDirectory)
{
destinationFile.deleteFile();
}
destinationFile.createDirectory();
destinationFile = destinationFile.resolvePath("Videos");
if(destinationFile.exists && !destinationFile.isDirectory)
{
destinationFile.deleteFile();
}
destinationFile.createDirectory();
destinationFile = destinationFile.resolvePath(parentApplication.userid+"Video"+new Date().getTime()+".mov");
trace(destinationFile.nativePath);
sourceFile.moveTo(destinationFile,true);
break;
}
I sure do hope this helps. This has been a very frustrating (and costly in terms of our project being government grant funded and having deadlines we utterly failed to meet), and I very much hope that these hard-won solutions might help others avoid the same experience.

How to get error message on a HTML5 Application Cache Error event?

During the caching of my offline webapp I receive a totally valid error which is displayed in the browser console like this:
Application Cache Error event: Manifest changed during update, scheduling retry
I can add a Listener to be informed that an error has occured.
window.applicationCache.addEventListener('error', function(e){
//handle error here
}, false);
How can I get the error detail, in this case "Manifest changed during update, scheduling retry"?
You must use window.onerror. The callback can have three parameters:
Error message (string)
Url where error was raised (string)
Line number where error was raised (number)
check this for more information:
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers.onerror
Still a valid issue today. In my example, my error log does not return anything. I am using IE11.
<html xmlns="http://www.w3.org/1999/xhtml" manifest="icozum.appcache">
onChecking events fires but then onError with cache status = 0 which is nocached.
window.applicationCache.onchecking = function (e) {
var doc = document.getElementById("cachestatus");
if (doc != null) {
doc.innerHTML += "Checking the cache.\n";
}
}
Then onError
window.applicationCache.onerror = function (e) {
var doc = document.getElementById("cachestatus");
if (doc != null) {
doc.innerHTML += "Cache error occurred." + applicationCache.status.toString() + "\n";
console.log(e);
console.log("test");
}
}
The output on the screen is
Checking the cache.
Cache error occurred.0
There is no detail info about the error in onError event handler. I got the real error by pressing the F12. Here is the screen shot. Is there any way to capture this much detail in onError event handler.
And finally I figured out the problem. The error is not due to missing file. The app cache file does exist, however in windows , visual studio (2013)/IIS does not recognize the extension .appcache. The following section needs to be added to the web.config file.
<system.webServer>
<staticContent>
<mimeMap fileExtension=".appcache" mimeType="text/cache-manifest"/>
</staticContent>
</system.webServer>