You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@flex.apache.org by "Gaius Coffey (JIRA)" <ji...@apache.org> on 2013/02/21 13:28:12 UTC
[jira] [Closed] (FLEX-33410) NetStream jumps when playing on
Android devices
[ https://issues.apache.org/jira/browse/FLEX-33410?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Gaius Coffey closed FLEX-33410.
-------------------------------
Resolution: Duplicate
I have created a bug report here: https://bugbase.adobe.com/index.cfm?event=bug&id=3504086
Please take a look, use my supplied test script, and vote for the issue if you can reproduce it.
G
> NetStream jumps when playing on Android devices
> -----------------------------------------------
>
> Key: FLEX-33410
> URL: https://issues.apache.org/jira/browse/FLEX-33410
> Project: Apache Flex
> Issue Type: Bug
> Components: Spark: VideoPlayer
> Environment: Android mobile
> Reporter: Gaius Coffey
> Priority: Blocker
> Labels: netstream, osmf, video
>
> When playing videos using OSMF on Android devices, NetStream will skip the final few seconds. This makes it impossible to use video commercially.
> Steps to reproduce:
> 1. Create a Mobile project similar to the below
> 2. Run it on an Android device and watch the traces as the video gets to the end
> Replicated on all of Motorola Xoom, Nexus 7, HTC Desire...
> package
> {
> import flash.display.Sprite;
> import flash.display.StageAlign;
> import flash.display.StageScaleMode;
> import flash.events.Event;
> import flash.events.NetDataEvent;
> import flash.events.NetStatusEvent;
> import flash.net.NetStream;
> import flash.text.TextField;
> import flash.utils.Dictionary;
> import flash.utils.getTimer;
>
> import org.osmf.elements.ProxyElement;
> import org.osmf.elements.SerialElement;
> import org.osmf.elements.VideoElement;
> import org.osmf.events.MediaErrorEvent;
> import org.osmf.events.MediaPlayerStateChangeEvent;
> import org.osmf.media.MediaElement;
> import org.osmf.media.MediaPlayerSprite;
> import org.osmf.media.URLResource;
> import org.osmf.net.NetStreamLoadTrait;
> import org.osmf.net.NetStreamSwitchManagerBase;
> import org.osmf.traits.MediaTraitType;
> import org.osmf.traits.TimeTrait;
> [SWF(backgroundColor="#272727")]
> public class DebugAdvert extends Sprite
> {
> public function DebugAdvert()
> {
> super();
>
> // support autoOrients
> stage.align = StageAlign.TOP_LEFT;
> stage.scaleMode = StageScaleMode.NO_SCALE;
> stage.addEventListener(Event.RESIZE,doResize);
>
> // Textfield for logging
> tf = new TextField;
> tf.backgroundColor = 0xffffff;
> tf.multiline = true;
> tf.background = true;
> addChild(tf);
>
> // MediaPlayerSprite for video display
> mp = new MediaPlayerSprite();
> addChild(mp);
>
> // Position everything for the first time.
> doResize(null);
>
> // Add OEF listener to update traces
> addEventListener(Event.ENTER_FRAME,everyFrame);
>
> // Start the process going by adding a video
> var ur:URLResource = new URLResource(TEST_VIDEO_CONTENT);
> var me:MediaElement = mp.mediaFactory.createMediaElement(ur);
> mp.media = me;
> }
> public const TEST_VIDEO_CONTENT:String = "videos/TOYOTA_YARIS_MORE_VER2_30_TV.mp4";
> /**
> * The threshold for a step between frames that will be logged as an error.
> * This should ideally be the same as frame interval, but will
> * vary with device performance. However, even allowing for that,
> * it should NEVER be as high as half a second, let alone the
> * three or more seconds seen on many Android devices.
> */
> public const ERROR_THRESHOLD:Number = 0.5;
> /**
> * Number of log records to maintain for display in the textfield
> */
> public const NUM_ERROR_RECORDS:uint = 10;
> /**
> * On resize, keep the textfield visible and media player sized.
> */
> protected function doResize(event:Event):void {
> var w:Number = Math.min(stage.fullScreenWidth,stage.fullScreenHeight);
> var h:Number = w;
> if(stage.width>stage.height) {
> mp.width = w;
> mp.height = h;
> tf.x = w;
> tf.y = 0;
> } else {
> mp.width = w;
> mp.height = h;
> tf.x = 0;
> tf.y = h;
> }
> tf.height = stage.fullScreenHeight-tf.y;
> tf.width = stage.fullScreenWidth-tf.x;
> }
> // Ref to media player
> protected var mp:MediaPlayerSprite;
> // Ref to tf for logging.
> protected var tf:TextField;
> /**
> * Ensure that, whatever the media we are playing, we can find
> * the relevant VideoElement to locate NetStream
> */
> protected function recurseForProxies(element:MediaElement):MediaElement {
> // if(element is RTEParallelAdTagElement) return (element as RTEParallelAdTagElement).displayElement;
> if(element is ProxyElement) return recurseForProxies((element as ProxyElement).proxiedElement);
> if(element is SerialElement) return recurseForProxies((element as SerialElement).currentChild);
> return element;
> }
> /**
> * On every frame, compare position to previous and log any jumps that are
> * exceptional.
> */
> protected function everyFrame(event:Event):void {
> // Find underlying media element. Return if empty.
> var proxiedElement:MediaElement = recurseForProxies(mp.media);
> if(!proxiedElement) return;
> // Check it has a time trait. Return if not.
> var tt:TimeTrait = proxiedElement.getTrait(MediaTraitType.TIME) as TimeTrait;
> if(!tt) return;
> // Return if tt is not yet inited.
> if(isNaN(tt.duration)) return;
> // Store current times for reference.
> var newTimes:Object = {currentTime:tt.currentTime,duration:tt.duration,percents:0};
> // Show trace only if there is a previous record to compare against.
> var showTrace:Boolean = false;
> // If last times is not set, then set it to current record and initialise for first trace of this media
> if(!lastTimes) {
> showTrace = true;
> lastTimes = newTimes;
> }
> // If time stamps are different to previous, then we have a change, so ensure showTrace is true
> if(tt.currentTime!=lastTimes.currentTime || tt.duration!=lastTimes.duration) showTrace = true;
> // If show trace, then generate a log record
> if(showTrace) {
> // Gap between current and previous trait values
> var tGap:Number = tt.currentTime-lastTimes.currentTime;
> var dGap:Number = tt.duration-lastTimes.duration;
>
> var remaining:Number = tt.duration-tt.currentTime;
> var stepTime:Number = tt.currentTime-lastTimes.currentTime;
> var gap:Number = 0;
> var nstime:Number = -1;
> if(proxiedElement is VideoElement) {
> var ve:VideoElement = proxiedElement as VideoElement;
> var nslt:NetStreamLoadTrait = ve.getTrait(MediaTraitType.LOAD) as NetStreamLoadTrait;
> var ns:NetStream = nslt ? nslt.netStream : null;
> if(ns) {
> if(!listenedNs[ns]) {
> // Clear database and add listeners if found
> for each(var nss:NetStream in listenedNs) {
> nss.removeEventListener(NetStatusEvent.NET_STATUS,traceNetStatus);
> nss.removeEventListener(NetDataEvent.MEDIA_TYPE_DATA,traceNetData);
> delete listenedNs[nss];
> }
> ns.addEventListener(NetStatusEvent.NET_STATUS,traceNetStatus,false,1,false);
> ns.addEventListener(NetDataEvent.MEDIA_TYPE_DATA,traceNetData);
>
> listenedNs[ns] = ns;
> }
> nstime = ns.time;
> }
> }
> // Check for an error that we need to report.
> var errorStr:String = "";
> if(stepTime>ERROR_THRESHOLD) errorStr = "\n[ERROR NETSTREAM HAS JUMPED BY "+stepTime+" SECONDS FOR NO ADEQUATELY EXPLAINED REASON.]";
> // Build the log record
> var log:String = "[trait d:\t"+tt.duration.toFixed(3)+"\tt:\t"+tt.currentTime.toFixed(3)+"\t]\t[NetStream t:\t"+nstime.toFixed(3)+"\t]\t[Step:\t"+stepTime.toFixed(3)+"\tremaining:\t"+remaining.toFixed(3)+"]";
> if(previousNetStatus||previousNetData||errorStr) log += "\n"+previousNetStatus+"\t"+previousNetData+errorStr;
> // Log it and update text field
> registerLog(log,errorStr!="");
> updateTf();
> previousNetData = previousNetStatus = "";
> // Store current times for comparison later.
> lastTimes = newTimes;
> }
> }
> /**
> * Listens for NetDataEvent and adds a trace value when it occurs.
> */
> protected function traceNetData(event:NetDataEvent):void {
> var ns:NetStream = event.target as NetStream;
> var addStr:String = event.info.handler+" @ "+getTimer()+" ms";
> addStr += JSON.stringify(event.info.args);
> if(previousNetData) previousNetData += " | ";
> previousNetData += addStr;
> }
> protected var previousNetData:String = "";
> /**
> * Listens for NetStatusEvent and adds a trace value when it occurs.
> */
> protected function traceNetStatus(event:NetStatusEvent):void {
> var addStr:String = event.info.code+" @ "+getTimer()+" ms";
> if(previousNetStatus) previousNetStatus = previousNetStatus+","+addStr;
> else previousNetStatus = addStr;
> }
> protected var previousNetStatus:String = "";
> /**
> * List of NetStreams that we are monitoring.
> */
> protected var listenedNs:Dictionary = new Dictionary;
> /**
> * Previous time stamps for comparison.
> */
> protected var lastTimes:Object;
> /**
> * Display log records with errors at the top.
> */
> protected function updateTf():void {
> trace("latestResultsStr: "+logs[0]);
> tf.text = errorLogs.join("\n")+"\n---\n"+logs.join("\n");
> }
> /**
> * List of last NUM_ERROR_RECORDS records.
> */
> protected var logs:Vector.<String> = new Vector.<String>;
> /**
> * List of error records.
> */
> protected var errorLogs:Vector.<String> = new Vector.<String>;
> protected function registerLog(s:String,isError:Boolean):void {
> if(isError) {
> errorLogs.unshift("ERROR "+s);
> }
> logs.unshift(s);
> logs.length = NUM_ERROR_RECORDS;
> }
> }
> }
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira