Video File Mystery: How to read MVW video files

Introduction: the .mvw mystery

A month or two ago, I overheard my boss talking with a coworker about reviews from a recent paper submission. The reviewer brought up an important confound that could offer a trivial explanation for one of the results in the paper. The only way to address the problem was to review videos from the experiments to see if the problem described by the reviewer actually occurred.

The good news was that there were many videos of the experiments taking place, so we could directly address the reviewers concern.

The bad news was that they were stored as MVW files. What the heck is an MVW file? Unfortunately not a typo of ‘WMV,’ the common windows file extension (I tried renaming to that right away, to no success). My coworker and boss spent the better part of an afternoon Googling the format. From an archived manual we learned that a company that no longer existed made the format to store raw live-streamed video from one of our high-speed cameras:

mvw_svsi_quote

The only mention of .mvw files that we found on the entire internet.

Beyond that, there was nothing. No software was currently maintained to view the videos, and the post doc who filmed the videos left the lab 3 years ago. To make matters worse, reviewers wanted revisions by the end of the week. A real video file mystery was afoot!

I asked to have a .mvw file to see if I could mess around with it later that night.

Since manual mentioned the .mvw file was a raw video format, I figured there was a good chance the file was stored in a logical way (unlike if the video used some sort of proprietary compression which might be very difficult to figure out).

The key to file mysteries: deciphering a file header

Later that night I decided to poke around the .mvw file and see if there was some sort of intelligible file header.  A file header is commonly coded into the leading bytes of the beginning of a computer file to leave some key information about how to parse the file in question.  For example, in .mp3 files, the header is often also used to store information about the bitrate of the song, if it is in stereo, and some other imptorant features.  Using MATLAB I used the following code to start exploring the .mvw file:

filepath='D:\Dropbox\Thesis\TempData\ProStreams.mvw'; %.mvw file
fid=fopen(filepath); %This returns a handle for reading the file as a binary file
fseek(fid,0,'bof')
header=fread(fid,10,'char'); %This reads 10 characters from the beginning of the file
fprintf('%d,',header)
fprintf('\n')

>> 0,1,0,0,6,0,0,0,238,1,0,0,8220,2,0,0,119,0,0,0

This didn’t make too much sense to me, so I ruled out that the header started with a character description. Next I tried reading integers:

fseek(fid,0,'bof');
header=fread(fid,10,'int');
fprintf('%d,',header)
fprintf('\n')

>>256,6,494,659,119,8353,361,1024,841822768,1093679153,808857645,12337,0,0,0,

This actually makes a bit of sense! 256 is a number which might tell us something about the bit-depth of the camera or where to start reading the video files. The 119 value stuck out to me because I know our high-speed camera has a default frame rate of 119 frames/second at it’s default resolution. The values 494 and 659 struck me as possible frame pixel sizes (width x height), and one of these values must have been the number of frames. Other values were not immediately clear to me.

To figure out what was going on, I tried using 256 as the place to start reading the video:

fseek(fid,0,'bof')
header=fread(fid,8,'int');
startRead=header(1);
frameSize=[header(3) header(4)];
numFrames=header(7);
fseek(fid,startRead,'bof');
data=fread(fid,'uint8');

The data variable returns as a vector with 117522106 entries. The next thought I had was that if the 494 and 659 were the frame pixel height and width, then dividing this vector length with (494 x 659) should reveal the number of frames. And in addition, it should be a completely round number (the camera is not recording half frames). The value we get if we divide 117522106 by (494 x 659) is 361. This is very promising because 361 is actually the 7th value in the header–too good to be a coincidence. This indicates that if we can reshape the long data vector into 361 frames that are 494 x 659 pixels, we will have a movie that should be replayed at 119 frames per second.

Reshaping and plotting the data

To go from one long vector of pixel values to a movie, we need to figure out how to reshape the vector into a 3D tensor (assuming the video is grayscale): height x width x frame.  It was not immediately obvious that this would work, so I decided to visualize a few different ways of reshaping the data and see if anything popped out.  The first thing I tried was the following:

figure;
dd=reshape(data,numFrames,frameSize(1),frameSize(2));
imagesc(squeeze(dd(1,:,:))); % Grabbing a 2D plane of this cube as the frame

This revealed an image that looked like:

This doesn't look right!

This doesn’t look right!

Tried a few more combinations until I tried this one:

figure;
dd=reshape(data,frameSize(1),frameSize(2),numFrames);
imagesc(dd(:,:,1)) %Again grabbing a 2D plane of this cube as the frame
colormap bone

and got:

This looks very promising!

This looks very promising!

Eureka! All I needed to do after that was flip the frameSize values to reflect a different height/width and I was in business. Here is a short rendering of some middle frames of video:

mvwVideo

Whiskers peaking!

And here is the code for rendering this gif:

function writeMVW_gif(mvwpath,useFrames,outPath)
% writeMVW_gif(mvwpath,useFrames,outPath)
%path = pathname filename of .mvw file
%useFrames = subset of frames to render as a gif
%outPath = pathname filename of .gif to save
%Last updated 2016-08-09
%Copywrite Brian Isett
if ~exist('playSpeedX','var') || isempty(playSpeedX)
    playSpeedX=1;
end
fid=fopen(mvwpath);
header=fread(fid,8,'int');
startRead=header(1);
frameSize=[header(4) header(3)];
frameRate=header(5) + header(6)/10000; %header(6) appears to be fractional frames/second (i.e. decimal points).
numFrames=header(7);
fseek(fid,startRead,'bof');
a=fread(fid,'uint8');
fclose(fid);
b=reshape(a,frameSize(1),frameSize(2),numFrames);
x = 0:0.01:1;
figure(1)
for n = 1:length(useFrames)
      f=useFrames(n);
      frame=uint8(b(:,:,f)');
      imshow(frame)  
      drawnow
      if n == 1;
          imwrite(frame,outPath,'gif', 'Loopcount',inf);
      else
          imwrite(frame,outPath,'gif','WriteMode','append','DelayTime',(1/frameRate));
      end
end

Of course for the website I wanted to render a .gif, but it was easy to render .avi files as well, which my coworkers went on to use in addressing the reviewers concern. Phew. Headers to the rescue!

It’s important to note that a few things worked in my favor: no compression was used in this video format, the video was stored in a very logical way, the video was grayscale (would have to reshape to a height x width x RGB x frames tensor if it were color), and I had some idea of what to expect (i.e. that the camera was highspeed, with a framerate around 119 fps by default).

In conclusion, if you ever come across a file type that no one seems to know how to read, consider looking at the file header and see if there are any clues.  Hope this can help someone else out in the future!