Order Now AdSolution Sign Up | Login » Bits on the Run Sign Up | Login »

Forums

/

Help: Solving Flash Video Playback on Thin Client?

7 replies [Last post]

Hi All,

I have also posted this at my blog, at http://www.crypticide.com/dropsafe/article/2610 - for more context.

I am trying to solve the problem of Flash video playback on the SunRay1[1], and so far I have had maybe 60% success, but it’s still not good enough.

My biggest problem is that if you Google for “flash video stuttering” or similar then you will get thousands of hits about using faster computers, using a faster network, changing your codecs, etc - but none of that is applicable to a thin client, or my situation.

So here’s the problem:

* YouTube videos play very acceptably on a SunRay1

* Even if I replicate the codecs, bitrate, and everything else I can find out about YouTube’s media, FFmpeg-encoded flash videos played on a SunRay1, using JWplayer, stutter badly and are unwatchable.

For those who speak FFmpeg, here’s how I am generating the FLV videos:

ffmpeg -i video.avi -vcodec flv -b 240k -s 320×240 -g 300 -ab 64k -ac 1 -ar 22050 -y video-low1.flv

…using code (FFmpeg version SVN-r15488) and libraries that I pulled and freshly built this week.

I am also using the latest JWplayer for Flash.

I encoded the above by copying everything that I can learn about YouTube formats:

* the audio is downsampled to 22kHz and mixed into mono LAME MP3 at 64kbits

* the video is Flash 7 format (Sorenson) and set to 240kbits, scaled during encoding to 320×240 and then played back at whatever 4:3 ratio size it is meant to be, in this case 480×360.

* the native frame rate is 25fps, and the FLV file inherits that; since YouTube videos play at 29fps, I am pretty sure that’s not the problem

* also I experimented with encoding with framerates as low as 8fps and found it didn’t help.

The SunRay server is a mostly-idle twin-core Opteron running at 2.4GHz, and as I say it does a fine job of playing back real YouTube videos, with audio, on the SunRay1 thin client. The webserver is on the local network, so there is no delay waiting for data - the 50Mb file is downloaded in seconds.

When playing the FLV file using JWplayer in Firefox, the video stutters, you can watch it refreshing the playback area, and the audio stops and starts, and the video stops and starts in sync; it was worse before I added “-g 300″ to increase the keyframe interval, but raising it any higher yields no benefits.

I have the following suspicions:

* Maybe the audio settings also need poking to have increased packet sizes, or something; I do get the impression that JWplayer is working hard to keep audio and video in sync, rather than just streaming the media in a laissez-faire way?

* Maybe the YouTube player has some special magic in it? Certainly it seems to look less pixelated than the JWplayer videos - a sort of “impressionistic” approach to colour?

* Maybe there are some non-obvious FFmpeg settings I need, like how to handle dropped frames on the server, and stuff like that?

In short, I am stuck, and I am pretty sure there aren’t many people who work in this space.

I am kinda hoping that it reaches one of them. Can anyone help?

[1] A SunRay is a networked thin-client device from Sun Microsystems; http://en.wikipedia.org/wiki/Sun_Ray for details; if you aren’t familiar with thin clients then you can probably think of them as “hardware devices that do VNC of another machine”, but they are actually a lot better than that.

[2] I am using a SunRay1 which is the oldest and slowest of the machines; apparently there will be some improvements to its firmware soon to enhance video playback, but I want to get this working *now* …

Just wondering how fast your upload speed is and what is the quality of service. Might be an issue if other streaming sources with lots of bandwidth play correctly.

Regards - Jimb

It appears to me, that you are re-sizing on the client from a native size of 320x240 to 480x360. That is a real CPU hog in Flash.

On Windows, I've seen re-sizing push CPU usage from 15-20% up to 100%. If the CPU can't keep up, you get stuttering, etc.

So... I would recommend that you encode to whatever final size you want on the client.

Also, H.264 is less demanding of both bandwidth and CPU cycles, and the quality is much better; so consider using H.264 in a MP4 container instead of Spark (H.263) in a FLV container.

@Jimb - the (virtual) desktop, and the webserver, are back-to-back on a dedicated gigabit ethernet; it's hard to get a faster upload than that... :-)

@klink: i did that because that's what YouTube seems to do, but I was also trying native-size playback and had the same problems; your point about H.264 is tempting though. Would you know if FFmpeg supports that, because it was not clear to me that it did...

@Alec,

ffmpeg does a good job of transcoding to H.264.

Here's an MS-DOS Batch file with progressively more complex transcoding. You can try out each step, or just jump to the last to see the result. There are also a couple of lines for encoding with a keyframe every 2 frames and 40fps.

I tried 2-pass encoding but couldn't find the magic combo. to get it to work.

ffmpeg-15394 windows binary from: http://tripp.arrozcru.org/

:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250                                                                            -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-7.mp4 2>mp4-7.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25                                                             -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-8.mp4 2>mp4-8.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac                                                   -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-10.mp4 2>mp4-10.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full                                          -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-11.mp4 2>mp4-11.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16                             -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-12.mp4 2>mp4-12.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5                     -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-13.mp4 2>mp4-13.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500                                                                                                              -y 11925-14.mp4 2>mp4-14.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma                                                                                              -y 11925-18.mp4 2>mp4-18.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8                                                   -y 11925-19.mp4 2>mp4-19.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71                                -y 11925-20.mp4 2>mp4-20.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71    -b_strategy 1               -y 11925-21.mp4 2>mp4-21.txt
:: GOOD
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 250 -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71    -b_strategy 1    -crf 30    -y 11925-22.mp4 2>mp4-22.txt
:: GOOD - keyframes @ 2s for scrubbing
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 40  -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71    -b_strategy 1    -crf 30    -y 11925-23.mp4 2>mp4-23.txt

:: keyframes at 1 every 2 frames for scrubbing
:: GOOD - keyframes @ 0.05s and framerate at 20fps - size went from 488kB to 1282kB ( 2.63X )
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 20 -g 2   -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71    -b_strategy 1    -crf 30    -y 11925-25.mp4 2>mp4-25.txt
:: GOOD - keyframes @ 0.05s and framerate at 40fps - size went from 488kB to 2680kB ( 5.49X )
:: ffmpeg -i 11925.flv   -vcodec libx264 -r 40 -g 2   -keyint_min 25 -coder ac -me full -me_range 16 -subq 5 -sc_threshold 40    -acodec libfaac -ab 96000 -ar 22500    -cmp +chroma    -partitions +parti4x4+partp8x8+partb8x8    -i_qfactor 0.71    -b_strategy 1    -crf 30    -y 11925-26.mp4 2>mp4-26.txt

Hello Alec Muffett,
Were you able to make it work as you needed? The solution from Klinc made it workas expected?
Thanks

Would you mind explaining a bit about the -i_qfactor, -keyint_min, and -b_strategy params that you used at the end? I am trying to find the best compromise between quality and size (to put vids on memory-limited devices like PDAs) and I think reducing the number of i-frames is the key to this, but I am getting confused w/ all the params...
-rmh

To reduce the size: framerate, video bitrate, and to a lesser extent, audio bitrate as well as the keyframe interval are the most important factors. Consider that the device probably can't utilize very high-quality video or audio even if you encode it that way. keyframes, of course eat up lots of bytes, less-granular scrubbing is usually OK, I find 3-5 second intervals are fine.

       Good Enough is the key phrase here, 'cause we'll never have enough bandwidth or CPU cycles.

-i_qfactor   <float> E.V.. qp factor between P and I frames
qp                   .DV.. per-block quantization parameter (QP)
quantization: http://en.wikipedia.org/wiki/Quantization_(signal_processing)

-keyint_min        <int>   E.V.. minimum interval between IDR-frames (x264)
http://forum.doom9.org/archive/index.php/t-89936.html

-b_strategy        <int>   E.V.. strategy to choose between I/P/B-frames
(This is for XviD, but it's a good explanation.)
http://snowbeach.gmxhome.de/MPEG-4_XviD_Koepi_24062003-1_Encoder_Guide_ver20030702.pdf

Best sources of info: http://forum.videohelp.com/
                      http://forum.doom9.org/

And of course, there's always Google.

Also, consider doing a 2-pass transcode for better results.

MS-DOS batch file (ffmpeg SVN-r15815):

:: 2-pass transcode to MP4[H.264/AAC] with W & H divisible by 16 (YouTube 480x270 to 480x272 AND 512k)

:: first pass
ffmpeg -y -i %1 -an                               -pass 1 -threads 2 -vcodec libx264 -b 512k -bf 3 -subq 6 -cmp 256 -refs 5 -qmin 10 -qmax 51 -qdiff 4 -coder 1 -loop 1 -me_method hex -me_range 16 -trellis 1 -flags +mv4 -flags2 +bpyramid+wpred+mixed_refs+8x8dct -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 -g 250 -r 20 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 %1.mp4

:: second pass
ffmpeg -y -i %1 -acodec libfaac -ar 44100 -ab 96k -pass 2 -threads 2 -vcodec libx264 -b 512k -bf 3 -subq 6 -cmp 256 -refs 5 -qmin 10 -qmax 51 -qdiff 4 -coder 1 -loop 1 -me_method hex -me_range 16 -trellis 1 -flags +mv4 -flags2 +bpyramid+wpred+mixed_refs+8x8dct -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 -g 250 -r 20 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 %1.mp4

:: move MOOV atoms
qt-faststart %1.mp4 %1.out.mp4

480x270 FLV 44,003 bytes
480x272 MP4 28,770 bytes 512k video bitrate
960x544 MP4 53,764 bytes 1024k video bitrate (of course the quality wasn't too good because of the 4:1 upconvert)

Note that ffmpeg is constantly changing, so parameters that worked with one version may not even exist with another version. Sometimes, there is a semi-equivalent replacement, other times, there is a completely different library so many parameters have to be changed.

I guess once you're close, you just have to try lots of small video files to get the final tweak.