Details
-
Bug
-
Status: Closed
-
Blocker
-
Resolution: Duplicate
-
None
-
None
-
Android mobile
Description
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()
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):voidUnknown macro: { var w}// 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};
{ showTrace = true; lastTimes = newTimes; }
// 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)// 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)
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):voidUnknown macro: { if(isError) { errorLogs.unshift("ERROR "+s); } logs.unshift(s); logs.length = NUM_ERROR_RECORDS; }}
}