It’s been a few weeks since I last posted, and I’ve accumulated a couple of useful CrystalHD improvements that I think are worth talking about. First off, my comprehensive interlaced detection algorithm is now merged to the main git tree, as are the changes I’m about to talk about, so there are now no outstanding changes to merge.
The CrystalHD hardware is capable of downscaling, so that it will shrink the decoded frames before they are copied over to system memory. While all vaguely modern graphics hardware supports scaling, it’s still useful that the CrystalHD can do it, and that’s because it allows you to scale before the copy; smaller frames take less time and CPU to copy over. Normally, this isn’t an issue, but some videos can make the hardware grumpy such that the total time needed to decode and copy over the frame is more than the time available if you want to playback in realtime. So, being able to shrink the copy time can save you from an unplayable clip. Mind you, it’s a weird hardware/firmware bug that this is even an issue – I’ve been able to playback bluray video just fine but certain encoded files at much lower bitrates can trigger this slow decode behaviour.
To take advantage of downscaling you will also need to update your Mplayer as I had to make a change there to support FFmpeg per-codec command line options. With the latest code you can do:
mplayer -lavdopts o=crystalhd_downscale_width=[width]
to specify a width (eg: Use 1280 for 720p)
I mentioned this briefly in my last update, saying that the hardware has a bug where it would output certain frames twice when decoding a DivX/XviD video in an AVI file with packed b-frames. I implemented a work-around and thought that my work was done, but it turns out that there are at least two ways of indicating packed b-frames in a file, and one of them triggers the bug while the other does not. Sounds great, you might think – except that the files which don’t trigger the bug do still look like the files which do – which caused my work-around to kick in and ruin the playback.
So, I had to find another way to distinguish them. To achieve that, I ended up staring at a binary diff of two files and saw that they were using different frame types as placeholders for the packed frames. In the files that trigger the bug, they are “drop frames” and in the normal files, they are “delay frames”. Despite their names, I don’t believe a decoder is supposed to either drop or delay anything when encountering these; rather it’s supposed to replace them with the packed frame it received earlier. In other words, there’s a convention here that a decoder has to understand and respect, and it seems the CrystalHD is not completely up to speed with things.
With that difference identified, I was able to craft an additional test that lets us distinguish the two cases and now packed b-frame support is hopefully complete.
While I don’t have anything meaningful to report in this area, I did spend some more time poking at the 70012, and while the existing code will likely yield something pretty close to a sane video stream, I still see discontinuities in the output, where frames mysteriously disappear, which very quickly leads to audio de-sync in Mplayer (which doesn’t understand the concept of a frame that fails to be decoded, so doesn’t know to re-sync the audio). I tried a number of different approaches, all with the same result – missing frames from files that play perfectly on the 70015. I know it’s possible to make it work, as both the gstreamer plugin and xbmc can do it; however, they are very different architectures that use separate input and output threads, which is not possible in FFmpeg. Ultimately, I’m not sure the support can really be improved, given the constraints of the FFmpeg architecture. Such is life.
Update: Reimar rightly points out that Mplayer can understand frames that fail to decode; I failed to remember the problem properly. What’s actually going on is that you indicate a failed frame by returning nothing; however, we only find out by obtaining the next output frame and seeing that it’s not the one we expected. At this point, returning an error would mean having to store the output frame for the next decode call and accepting that the input pipeline would increase by one frame. If that happens enough times, the pipeline will fill up completely and then we’re in real trouble. So, rather, I’m wishing I could return a frame and indicate that other frames had failed to be decoded at the same time.