MpegVideoInfo.cs
/*=========================================================================
Program: GDCM (Grassroots DICOM). A DICOM library
Copyright (c) 2006-2011 Mathieu Malaterre
All rights reserved.
See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/*
* This examples takes in a MPEG2 and write out a Video Endoscopic Imagae Storage
* encoded using MPEG2 @ Main Profile
* ref: http://chrisa.wordpress.com/2007/11/21/decoding-mpeg2-information/
* See also:
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html#gop
* http://cvs.linux.hr/cgi-bin/viewcvs.cgi/mpeg_mod/README.infompeg?view=markup
* http://www.guru-group.fi/~too/sw/m2vmp2cut/mpeg2info.c
*/
/*
* Provides information about an MPEG2 file, including the duration, frame rate, aspect
* ratio, and resolution. Good information about the MPEG2 file structure that helps
* explain parts of the code can be found here:
* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html#gop
*
* Copyright (c) 2007 Chris Anderson (chrisa@wordpress.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
using System;
using System.IO;
using gdcm;
public class Mpeg2VideoInfo
{
#region Member Variables
private TimeSpan m_startTime = TimeSpan.Zero;
private TimeSpan m_endTime = TimeSpan.Zero;
private TimeSpan m_duration = TimeSpan.Zero;
private eAspectRatios m_aspectRatio = eAspectRatios.Invalid;
private eFrameRates m_frameRate = 0;
private int m_pictureWidth = 0;
private int m_pictureHeight = 0;
#endregion
#region Constants
private const byte PADDING_PACKET = 0xBE;
private const byte VIDEO_PACKET = 0xE0;
private const byte AUDIO_PACKET = 0xC0;
private const byte SYSTEM_PACKET = 0xBB;
private const byte TIMESTAMP_PACKET = 0xB8;
private const byte HEADER_PACKET = 0xB3;
private const int BUFFER_SIZE = 8162; // 8K buffer
private readonly static TimeSpan EMPTY_TIMESPAN = new TimeSpan(0, 0, -1);
#endregion
#region Enumerations
public enum eFrameRates
{
Invalid,
PulldownNTSC, // 24000d/1001d = 23.976 Hz
Film, // 24 Hz
PAL, // 25 Hz
NTSC, // 30000d/1001d = 29.97 Hz
DropFrameNTSC, // 30 Hz
DoubleRatePAL, // 50 Hz
DoubleRateNTSC, // 59.97 Hz
DoubleRateDropFrameNTSC // 60 Hz
}
public enum eAspectRatios
{
Invalid,
VGA, // 1/1
StandardTV, // 4/3
LargeTV, // 16/9
Cinema // 2.21/1
}
#endregion
#region Constructor
public Mpeg2VideoInfo(string file)
{
ParseMpeg(file);
}
#endregion
#region Public Properties
public TimeSpan StartTime
{
get { return m_startTime; }
}
public TimeSpan EndTime
{
get { return m_endTime; }
}
public TimeSpan Duration
{
get { return m_duration; }
}
public eAspectRatios AspectRatio
{
get { return m_aspectRatio; }
}
public eFrameRates FrameRate
{
get { return m_frameRate; }
}
public int PictureWidth
{
get { return m_pictureWidth; }
}
public int PictureHeight
{
get { return m_pictureHeight; }
}
#endregion
#region Private Functions
private void ParseMpeg(string file)
{
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader br = new BinaryReader(fs);
m_startTime = GetStartTimeStampInfo(br);
m_endTime = GetEndTimeStampInfo(br);
m_duration = m_endTime.Subtract(m_startTime);
GetHeaderInfo(br);
br.Close();
fs.Close();
}
private TimeSpan GetStartTimeStampInfo(BinaryReader br)
{
TimeSpan startTime = EMPTY_TIMESPAN;
byte[] buffer = new byte[BUFFER_SIZE];
br.BaseStream.Seek(0, SeekOrigin.Begin);
while (startTime == EMPTY_TIMESPAN && br.BaseStream.Position < br.BaseStream.Length)
{
int readBytes = br.Read(buffer, 0, BUFFER_SIZE);
for (int offset = 0; offset < readBytes - 8; offset++)
{
if (IsStreamMarker(ref buffer, offset, TIMESTAMP_PACKET))
{
offset += 4; // Move to the data position which follows the stream header
uint timeStampEncoded = GetData(ref buffer, offset);
startTime = DecodeTimeStamp(timeStampEncoded);
if (startTime != EMPTY_TIMESPAN)
break;
}
}
}
return startTime;
}
private TimeSpan GetEndTimeStampInfo(BinaryReader br)
{
TimeSpan endTime = EMPTY_TIMESPAN;
byte[] buffer = new byte[BUFFER_SIZE];
br.BaseStream.Seek(-BUFFER_SIZE, SeekOrigin.End);
while (endTime == EMPTY_TIMESPAN && br.BaseStream.Position > BUFFER_SIZE)
{
int readBytes = br.Read(buffer, 0, BUFFER_SIZE);
for (int offset = readBytes - 8; offset >= 0; offset--)
{
if (IsStreamMarker(ref buffer, offset, TIMESTAMP_PACKET))
{
offset += 4; // Move to the data position which follows the stream header
uint timeStampEncoded = GetData(ref buffer, offset);
endTime = DecodeTimeStamp(timeStampEncoded);
if (endTime != EMPTY_TIMESPAN)
break;
}
}
br.BaseStream.Seek(-BUFFER_SIZE * 2, SeekOrigin.Current);
}
return endTime;
}
private TimeSpan DecodeTimeStamp(uint timeStampEncoded)
{
TimeSpan timeStamp = EMPTY_TIMESPAN;
// Mask out the bits containing the property we are after, then
// shift the data to the right to get its value
int hour = (int)(timeStampEncoded & 0x7C000000) >> 26; // Bits 31 -> 27
int minute = (int)(timeStampEncoded & 0x03F00000) >> 20; // Bits 26 -> 21
int second = (int)(timeStampEncoded & 0x0007E000) >> 13; // Bits 19 -> 14
int frame = (int)(timeStampEncoded & 0x00001F80) >> 7; // Bits 13 -> 8 - not used, but included for completeness
timeStamp = new TimeSpan(hour, minute, second);
return timeStamp;
}
private void GetHeaderInfo(BinaryReader br)
{
byte[] buffer = new byte[BUFFER_SIZE];
br.BaseStream.Seek(0, SeekOrigin.Begin);
br.Read(buffer, 0, BUFFER_SIZE);
for (int offset = 0; offset < buffer.Length - 4; offset++)
{
if (IsStreamMarker(ref buffer, offset, HEADER_PACKET))
{
offset += 4; // Move to the data position which follows the stream header
uint headerData = GetData(ref buffer, offset);
// Mask out the bits containing the property we are after, then
// shift the data to the right to get its value
m_pictureWidth = (int)(headerData & 0xFFF00000) >> 20;
m_pictureHeight = (int)(headerData & 0x000FFF00) >> 8;
uint aspectRatioIndex = (headerData & 0x000000F0) >> 4;
uint fpsIndex = headerData & 0x0000000F;
m_aspectRatio = (eAspectRatios)fpsIndex;
m_frameRate = (eFrameRates)fpsIndex;
break;
}
}
}
private uint GetData(ref byte[] buffer, int offset)
{
return (uint) ((buffer[offset] << 24) |
(buffer[offset + 1] << 16) |
(buffer[offset + 2] << 8) |
(buffer[offset + 3]));
}
private bool IsStreamMarker(ref byte[] buffer, int offset, byte markerType)
{
return (buffer[offset] == 0x00 &&
buffer[offset + 1] == 0x00 &&
buffer[offset + 2] == 0x01 &&
buffer[offset + 3] == markerType);
}
#endregion
public static int Main(string[] args)
{
string file1 = args[0];
Mpeg2VideoInfo info = new Mpeg2VideoInfo(file1);
System.Console.WriteLine( info.StartTime );
System.Console.WriteLine( info.EndTime );
System.Console.WriteLine( info.Duration );
System.Console.WriteLine( info.AspectRatio );
System.Console.WriteLine( info.FrameRate );
System.Console.WriteLine( info.PictureWidth );
System.Console.WriteLine( info.PictureHeight );
ImageReader r = new ImageReader();
//Image image = new Image();
Image image = r.GetImage();
image.SetNumberOfDimensions( 3 );
DataElement pixeldata = new DataElement( new gdcm.Tag(0x7fe0,0x0010) );
System.IO.FileStream infile =
new System.IO.FileStream(file1, System.IO.FileMode.Open, System.IO.FileAccess.Read);
uint fsize = gdcm.PosixEmulation.FileSize(file1);
byte[] jstream = new byte[fsize];
infile.Read(jstream, 0 , jstream.Length);
SmartPtrFrag sq = SequenceOfFragments.New();
Fragment frag = new Fragment();
frag.SetByteValue( jstream, new gdcm.VL( (uint)jstream.Length) );
sq.AddFragment( frag );
pixeldata.SetValue( sq.__ref__() );
// insert:
image.SetDataElement( pixeldata );
PhotometricInterpretation pi = new PhotometricInterpretation( PhotometricInterpretation.PIType.YBR_PARTIAL_420 );
image.SetPhotometricInterpretation( pi );
// FIXME hardcoded:
PixelFormat pixeltype = new PixelFormat(3,8,8,7);
image.SetPixelFormat( pixeltype );
// FIXME hardcoded:
TransferSyntax ts = new TransferSyntax( TransferSyntax.TSType.MPEG2MainProfile);
image.SetTransferSyntax( ts );
image.SetDimension(0, (uint)info.PictureWidth);
image.SetDimension(1, (uint)info.PictureHeight);
image.SetDimension(2, 721);
ImageWriter writer = new ImageWriter();
gdcm.File file = writer.GetFile();
file.GetHeader().SetDataSetTransferSyntax( ts );
Anonymizer anon = new Anonymizer();
anon.SetFile( file );
MediaStorage ms = new MediaStorage( MediaStorage.MSType.VideoEndoscopicImageStorage);
UIDGenerator gen = new UIDGenerator();
anon.Replace( new Tag(0x0008,0x16), ms.GetString() );
anon.Replace( new Tag(0x0018,0x40), "25" );
anon.Replace( new Tag(0x0018,0x1063), "40.000000" );
anon.Replace( new Tag(0x0028,0x34), "4\\3" );
anon.Replace( new Tag(0x0028,0x2110), "01" );
writer.SetImage( image );
writer.SetFileName( "dummy.dcm" );
if( !writer.Write() )
{
System.Console.WriteLine( "Could not write" );
return 1;
}
return 0;
}
}
gdcm::Anonymizer
Anonymizer.
Definition gdcmAnonymizer.h:78
gdcm::Anonymizer::SetFile
void SetFile(const File &f)
Set/Get File.
Definition gdcmAnonymizer.h:126
gdcm::Anonymizer::Replace
bool Replace(Tag const &t, const char *value)
gdcm::Bitmap::SetNumberOfDimensions
void SetNumberOfDimensions(unsigned int dim)
gdcm::Bitmap::SetDataElement
void SetDataElement(DataElement const &de)
Definition gdcmBitmap.h:76
gdcm::Bitmap::SetDimension
void SetDimension(unsigned int idx, unsigned int dim)
gdcm::Bitmap::SetTransferSyntax
void SetTransferSyntax(TransferSyntax const &ts)
Transfer syntax.
Definition gdcmBitmap.h:69
gdcm::Bitmap::SetPhotometricInterpretation
void SetPhotometricInterpretation(PhotometricInterpretation const &pi)
gdcm::Bitmap::SetPixelFormat
void SetPixelFormat(PixelFormat const &pf)
Definition gdcmBitmap.h:115
gdcm::DataElement
Class to represent a Data Element either Implicit or Explicit.
Definition gdcmDataElement.h:59
gdcm::DataElement::SetValue
void SetValue(Value const &vl)
Definition gdcmDataElement.h:100
gdcm::DataElement::SetByteValue
void SetByteValue(const char *array, VL length)
Definition gdcmDataElement.h:126
gdcm::FileMetaInformation::SetDataSetTransferSyntax
void SetDataSetTransferSyntax(const TransferSyntax &ts)
gdcm::File
a DICOM File
Definition gdcmFile.h:34
gdcm::File::GetHeader
const FileMetaInformation & GetHeader() const
Get File Meta Information.
Definition gdcmFile.h:48
gdcm::Fragment
Class to represent a Fragment.
Definition gdcmFragment.h:44
gdcm::ImageReader
ImageReader.
Definition gdcmImageReader.h:34
gdcm::ImageReader::GetImage
const Image & GetImage() const
Return the read image.
gdcm::ImageWriter
ImageWriter.
Definition gdcmImageWriter.h:33
gdcm::ImageWriter::Write
bool Write() override
Write.
gdcm::Image
Image.
Definition gdcmImage.h:47
gdcm::MediaStorage
MediaStorage.
Definition gdcmMediaStorage.h:44
gdcm::MediaStorage::MSType
MSType
Definition gdcmMediaStorage.h:46
gdcm::MediaStorage::GetString
const char * GetString() const
Return the Media String of the object.
gdcm::PhotometricInterpretation
Class to represent an PhotometricInterpretation.
Definition gdcmPhotometricInterpretation.h:29
gdcm::PhotometricInterpretation::PIType
PIType
Definition gdcmPhotometricInterpretation.h:31
gdcm::PixelFormat
PixelFormat.
Definition gdcmPixelFormat.h:46
gdcm::PixmapWriter::SetImage
virtual void SetImage(Pixmap const &img)
gdcm::SequenceOfFragments
Class to represent a Sequence Of Fragments.
Definition gdcmSequenceOfFragments.h:32
gdcm::SequenceOfFragments::New
static SmartPointer< SequenceOfFragments > New()
Definition gdcmSequenceOfFragments.h:289
gdcm::System
Class to do system operation.
Definition gdcmSystem.h:27
gdcm::Tag
Class to represent a DICOM Data Element (Attribute) Tag (Group, Element).
Definition gdcmTag.h:39
gdcm::TransferSyntax
Class to manipulate Transfer Syntax.
Definition gdcmTransferSyntax.h:40
gdcm::TransferSyntax::TSType
TSType
Definition gdcmTransferSyntax.h:61
gdcm::UIDGenerator
Class for generating unique UID.
Definition gdcmUIDGenerator.h:28
gdcm::VL
Value Length.
Definition gdcmVL.h:30
gdcm::Writer::GetFile
File & GetFile()
Definition gdcmWriter.h:67
gdcm::Writer::SetFileName
void SetFileName(const char *filename_native)
Set the filename of DICOM file to write:
gdcm
Definition gdcmASN1.h:21