I am doing a lot of work with Flash MX and video (flv) and I recently needed to get the duration off of a bunch of FLV files. Sorensen now correctly adds the duration, width, height, framerate, creation date, and the audio and video data rates. I wrote some C# code to read and parse this information off the file.
using System.IO;
namespace Refresh.FlashTools
{
/// <summary>
/// Reads the meta information embedded in an FLV file
/// </summary>
public class FlvMetaDataReader
{
/// <summary>
/// Reads the meta information (if present) in an FLV
/// </summary>
/// <param name="path">The path to the FLV file</returns>
public static FlvMetaInfo GetFlvMetaInfo(string path)
{
if (!File.Exists(path))
{
throw new Exception(String.Format("File '{0}' doesn't exist for FlvMetaDataReader.GetFlvMetaInfo(path)", path));
}
bool hasMetaData = false;
double duration = 0;
double width = 0;
double height = 0;
double videoDataRate = 0;
double audioDataRate = 0;
Double frameRate = 0;
DateTime creationDate = DateTime.MinValue;
// open file
FileStream fileStream = new FileStream(path, FileMode.Open);
try
{
// read where "onMetaData"
byte[] bytes = new byte[10];
fileStream.Seek(27, SeekOrigin.Begin);
int result = fileStream.Read(bytes, 0, 10);
// if "onMetaData" exists then proceed to read the attributes
string onMetaData = ByteArrayToString(bytes);
if (onMetaData == "onMetaData")
{
hasMetaData = true;
// 16 bytes past "onMetaData" is the data for "duration"
duration = GetNextDouble(fileStream, 16, 8);
// 8 bytes past "duration" is the data for "width"
width = GetNextDouble(fileStream, 8, 8);
// 9 bytes past "width" is the data for "height"
height = GetNextDouble(fileStream, 9, 8);
// 16 bytes past "height" is the data for "videoDataRate"
videoDataRate = GetNextDouble(fileStream, 16, 8);
// 16 bytes past "videoDataRate" is the data for "audioDataRate"
audioDataRate = GetNextDouble(fileStream, 16, 8);
// 12 bytes past "audioDataRate" is the data for "frameRate"
frameRate = GetNextDouble(fileStream, 12, 8);
// read in bytes for creationDate manually
fileStream.Seek(17, SeekOrigin.Current);
byte[] seekBytes = new byte[24];
result = fileStream.Read(seekBytes, 0, 24);
string dateString = ByteArrayToString(seekBytes);
// create .NET readable date string
// cut off Day of Week
dateString = dateString.Substring(4);
// grab 1) month and day, 2) year, 3) time
dateString = dateString.Substring(0, 6) + " " + dateString.Substring(16, 4) + " " + dateString.Substring(7, 8);
// .NET 2.0 has DateTime.TryParse
try
{
creationDate = Convert.ToDateTime(dateString);
}
catch { }
}
}
catch (Exception e)
{
// no error handling
}
finally
{
fileStream.Close();
}
return new FlvMetaInfo(hasMetaData, duration, width, height, videoDataRate, audioDataRate, frameRate, creationDate);
}
private static Double GetNextDouble(FileStream fileStream, int offset, int length)
{
// move the desired number of places in the array
fileStream.Seek(offset, SeekOrigin.Current);
// create byte array
byte[] bytes = new byte[length];
// read bytes
int result = fileStream.Read(bytes, 0, length);
// convert to double (all flass values are written in reverse order)
return ByteArrayToDouble(bytes, true);
}
private static string ByteArrayToString(byte[] bytes)
{
string byteString = string.Empty;
foreach (byte b in bytes)
{
byteString += Convert.ToChar(b).ToString();
}
return byteString;
}
private static Double ByteArrayToDouble(byte[] bytes, bool readInReverse)
{
if (bytes.Length != 8)
throw new Exception("bytes must be exactly 8 in Length");
if (readInReverse)
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
}
/// <summary>
/// Read only container holding meta data embedded in FLV files
/// </summary>
public class FlvMetaInfo
{
private Double _duration;
private Double _width;
private Double _height;
private Double _frameRate;
private Double _videoDataRate;
private Double _audioDataRate;
private DateTime _creationDate;
private bool _hasMetaData;
/// <summary>
/// The duration in seconds of the video
/// </summary>
public Double Duration
{
get { return _duration; }
//set { _duration = value; }
}
/// <summary>
/// The width in pixels of the video
/// </summary>
public Double Width
{
get { return _width; }
//set { _width = value; }
}
/// <summary>
/// The height in pixels of the video
/// </summary>
public Double Height
{
get { return _height; }
//set { _height = value; }
}
/// <summary>
/// The data rate in KB/sec of the video
/// </summary>
public Double VideoDataRate
{
get { return _videoDataRate; }
//set { _videoDataRate = value; }
}
/// <summary>
/// The data rate in KB/sec of the video's audio track
/// </summary>
public Double AudioDataRate
{
get { return _audioDataRate; }
//set { _audioDataRate = value; }
}
/// <summary>
/// The frame rate of the video
/// </summary>
public Double FrameRate
{
get { return _frameRate; }
//set { _frameRate = value; }
}
/// <summary>
/// The creation date of the video
/// </summary>
public DateTime CreationDate
{
get { return _creationDate; }
//set { _creationDate = value; }
}
/// <summary>
/// Whether or not the FLV has meta data
/// </summary>
public bool HasMetaData
{
get { return _hasMetaData; }
//set { _hasMetaData = value; }
}
internal FlvMetaInfo(bool hasMetaData, Double duration, Double width, Double height, Double videoDataRate, Double audioDataRate, Double frameRate, DateTime creationDate)
{
_hasMetaData = hasMetaData;
_duration = duration;
_width = width;
_height = height;
_videoDataRate = videoDataRate;
_audioDataRate = audioDataRate;
_frameRate = frameRate;
_creationDate = creationDate;
}
}
}
Update: Dave Daku converted this to VB:
Imports System.IO
Namespace Refresh.FlashTools
'
' Returns the meta information embedded in an FLV file
' Written in C# by John Dyer: http://johndyer.name/post/2005/08/Flash-FLV-meta-reader-in-NET-(C).aspx
' Conversion to VB.net by Dave Daku (11/29/2007)
'
Public Class flvMetaDataReader
Public Shared Function getFlvMetaInfo(ByRef path As String) As flvMetaInfo
If (Not File.Exists(path)) Then
Throw New Exception(String.Format("File '{0}' doesn't exist for flvMetaDataReader.getFlvMetaInfo(path)", path))
End If
Dim hasMetaData As Boolean = False
Dim duration As Double = 0
Dim width As Double = 0
Dim height As Double = 0
Dim videoDataRate As Double = 0
Dim audioDataRate As Double = 0
Dim frameRate As Double = 0
Dim creationDate As DateTime = DateTime.MinValue
' open the file
Dim fileStream As FileStream = New FileStream(path, FileMode.Open)
Try
' read where "onMetaData"
Dim bytes As Byte() = New Byte(10) {}
fileStream.Seek(27, SeekOrigin.Begin)
Dim result As Integer = fileStream.Read(bytes, 0, 10)
' if "onMetaData" exists then proceed to read the attributes
Dim onMetaData As String = byteArrayToString(bytes)
If (onMetaData = "onMetaData") Then
hasMetaData = True
' 16 bytes past "onMetaData" is the data for "duration"
duration = getNextDouble(fileStream, 16, 8)
' 8 bytes past "duration" is the data for "width"
width = getNextDouble(fileStream, 8, 8)
' 9 bytes past "width" is the data for "height"
height = getNextDouble(fileStream, 9, 8)
' 16 bytes past "height" is the data for "videoDataRate"
videoDataRate = getNextDouble(fileStream, 16, 8)
' 16 bytes past "videoDataRate" is the data for "audioDataRate"
audioDataRate = getNextDouble(fileStream, 16, 8)
' 12 bytes past "audioDataRate" is the data for "frameRate"
frameRate = getNextDouble(fileStream, 12, 8)
' read in bytes for creationDate manually
fileStream.Seek(17, SeekOrigin.Current)
Dim seekBytes As Byte() = New Byte(24) {}
result = fileStream.Read(seekBytes, 0, 24)
Dim dateString As String = byteArrayToString(seekBytes)
' create .NET readable date string
' cut off Day of Week
dateString = dateString.Substring(4)
' grab 1) month and day, 2) year, 3) time
dateString = dateString.Substring(0, 6) + " " + dateString.Substring(16, 4) + " " + dateString.Substring(7, 8)
' .NET 2.0 has dateTime.tryParse
Try
Date.TryParse(dateString, creationDate)
Catch ex As Exception
Try
creationDate = CDate(dateString)
Catch ex2 As Exception
End Try
End Try
End If
Catch ex As Exception
' no error handling
Finally
fileStream.Close()
End Try
Return New flvMetaInfo(hasMetaData, duration, width, height, videoDataRate, audioDataRate, frameRate, creationDate)
End Function
Private Shared Function getNextDouble(ByVal fileStream As
FileStream, ByVal offset As Integer, ByVal length As Integer) As Double
' move the desired number of places in the array
fileStream.Seek(offset, SeekOrigin.Current)
' create byte array
Dim bytes As Byte() = New Byte(length) {}
' read bytes
Dim result As Integer = fileStream.Read(bytes, 0, length)
' convert to double (all flash values are written in reverse orer)
Return byteArrayToDouble(bytes, True)
End Function
Private Shared Function byteArrayToString(ByRef bytes() As Byte) As String
Dim byteString As String = String.Empty
For Each b As Byte In bytes
byteString += Convert.ToChar(b).ToString()
Next
Return byteString
End Function
Private Shared Function byteArrayToDouble(ByRef bytes As Byte(), ByVal readInReverse As Boolean) As Double
If (bytes.Length <> 8) Then
Throw New Exception("Bytes must be exactly 8 in length")
End If
If (readInReverse) Then
Array.Reverse(bytes)
End If
Return BitConverter.ToDouble(bytes, 0)
End Function
End Class
' Read-only container for holding meta data embedded in FLV files
Public Class flvMetaInfo
Private _duration As Double
Private _width As Double
Private _height As Double
Private _frameRate As Double
Private _videoDataRate As Double
Private _audioDataRate As Double
Private _creationDate As DateTime
Private _hasMetaData As Boolean
' The duration in seconds of the video
Public ReadOnly Property duration() As Double
Get
Return _duration
End Get
End Property
' The width in pixels of the video
Public ReadOnly Property width() As Double
Get
Return _width
End Get
End Property
' The height in pixels of the video
Public ReadOnly Property height() As Double
Get
Return _height
End Get
End Property
' the data rate in KB/sec of the video
Public ReadOnly Property videoDataRate() As Double
Get
Return _videoDataRate
End Get
End Property
' the data rate in KB/sec of the video's audio track
Public ReadOnly Property audioDataRate() As Double
Get
Return _audioDataRate
End Get
End Property
' the frame rate of the video
Public ReadOnly Property frameRate() As Double
Get
Return _frameRate
End Get
End Property
' the creation date of the video
Public ReadOnly Property creationDate() As DateTime
Get
Return _creationDate
End Get
End Property
' the frame rate of the video
Public ReadOnly Property hasMetaData() As Boolean
Get
Return _hasMetaData
End Get
End Property
Public Sub New(ByVal hasMetaData As Boolean, ByVal duration As
Double, ByVal width As Double, ByVal height As Double, ByVal
videoDataRate As Double, ByVal audioDataRate As Double, ByVal frameRate
As Double, ByVal creationDate As DateTime)
_hasMetaData = hasMetaData
_duration = duration
_width = width
_height = height
_videoDataRate = videoDataRate
_audioDataRate = audioDataRate
_frameRate = frameRate
_creationDate = creationDate
End Sub
End Class
End Namespace
I'd like to also be able to get the duration off the FLV if the meta data did not get inserted, but the best solution I found (FLV MetaData Injector) is not open source.