UISlider with buffering progress - AVPlayer - avplayer

I'm using AVPlayer to stream audio from a server and what I want to do now is to show a custom UISlider who shows the progress of the buffering.
Anyone come up with this? Please give me solution.
Now player load whole duration in once and show in UISlider.

you need to use preferredforwardbufferduration
This property defines the preferred forward buffer duration in seconds. If set to 0, the player will choose an appropriate level of buffering for most use cases. Setting this property to a low value will increase the chance that playback will stall and re-buffer, while setting it to a high value will increase demand on system resources.
also check here for detailed example of how to use : Is it possible to pause and resume buffering of AVPlayer?
inside your player you need to observe time range using below function
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
like this:
let timeRanges = change![NSKeyValueChangeKey.newKey] as? NSArray
if timeRanges != nil && timeRanges!.count != 0 {
DispatchQueue.main.async {
let cmTimeRange = (timeRanges![0] as AnyObject).timeRangeValue as CMTimeRange
self.playerView.timeSlider.bufferValue = Float(CMTimeGetSeconds(cmTimeRange.start) + CMTimeGetSeconds(cmTimeRange.duration))
}
}
I have a custom slider with a variable I defined as bufferValue you can override func draw(_ rect: CGRect)inside your slider and apply changes while buffer is progressing.
I get draw to call anytime bufferValue changes by calling self.setNeedsDisplay()inside didSet
#IBInspectable var bufferValue : Float = 0 {
didSet {
if(bufferValue < 0 ) {
bufferValue = 0
} else if (bufferValue > maximumValue) {
bufferValue = maximumValue
}
self.setNeedsDisplay()
}
}

Related

Phaser 3 restarting tween with updated start values

I have 4 reusable tweens that should move the target relative to its current position.
Each of them is basically the same as this example one, but for each cardinal direction:
goLeft = this.tweens.add({
targets: gamePieceSprite,
x: {value: '-=64'},
duration: 1000,
paused: true
});
Since I want to be able to use these tweens multiple times, I use the restart() method, which I want to play the tween from the sprite's current position, but even when I change the position of the sprite, when I call the function to restart the tween it plays from the coordinates at which it first played and not where the targeted sprite currently is.
var pathCounter = 0;
testPathText.on('pointerdown', function(){
if (pathCounter % 2 == 0)
{
gamePieceSprite.setX(startTile.x);
gamePieceSprite.setY(startTile.y);
}
else
{
if (levelStartRotation[level] == '1')
{
goLeft.restart();
}
else if (levelStartRotation[level] == '2')
{
goUp.restart();
}
else if (levelStartRotation[level] == '3')
{
goDown.restart();
}
else if (levelStartRotation[level] == '4')
{
goRight.restart();
}
}
pathCounter++;
});
Is there any way for me to update the start point of the tween so that restarting it doesn't just start from where it started before?
I was also frustrated trying to figure this one out (and it looks like there still isn't a solution, almost one year later). I have not found any methods or best practices to do so yet, but by digging through the tween object, I discovered you can access the properties of a tween in its data array. Since it's an array, if you have multiple properties, you will have to find the property by comparing it's key value. If you only have one, simply get data[0]. The property has a start field that you can set.
In your case, try:
...
if (levelStartRotation[level] == '1')
{
goLeft.data[0].start = startTile.x;
goLeft.restart();
}
...

How to improve canvas fabric.js performance with large number of Objects

I need make an App that has about 30k Objects, a user can Pan, Zoom or "Select on click" any of those objects.
Fabric.js Canvas is being used
I have done the same using SVG's and the svg-pan-zoom plugin (no Canvas Element) with better results
Problem: there is a significant Lag while Zooming, Panning or Object on Click
will removing Fabric.js improve performance?
will switching to WebGL improve performance?
Have tried Fabric specific options
fabric.Object.prototype.objectCaching = false;
fabric.Object.prototype.statefullCache = false;
fabric.Object.prototype.noScaleCache = true;
fabric.Object.prototype.needsItsOwnCache = false;
UPDATE
Heres the updated Fiddle
for reference :
canvas-vs-svg-vs-div Stackoverflow
Stackoverflow
Don't Render in IO EVENTS!
Though not a complete fix to the update speed this answer will about double the interaction speed.
A common, almost standard, mistake made with mouse and event interaction with the canvas (and DOM) is to delegate rendering to mouse/touch events. This is very bad practice as mouse events fire at much higher rates than the display can display. It becomes worse when your rendering time is high as you queue up mouse events (pseudo render events) and do a re render for every movement of the mouse
Note blocking code will stop mouse events but as soon as the engine is idle the mouse will start firing at full rate again.
Use the mouse events just to get the mouse state. Use an animation loop that is synced to the display to render only when needed and there is time available. Things like the wheel and mouse movement deltas should be recorded cumulatively.
mouse.dx += event.movementX;
mouse.dy += event.movementY;
mouse.wheel += event.wheelDelta;
And consume them in the main render loop...
function update(){
// ... code to use mouse
// consume deltas
mouse.x = mouse.y = mouse.wheel = 0;
...this ensures that the mouse state is accurately followed when you may have many mouse events between render updates.
Example, separating events from rendering.
Change you code in the fiddle you provided to the following, on my machine it about doubled the rendering speed (which is still very slow).
// from just after the function applyZoom replace all the code
var mouse = { // holds the mouse state
x : 0,
y : 0,
down : false,
w : 0,
delta : new fabric.Point(0,0),
}
// event just track mouse state
function zoom(e) {
if(e != null) { e.preventDefault() }
var evt=window.event || e;
mouse.x = e.offsetX;
mouse.y = e.offsetY;
mouse.w += evt.detail? evt.detail*(-120) : evt.wheelDelta;
return false;
}
canvas.on('mouse:up', function (e) { mouse.down = false });
canvas.on('mouse:out', function (e) { mouse.down = false });
canvas.on('mouse:down', function (e) { mouse.down = true });
canvas.on('mouse:move', function(e) {
if (e && e.e) {
mouse.delta.x += e.e.movementX;
mouse.delta.y += e.e.movementY;
}
});
// main animation loop
function update(){
if(mouse.w !== 0){ // if the wheel has moved do zoom
var curZoom = canvas.getZoom();
canvas.zoomToPoint(
{ x : mouse.x, y: mouse.y },
canvas.getZoom() + mouse.w / 4000
);
mouse.w = 0; // consume wheel delta
}else if(mouse.down) { // if mouse button down
canvas.relativePan(mouse.delta);
}
// consume mouse delta
mouse.delta.x = 0;
mouse.delta.y = 0;
requestAnimationFrame(update);
}
requestAnimationFrame(update);

Buggy and Slow scrolling when loading TableView images from CoreData

Problem :
The process of loading images to Table View using their Paths that who stored in Core Data DB works fine , but the user experience not going well. The scroll is slow and buggy.
Importants Notes :
For my local DB i use Core Data
Im not saving the image it self in the Core Data , only their path(the image name)
As for the table view DataSource , i use an array of type Person that contains an ID and Img name(TableView rows equal array.Count).
-This is the url im getting my Json from (Check it out) - Json Link
All the object inside the Core Data DB
As far as i know , i did all the UI Updates in the Main theard
After each check im reloading the tableview.
This are the steps that being taking in a right sequence :
Get the data using NSURLSession - DataTask. After that "parsing" it and check if each object(In a for loop) exists in the Core Data DB,and than appending his variables to the TableView datasource array , and reloading the data
1)
let request = NSMutableURLRequest(URL: dataSourceURL!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
if data != nil {
let datasourceDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSDictionary
var DataArray = datasourceDictionary["descisions"] as NSArray
//Convert it to useable array
for var i = 0 ; i < DataArray.count ; i++ {
let ID_s = DataArray[i]["ID"]! as Int
//For each Object from the Json array i'm check if he is exisitng in the local DB
var ConvertetdID = Int32(ID_s)
let object : Lockdown? = self.CheckIfObjectExistInDBbyID(ConvertetdID)
//CheckIfExists - if it does , it return an object with the correct values
if object != nil {
//exists - load file from Core Data
let imgname = object!.imgPath
let photoRecord = PhotoRecord(name:"\(ConvertetdID)", url:imgname)
self.photos.append(photoRecord)
//TableView object array (photos)
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
//After each check reload the tableView
}
}
}
}
task.resume()
Method that checks if he is exists or not in Core Data DB(the method receive the ID and returns object if exists and nil if not :
func CheckIfObjectExistInDBbyID(id : Int32) -> Lockdown? {
let appDelegate =
UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
var request : NSFetchRequest = NSFetchRequest(entityName: "Lockdown")
request.predicate = NSPredicate(format: "id = %d", id)
var error : NSError?
request.fetchLimit = 1
var count : Int = managedContext.countForFetchRequest(request,error: &error)
if count == 0 {
// println("Error : \(error?.localizedDescription)")
println("Object \(id) Dosent exist in CoreData")
return nil
}
println("Object \(id) exist in CoreData")
let result = managedContext.executeFetchRequest(request, error: &error) as NSArray!
let lockdown = result.objectAtIndex(0) as Lockdown
println(lockdown.id)
return lockdown
}
cellForRowAtIndexPath method
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as UITableViewCelllet photoDetails = photos[indexPath.row]
cell.textLabel.text = photoDetails.name as String
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
var myPathList : NSArray = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var myPath = myPathList[0] as String
myPath = myPath.stringByAppendingPathComponent("\(photoDetails.name).png")
var image : UIImage = UIImage(contentsOfFile: myPath)!
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
Any suggestions what is the cause of the problem?
You shouldn't always load the image from disk, it's slow. Instead, load the image from disk the first time and then store it in a cache (in memory). Check if the image is cached before loading it from disk.
You need to empty the cache if it gets too big or you get a memory warning.
Advanced versions can pre-cache some images for the cells that are just below the current scroll position.
You may be able to use a library for this, perhaps SDWebImage with file URLs (I haven't tried this).
The buggy part is because your async block doesn't check that the cell hasn't been reused before the image is loaded. If it has then the wrong image will appear on the reused cell (until it's replaced by another image). To fix, capture the indexPath in the block and get the cell for that indexPath once the image is loaded - if the table view returns nil then the cell is no longer visible and you don't need to do anything other than cache the image for future use.

How to specify different delays between slides in bxslider

Ran across the following problem in bxslider- how can you apply different delays between slides in the auto show?
I came up with the following solution which I will show here:
in the jquery.bxslider.js replace:
el.startAuto = function(preventControlUpdate){
// if an interval already exists, disregard call
if(slider.interval) return;
// create an interval
slider.interval = setInterval(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
}, slider.settings.pause);
// if auto controls are displayed and preventControlUpdate is not true
if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('stop');
}
With
/**EDITS: By CRB - techdude **/
el.startAuto = function(preventControlUpdate){
el.continueAuto();
}
el.continueAuto = function(){
//get how long the current slide should stay
var duration = slider.children.eq(parseInt(slider.active.index)).attr("duration");
if(duration == ""){
duration = slider.settings.pause;
} else {
duration = parseInt(duration);
}
console.log(duration);
// create a timeout
slider.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
// if auto controls are displayed and preventControlUpdate is not true
if (slider.settings.autoControls && preventControlUpdate != true) updateAutoControls('stop');
}
//*End Edits*/
Then to change the duration of a slide, simply give its li tag a duration attribute like this:
where duration is the number of milliseconds for the slide to pause.
To set the default duration, simply use the pause: option in the settings:
$("element").bxSlider({
auto:true,
pause: 4000
};
Hope this helps. Maybe bx slider will even add it to a future version. :)
What are the you're using to pick this up? Any way you can put up a gist of it working?
Perhaps this will help clarify:
In principle, the way this works is I change the setInterval with a setTimeout so the interval can be changed each time.
The key to getting multiple elements to work on a page is to not use the slider.timer object, but probably to use the el.timer object so the line would read something like,
el.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
Instead of
slider.timer = setTimeout(function(){
slider.settings.autoDirection == 'next' ? el.goToNextSlide() : el.goToPrevSlide();
el.continueAuto();
}, duration);
I haven't tested it with multiple sliders, but let me know if this works. That is the principle anyway. The only problem with this, however, is that I believe that you would need to modify the el.pause method to use the el.timer as well, otherwise the slideshow can't be paused. I think that was the reason I did it the way I did. However, it was a long time ago.

stop movieclip when it reaches specific frame

I have created a movieclip which have 120 frames.
Now i have used multiple instances of this movieclip on stage.
I want each movieclip to stop when it reaches a particular frame. (frame number is different for all instances)
i tried following code
if (char_1.currentFrame == 36) {char_1.stop();}
but it's not working. i tried to trace the current frame and it's always showing 1.
trace(char_1.currentFrame);
Any solution please?
try something like this:
// root
var allMCs:Dictionary = new Dictionary();
allMCs[char_1] = 32; // frame on which to stop char_1
allMCs[char_2] = 12; // frame on which to stop char_2
allMCs[char_3] = 47; // frame on which to stop char_3
// ... add more
// add ENTER_FRAME listener
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(evt:Event):void
{
// loop through all MCs and check their frame
for (var mc:MovieClip in allMCs)
{
trace(mc + " frame: " + mc.currentFrame);
if (mc.currentFrame == allMCs[mc])
{
mc.stop();
}
}
}
if the traces still state that the currentFrame is 1, make sure to do a play(); in every MC you use.

Resources