freeMXF.org

Open discussion about freeMXF.org tools and MXF in general
It is currently Thu Oct 29, 2020 11:17 pm
Board index » MXF Categories » freeMXF.org Tools



Post new topic Reply to topic  [ 6 posts ] 
Author
Search for:
Message

Offline
Board User

Joined: Mon Jul 11, 2011 6:13 pm
Posts: 5

Post Posted: Mon Jul 11, 2011 8:48 pm 
Top  
Hello,

I'm totally new to mxf format and Mxflib, and I'd like to code a method which would allow me to extract precise jp2k images from a mxf file knowing their timecodes. So I decided to use Mxflib. From what I've understood after reading a paper about MXF format, I have to read each body partitions of the file, which each contains an Essence Container (Generic Container), and each Essence Container contains Content Packages. Each Content Package represents a frame of the video, and especially a Picture Item (the image) and a Data Item which contains the Timecode.
But even with little understanding of the format, I don't see how I can code that with Mxflib classes and methods. I read mxfsplit.cpp but I don't see how I can obtain images and timecodes (I see a ThisSink variable which is an EssenceSink but I doesn't seem to be holding Picture, Audio, Data etc... Items. I suppose that this code takes partitions of the file one by one :

Code:
std::string DictName = "dict.xml";
LoadDictionary(DictName);
MXFFilePtr TestFile = new MXFFile;
   if (! TestFile->Open(argv[num_options+1], true))
   {
      perror(argv[num_options+1]);
      return 1;
   }

   // Get a RIP (however possible)
   TestFile->GetRIP();
RIP::iterator it = TestFile->FileRIP.begin();
   while(it != TestFile->FileRIP.end())
   {
TestFile->Seek((*it).second->ByteOffset);
         PartitionPtr ThisPartition = TestFile->ReadPartition();
         if(ThisPartition)
         {

// Find a way to extract Essence Container, then Content Packages and their Picture and Data Items ...

...


But I don't see how I can then extract Essence Container, then Content Packages, then Picture and Data Items.
Some help would be really appreciated from you expert folks 8) .

 Profile  

Offline
Insider

Joined: Thu Apr 15, 2004 10:39 am
Posts: 198
Location: Scotland

Post Posted: Mon Jul 11, 2011 11:07 pm 
Top  
What you will need to do is use a BodyReader object.

The basic principle is as follows:
Code:
// Open your input file
MXFFilePtr InFile = new MXFFile();
InFile->Open(Filename, true);

// Get the master metadata
PartitionPtr MasterPartition = InFile->ReadMasterPartition();
MasterPartition->ReadMetadata();
MetadataPtr Meta = MasterPartition->ParseMetadata();

/* Decide what BodySID you are interested in and the JP2k track within that essence container */

// Build body reader for this file
BodyReaderPtr Reader = new BodyReader(InFile);

// Notify the reader that we are interested in the specified stream
MakeGCReader(BodySID, Handler);

// Set up a handler for the sub-stream holding the JP2k essence
GCReaderPtr StreamReader = Reader->GetGCReader(BodySID);
DataHandlerPtr Handler = new JP2KDataHandler( /*Some Params*/);
StreamReader->SetDataHandler(TrackNumber, Handler);

// Parse the file
InFile->Seek(0);
while(!InFile->EOF())
{
    if (!Reader->ReadFromFile()) break;
}

// Done
InFile->Close();


You will need to define a class JP2KDataHandler based on GCReadHandler_Base. The Handler object instantiated from this class in the above code will receive HandleData() callbacks each time a KLV of essence is read from the specified sub-stream of the essence. The code shown will actually cause every JP2K image to be sent to your handler, and you will need a way to sort out the ones of interest. This can be done by decoding the index table (if your file has one) or simply counting from the start of the file. You may need to use BodyReader->ReadFromFile(true) to force only a single KLV to be parsed when you are at the desired location, or send GCReader::StopReading() from the handler when it has the data it needs

 Profile WWW  

Offline
Board User

Joined: Mon Jul 11, 2011 6:13 pm
Posts: 5

Post Posted: Tue Jul 12, 2011 9:04 am 
Top  
Hi,
Thanks for your quick and accurate reply.


I, however, have some questions :

1) You said "Decide what BodySID you are interested in and the JP2k track within that essence container", but I don't see what BodySID represents. I executed this code on my mxf file :
Code:
// Open the file
      MXFFilePtr TestFile = new MXFFile;
      bool ret = TestFile->Open(file, true);
      if (!ret) {
         cout << "Error when opening " << file << endl;
         system("pause");
      }
// Obtain RIP (if present)
      TestFile->GetRIP();

      RIP::iterator it = TestFile->FileRIP.begin();

      while(it != TestFile->FileRIP.end()) {

         // Go to the pointed Partition
         TestFile->Seek((*it).second->ByteOffset);

         // Read Partition
         PartitionPtr ThisPartition = TestFile->ReadPartition();

         // Get BodySID
         UInt32 bSID = ThisPartition->GetUInt(BodySID_UL);
cout << " BodySID = " << bSID << endl;


And all I obtained was BodySID of either 1 or 0. Is it normal ?


2) Shouldn't the part
Code:
// Notify the reader that we are interested in the specified stream
MakeGCReader(BodySID, Handler);
be placed just before the "Parse File" part, since the Handler variable is declared after the call to MakeGCReader in your code ?


3) By "DataHandlerPtr", did you rather mean "GCReaderHandlerPtr" ?

4) Should the extraction of a frame be done in the HandleData function of JP2KDataHandler using the KLVObjectPtr ?

 Profile  

Offline
Board User

Joined: Mon Jul 11, 2011 6:13 pm
Posts: 5

Post Posted: Wed Jul 13, 2011 7:52 am 
Top  
Alright so I wrote this part of code in the function that reads my file :
Code:
// Load Dictionary
      string DictName = "dict.xml";
#ifdef COMPILED_DICT
      if( UseCompiledDict )
      {
         LoadDictionary(DictData);
      }
      else
#endif // COMPILED_DICT
      {
         LoadDictionary(DictName);
      }

      // Open input file
      MXFFilePtr InFile = new MXFFile();
      InFile->Open(file, true);

      // Get the master metadata
      PartitionPtr MasterPartition = InFile->ReadMasterPartition();
      MasterPartition->ReadMetadata();
      MetadataPtr Meta = MasterPartition->ParseMetadata();

      // Decide what BodySID we are interested in and the JP2k track within that essence container
      UInt32 BodySID = 1;
      UInt32 TrackNumber = 1;

      // Build body reader for this file
      BodyReaderPtr Reader = new BodyReader(InFile);

      GCReadHandlerPtr Handler = new JP2KDataHandler();

      // Notify the reader that we are interested in the specified stream
      Reader->MakeGCReader(BodySID, Handler);

      // Set up a handler for the sub-stream holding the JP2k essence
      GCReaderPtr StreamReader = Reader->GetGCReader(BodySID);
      StreamReader->SetDataHandler(TrackNumber, Handler);

      // Parse the file
      InFile->Seek(0);
      while ( !(InFile->Eof()) ) {
         if (!(Reader->ReadFromFile())) {
            break;
         }
      }

      // Done
      InFile->Close();


BodySID and Tracknumber values have yet to be set properly. Like I said in my previous post, BodySID seems to have only 2 possible values in the file I'm working on : 0 or 1. Since a value of 0 means that the partition has no body, I set BodySID variable to 1. Concerning Tracknumber, well, I just randomly put 1 (may have to change I guess) ...


Here's my JP2KDataHandler class :
Code:
class JP2KDataHandler : public GCReadHandler_Base
{
public:

   /*!
      @brief Constructeur par défaut
   */
   JP2KDataHandler() {}

   /*!
      @brief Destructeur
   */
   ~JP2KDataHandler() {}

   /*!
      @brief HandleData
   */
   bool HandleData(GCReaderPtr Caller, KLVObjectPtr Object)
{
UL objectUL = Object->GetUL();
      cout << "UL = " << objectUL.GetString() << endl;
}
      
};


Am I supposed to put something in the default constructor ?
When executing the code on my mxf file, the printed ULs are :
UL = [060e2b34.0102.0101.0d010301.15010801]
UL = [060e2b34.0102.0101.0d010301.16020101]
UL = [060e2b34.0102.0101.0d010301.16020102]
UL = [060e2b34.0102.0101.03010210.01000000]

I don't see these UL in the dict.xml file, but I guess the first UL represents the JPEG2000 images, the second & third the 2 audio streams and the last the data item. So all I would have to do is extract the value of the KLVObject in HandleData when UL is [060e2b34.0102.0101.0d010301.15010801] or [060e2b34.0102.0101.03010210.01000000].

Am I right ?

 Profile  

Offline
Board User

Joined: Mon Jul 11, 2011 6:13 pm
Posts: 5

Post Posted: Sat Jul 16, 2011 10:41 am 
Top  
I'm stuck on this since days. Here's what I wrote so far for the HandleData function :

Code:
bool JP2KDataHandler::HandleData(GCReaderPtr Caller, KLVObjectPtr Object)
{
   // Read the Label
   UL objectUL = Object->GetUL();

   if (objectUL.GetString().compare(29,6,"150108") == 0) {   // Jpeg2000 Frame  (I guess)

      nbFrames++;

      cout << "JPEG 2000 frame" << endl;

      int read = Object->ReadData((size_t) Object->GetLength());
      DataChunk rawData = Object->GetData();


   } else if (objectUL.GetString().compare(29,6,"160201") == 0) {   // Audio Stream  (I guess)

      // Nothing to do since it's not the part I'm interested in
      cout << "Audio Stream" << endl;

   } else if (objectUL.GetString().compare(29,6,"010000") == 0) {   // Data Item  (I guess)

      cout << "Data Item" << endl;

   } else {

      cout << "UNKNOWN UL" << endl;

   }


   return true;
}


Concerning the jpeg2000 frame case, is the picture contained in rawData.Data ? When printing rawData.Size it's only around 250,000 whereas the video on my mxf file is 1920x1080 ! Should I use a class or function to convert it and export it to a jp2 file (Like using something like
Code:
JP2K_EssenceSubParser* jpSub = new JP2K_EssenceSubParser; FileHandle fileH = FileOpenRead(file); EssenceStreamDescriptorList PDList = jpSub->IdentifyEssence(fileH);
?

 Profile  

Offline
Board User

Joined: Mon Jul 11, 2011 6:13 pm
Posts: 5

Post Posted: Thu Jul 21, 2011 1:16 pm 
Top  
Okay I finally managed to extract all I want from my mxf files.

However, I tried reading new jpeg2000-mxf files I got, but it crashes when reading the Master Partition. It's exactly the same problem than the one in this topic http://freemxf.org/forum/viewtopic.php?t=229 . It seems that the audio format AES3PCM is not contained in the dictionnary "dict.xml" (or has problem, since I see a AES3PCMDescriptor tag in dict.xml) and then cause the ReadMetadata function to crash. Is there a way to "update" dict.xml so as it recognizes this format, or find a way to skip these part of metadata when reading (since I'm not interested in the audio stream anyways) ?

 Profile  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

Jump to:  


Who is online

Users browsing this forum: No registered users and 1 guest

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group :: Style based on FI Subice by phpBBservice.nl :: All times are UTC