bvibber / ogv.js

JavaScript media player using Ogg/Vorbis/Theora/Opus/WebM libs compiled with Emscripten
https://brooke.vibber.net/misc/ogv.js/demo/
Other
1.2k stars 101 forks source link

Progressive slowdown on Internet Explorer 11 #31

Closed maikmerten closed 10 years ago

maikmerten commented 10 years ago

Depending on what video is played, playback in Internet Explorer 11 will progressively get slower. The profiler will show huge amounts of time spent in the "set" function (no location given).

Some affected files:

https://commons.wikimedia.org/wiki/File%3AEgg_grooming_behaviour_of_Reticulitermes_speratus_workers_in_a_nursery_cell_-_pone.0000813.s006.ogv

https://commons.wikimedia.org/wiki/File%3AExploring-Eye-Movements-in-Patients-with-Glaucoma-When-Viewing-a-Driving-Scene-pone.0009710.s001.ogv

https://commons.wikimedia.org/wiki/File%3ABorkumer_Kleinbahn_01.webmhd.webm

Some unaffected files:

https://commons.wikimedia.org/wiki/File%3AMunising_Falls.ogv

https://commons.wikimedia.org/wiki/File%3AJarry_-_M%C3%A9tro_de_Montr%C3%A9al_%28640%C3%97360%29.ogv

maikmerten commented 10 years ago

The expensive set() invocations seem to be part of emscripten's memcpy routine.

set-calltree

bvibber commented 10 years ago

I tried sticking a console.log into _emscripten_memcpy_big ... some of the data's relatively small and consistent, but it copies larger chunks as well which seem to increase in size over time, up to chunks over 8 megabytes -- much larger than the framebuffer size -- and to higher and higher positions in memory.

This makes me suspect there's some kind of reallocing going on and we've got memory fragmentation inside the C program.

I actually see the same large memcpy's going on in Firefox and Chrome, but I think IE's TypedArray 'set' is just badly implemented and so is exposing the problem. :)

On the 'Jarry' video (which doesn't slow down) I never see memcpy's larger than 65536 and the addresses don't go up as fast.

maikmerten commented 10 years ago

Compiled with --js-opts 0 -g4

It appears the problem is in libogg, ogg_stream_pagein()

memcpy-big

bvibber commented 10 years ago

Ah, that's much more legible. :D

If I'm reading this right, the affected ogv files appear to have multiple theora packets/frames per ogg page -- we're only decoding one theora packet per page, so the extras queue up in the stream state structure.

I can hack around this by replacing:

if (ogg_stream_packetout(&theoraStreamState, &oggPacket) > 0 ){

with

while (ogg_stream_packetout(&theoraStreamState, &oggPacket) > 0 ){

in processDecoding() in ogv-libs.c however this appears to skip frames which is not ideal. May need to retool the loop so it only fetches another page once packets are exhausted.

(Note also I've had to make a tweak to demo.js to work around a temporary issue with the video files -- I've enabled lower-resolution transcodes but it'll be a while before they're done and the old demo code assumed everything was complete, so it either loads a super-low res file or fails altogether. Temporary tweak keeps the current behavior and only loads 480p or source .ogv. Will make the sizes selectable later.)

maikmerten commented 10 years ago

Yes, it appears that pages should only be fetched once the packets are exhausted. In

http://svn.xiph.org/trunk/theora/examples/player_example.c

this is coded as

if(!videobuf_ready || !audiobuf_ready){
  /* no data yet for somebody.  Grab another page */
  buffer_data(infile,&oy);
  while(ogg_sync_pageout(&oy,&og)>0){
    queue_page(&og);
  }
}
maikmerten commented 10 years ago

A conversation on freenodes's #theora confirmed the suspicions regarding the order of consuming packets and pages.

bvibber commented 10 years ago

Excellent, that should be doable...

bvibber commented 10 years ago

Fixed in 69c59a6. Thanks for the debugging help!