{"id":3234,"date":"2011-04-02T18:36:17","date_gmt":"2011-04-03T01:36:17","guid":{"rendered":"http:\/\/multimedia.cx\/eggs\/?p=3234"},"modified":"2011-04-02T18:44:08","modified_gmt":"2011-04-03T01:44:08","slug":"re-radius-videovision","status":"publish","type":"post","link":"https:\/\/multimedia.cx\/eggs\/re-radius-videovision\/","title":{"rendered":"Reverse Engineering Radius VideoVision"},"content":{"rendered":"<p>I was called upon to help reverse engineer an old video codec called VideoVision (FourCC: PGVV), ostensibly from a company named Radius. I&#8217;m not sure of the details exactly but I think a game developer has a bunch of original FMV data from an old game locked up in this format. The name of the codec sounded familiar. Indeed, we have had <a href=\"http:\/\/samples.mplayerhq.hu\/V-codecs\/PGVV-RadiusStudio\/\">a sample in the repository since 2002<\/a>. Alex B. did <a href=\"http:\/\/wiki.multimedia.cx\/index.php?title=Radius_Studio_Video\">some wiki work on the codec<\/a> some years ago. The wiki mentions that there existed a tool to transcode PGVV data into MJPEG-B data, which is already known and supported by FFmpeg.<\/p>\n<p><strong>The Software<\/strong><br \/>\nMy contacts were able to point me to some software, now safely archived in the PGVV samples directory. There is StudioPlayer2.6.2.sit.hqx which is supposed to be a QuickTime component for working with PGVV data. I can&#8217;t even remember how to deal with .sit or .hqx data. Then there is RadiusVVTranscoder101.zip which is the tool that transcodes to MJPEG-B.<\/p>\n<p><strong>Disassembling for Reverse Engineering<\/strong><br \/>\nSince I could actually unpack the transcoder, I set my sights on that. Unpacking the archive sets up a directory structure for a component. There is a binary called RadiusVVTranscoder under RadiusVVTranscoder.component\/Contents\/MacOS\/. Basic deadlisting disassembly is performed via &#8216;otool&#8217; as shown:<\/p>\n<pre>\r\n  otool -tV RadiusVVTranscoder | c++filt\r\n<\/pre>\n<p>This results in a deadlisting of both PowerPC and 32-bit x86 code, as the binary is a &#8220;fat&#8221; Mac OS X binary designed to run on both architectures. The command line also demangles C++ function signatures which gives useful insight into the parameters passed to a function.<\/p>\n<p><strong>Pretty Pictures<\/strong><br \/>\nThe binary had a lot of descriptive symbols. As a basis for reverse engineering, I constructed call graphs using these symbols. Here are the 2 most relevant portions (click for larger images).<\/p>\n<p>The codec initialization generates Huffman tables relevant to the codec:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-init-graph.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-init-graph-300x51.png\" alt=\"\" title=\"Radius VideoVision codec initialization\" width=\"300\" height=\"51\" class=\"aligncenter size-medium wp-image-3238\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-init-graph-300x51.png 300w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-init-graph.png 896w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>The main decode function calls AddMJPGFrame which apparently does the heavy lifting for the transcode process:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-graph.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-graph-300x106.png\" alt=\"\" title=\"Relevant function call graph for the PGVV decoder\" width=\"300\" height=\"106\" class=\"aligncenter size-medium wp-image-3235\" srcset=\"https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-graph-300x106.png 300w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-graph-1024x363.png 1024w, https:\/\/multimedia.cx\/eggs\/wp-content\/uploads\/2011\/04\/pgvv-function-graph.png 1249w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Based on this tree, I&#8217;m guessing that luma blocks can be losslessly transcoded (perhaps with different Huffman tables) which chroma blocks may rely on a different quantization method.<\/p>\n<p><strong>Assembly Constructs<\/strong><br \/>\nI started looking at the instructions (the x86 ones, of course). The binary uses a calling convention I haven&#8217;t seen before, at least not for the x86: Rather than pushing function arguments onto the stack, the code manually subtracts, e.g., 12 from the ESP register, loads 3 32-bit arguments into memory relative to ESP, and then proceeds with the function call.<\/p>\n<p>I&#8217;m also a little unclear on constructs such as &#8220;call ___i686.get_pc_thunk.bx&#8221; seen throughout relevant functions such as MakeRadiusQuantizationTables(). <\/p>\n<p>I&#8217;m just presenting what I have so far in case anyone else wants to try their hand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reverse engineering the Radius VideoVision (PGVV) video codec<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-3234","post","type-post","status-publish","format-standard","hentry","category-reverse-engineering"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/3234","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/comments?post=3234"}],"version-history":[{"count":7,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/3234\/revisions"}],"predecessor-version":[{"id":3242,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/posts\/3234\/revisions\/3242"}],"wp:attachment":[{"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/media?parent=3234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/categories?post=3234"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/multimedia.cx\/eggs\/wp-json\/wp\/v2\/tags?post=3234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}