"Fossies" - the Fresh Open Source Software Archive

Member "pfstools-2.2.0/src/fileformat/pfsinyuv.cpp" (12 Aug 2021, 21905 Bytes) of package /linux/privat/pfstools-2.2.0.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pfsinyuv.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.0_vs_2.2.0.

A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.


    1 /**
    2  * @brief Read RAW Yuv files, commonly used for video compression
    3  * 
    4  * This file is a part of PFSTOOLS package.
    5  * ---------------------------------------------------------------------- 
    6  * Copyright (C) 2017 Rafal Mantiuk
    7  * 
    8  *  This program is free software; you can redistribute it and/or modify
    9  *  it under the terms of the GNU General Public License as published by
   10  *  the Free Software Foundation; either version 2 of the License, or
   11  *  (at your option) any later version.
   12  *
   13  *  This program is distributed in the hope that it will be useful,
   14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  *  GNU General Public License for more details.
   17  *
   18  *  You should have received a copy of the GNU General Public License
   19  *  along with this program; if not, write to the Free Software
   20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21  * ---------------------------------------------------------------------- 
   22  * 
   23  * @author George Ash
   24  *
   25  */
   26 
   27 #include <config.h>
   28 #include <string.h>
   29 
   30 #include <cstdlib>
   31 
   32 #include <algorithm>
   33 #include <iostream>
   34 #include <vector>
   35 //#include <arpa/inet.h>
   36 
   37 #include <cmath>
   38 #include <getopt.h>
   39 #include <pfs.h>
   40 #include <pfsutils.cpp>
   41 #include <climits>
   42 
   43 #define PROG_NAME "pfsinyuv"
   44 
   45 char charsToRemove[7] = {'b', 'i', 't', 's', 'f', 'p', 's'};
   46 
   47 //PL means a planar format per frame.
   48 enum ChromaFormat {PL420, PL444, V210};
   49 
   50 template<typename T>
   51 inline T clamp( T val, T min, T max )
   52 {
   53   if( val < min )
   54     return min;
   55   if( val > max )
   56     return max;
   57   return val;  
   58 }
   59 
   60 
   61 template<typename T>
   62 bool read_yuv_channel( FILE *fh, int width, int height, int stride, pfs::Array2D *dest, float gain, float offset, float min, float max )
   63 {
   64   T *line_buf = new T[width];
   65     
   66   for( int y = 0; y < height; y++ ) {      size_t read = fread( line_buf, sizeof( T ), width, fh );
   67   if( y == 0 && read == 0 ) { // End of file reached
   68     delete [] line_buf;
   69     return false;
   70   }
   71       
   72     if( read != width ) {
   73       delete [] line_buf;
   74       throw pfs::Exception( "Error when reading Yuv file" );
   75     }
   76     for( int x = 0; x < width; x++ ) {    
   77       float v_float = (float)line_buf[x]*gain - offset;        
   78       (*dest)(x<<stride, y<<stride) = clamp( v_float, min, max );
   79     }
   80     
   81   }    
   82   delete [] line_buf;
   83   return true;
   84 }
   85 
   86 void upscale_420to444( pfs::Array2D *ch )
   87 {
   88   const int height = ch->getRows();
   89   const int width = ch->getCols();
   90   
   91   // For even rows
   92     for( int y = 0; y < height; y += 2 ) {
   93     float v_l = (*ch)(0,y);
   94     float v_r;
   95     int x;    
   96     for( x = 1; x < (width-1); x += 2 ) {     
   97       v_r = (*ch)(x+1,y);
   98       // Interpolate between pixels on the left and right
   99       (*ch)(x,y) = (v_l + v_r)/2.f;
  100       v_l = v_r;
  101     }
  102     if( x < width )
  103       (*ch)(x,y) = v_r;         
  104   }
  105 
  106   // For odd rows
  107   int y;
  108     for( y = 1; y < (height-1); y += 2 ) {
  109     for( int x = 0; x < width; x++ ) {          
  110       (*ch)(x,y) = ((*ch)(x,y-1) + (*ch)(x,y+1))/2.f;     
  111     }   
  112   }
  113   // Last row
  114   if( y < height ) {
  115     for( int x = 0; x < width; x++ ) {          
  116       (*ch)(x,y) = (*ch)(x,y-1);      
  117     }   
  118   }   
  119 }
  120 
  121 void upscale_422to444( pfs::Array2D *ch )
  122 {
  123   const int height = ch->getRows();
  124   const int width = ch->getCols();
  125   // For odd columns
  126   for (int y = 0; y < height; y++){
  127     for( int x = 1; x < width-1; x+=2 ) {          
  128       (*ch)(x,y) = ((*ch)(x-1,y) + (*ch)(x+1,y))/2.f;     
  129     } 
  130   }
  131 }
  132 
  133 void parse210Block(unsigned int word, pfs::Array2D *thirdPart, pfs::Array2D *secondPart, pfs::Array2D *firstPart, unsigned int thirdPartX, unsigned int secondPartx, unsigned int firstPartX, unsigned int y, bool ycy){ //last bool denotes the ordering of luma and chroma within a word. not ycy -? cyc
  134 
  135   const unsigned int bitmask = 0x03FF;
  136 
  137   const float luma_offset = 16.f/219.f;
  138   const float luma_gain = 1.f/( (float)(1<<(2))*219.f);
  139   
  140   const float chroma_offset = 128.f/224.f;
  141   const float chroma_gain = 1.f/( (float)(1<<(2))*224.f);
  142   
  143   //now because of the somewhat arbitrary ordering, we don't know beforehand what offset and gain to apply. I'm just trying to make this less messy, but there's definitely a better way. TODO this.
  144   float first_last_gain = ycy ? luma_gain : chroma_gain;
  145   float first_last_offset = ycy ? luma_offset : chroma_offset;
  146   
  147   float second_gain = ycy ? chroma_gain : luma_gain;
  148   float second_offset = ycy ? chroma_offset : luma_offset;
  149   
  150   (*thirdPart)(thirdPartX,y) = (float)((word&(bitmask<<20))>>20)*first_last_gain - first_last_offset;
  151   (*secondPart)(secondPartx,y) = (float)((word&(bitmask<<10))>>10)*second_gain - second_offset;
  152   (*firstPart)(firstPartX,y) = (float)(word&(bitmask))*first_last_gain - first_last_offset;
  153 }
  154 
  155 bool read_v210_yuv( FILE *fh, int width, int height, pfs::Array2D *Ydest,pfs::Array2D *Cbdest, pfs::Array2D *Crdest){
  156   assert(sizeof(unsigned int) == 4);
  157   
  158   //2*width needed because width is needed to store luma, and width is also needed to store both chroma
  159   
  160   unsigned int paddedLineWidth = (unsigned int) ceil(((float)width)/6.)*4; //in 32 bit words
  161   unsigned int * line_buf = new unsigned int[paddedLineWidth];
  162 
  163 
  164   for( int y = 0; y<height; y++){
  165     size_t read = fread( line_buf, sizeof( unsigned int ), paddedLineWidth, fh );
  166 
  167     if( y == 0 && read == 0 ) { // End of file reached
  168       delete [] line_buf;
  169       return false;
  170     }  
  171     else if (read != paddedLineWidth){
  172       delete [] line_buf;
  173       throw pfs::Exception("Error when reading YUV file");
  174     }
  175     else{
  176       //who came up with this insane format
  177       //we read 4 words at a time :'(
  178       //loop over image pixels, therefore actual data lies in 2* our iteration variable
  179       //+= 6 because we write to 6 pixels below
  180       unsigned int bufIndex;
  181       for (int x = 0; x<width; x+=6){
  182         unsigned int word;
  183         bufIndex = (x/6)*4;
  184         
  185         //we store chroma in even cells, will interpolate later
  186 
  187         //first block 
  188         word = line_buf[bufIndex];
  189         
  190         parse210Block(word, Crdest, Ydest, Cbdest, x, x, x, y, false);
  191       
  192         //second block
  193         word = line_buf[bufIndex + 1];
  194 
  195         parse210Block(word, Ydest, Cbdest, Ydest, x+2, x+2, x+1, y, true);
  196 
  197         //third block
  198         word = line_buf[bufIndex + 2];
  199         
  200         parse210Block(word, Cbdest, Ydest, Crdest, x+4, x+3, x+2, y, false);
  201 
  202         //forth block
  203         word = line_buf[bufIndex + 3];
  204 
  205         parse210Block(word, Ydest, Crdest, Ydest, x+5, x+4, x+4, y, true);
  206         
  207       }  
  208     }
  209   }
  210   delete[] line_buf;
  211   upscale_422to444(Cbdest);
  212   upscale_422to444(Crdest);
  213   return true;
  214 }
  215 
  216 void removeCharsFromString(std::string &str, char* charsToRemove ) {
  217   for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) {
  218         str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() );
  219    }
  220 }
  221 
  222 int parseIntField(std::string i){
  223   //parses an integer field. Removes pesky characters like the b in 10b or the p in 1080p
  224   removeCharsFromString(i, charsToRemove);
  225   return std::atoi(i.data());
  226 }
  227 
  228 //more readable helper method for strings, case insensitive
  229 bool contains(std::string s1, std::string s2){
  230   
  231   std::transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
  232   std::transform(s2.begin(), s2.end(), s2.begin(), ::tolower);
  233   
  234   return s1.find(s2) != std::string::npos || s1 == s2;
  235 }
  236 
  237 
  238 //returns a vector of strings tokenized 
  239 std::vector<std::string> split(std::string s, std::string delim){
  240   return std::vector<std::string>();
  241 }
  242 
  243 bool parseFileName(const char * fileNameIn, int * width,  int * height, int * bitdepth, pfs::ColorSpace * colorspace, ChromaFormat * chromaFormat, int * fps){
  244   try{
  245     /* Parses the filename, puts the result into fields pointed to by the arguments
  246      returns true if all went well, false if there's a malformed filename
  247      string should be <prefix>_<width>x<height(p)>_<fps>_<bitdepth(b)>_<colorspace>_<chroma_format>
  248      Regex would be nice*/  
  249     std::string fileName(fileNameIn); 
  250     const std::string delimiter = "_";
  251     size_t pos = 0;
  252     std::string token;  
  253     std::vector<std::string> tokens;
  254 
  255     while ((pos = fileName.find(delimiter)) != std::string::npos) {
  256         token = fileName.substr(0, pos);
  257         tokens.push_back(token);
  258         fileName.erase(0, pos + delimiter.length());
  259     }
  260     tokens.push_back(fileName);
  261 
  262     for (std::vector<std::string>::iterator tok = tokens.begin() + 1; tok != tokens.end(); ++tok) {
  263       
  264       if (contains(*tok, "x")){
  265         sscanf(tok->c_str(), "%dx%d", width, height);
  266       }
  267       else if(*tok == "10" || *tok == "8" || *tok == "12" || tok->at(tok->length() - 1) == 'b' || contains(*tok, "bit")){ //last character is a b
  268         *bitdepth = parseIntField(*tok); 
  269       }
  270       else if(*tok == "24" || *tok == "25" || *tok == "50" || *tok == "60" || contains(*tok, "fps")){
  271         *fps = parseIntField(*tok);
  272       }
  273       else if (contains(*tok, "pq") || (contains(*tok, "2020") && !contains(*tok, "hlg"))){
  274         *colorspace = pfs::CS_PQYCbCr2020;
  275       }
  276       else if (contains(*tok, "bt") || contains(*tok, "709")){
  277         *colorspace = pfs::CS_YCbCr709;
  278       }
  279       else if (contains(*tok, "hlg")){
  280         *colorspace = pfs::CS_HLGYCbCr2020;
  281       }
  282       else if(contains(*tok, "444")) *chromaFormat = PL444; 
  283       else if(contains(*tok, "420")) *chromaFormat = PL420;
  284       else if(contains(*tok, "V210")) *chromaFormat = V210;
  285     }
  286     return true;
  287   }
  288   catch(...){
  289     return false;
  290   }
  291 }
  292 
  293 class YUVReader
  294 {
  295   FILE *fh;
  296   int width, height;
  297   int bit_depth;
  298   ChromaFormat chromaFormat;
  299   unsigned long int fileSize;
  300   
  301   enum ColorSpace { REC709, REC2020 };
  302   ColorSpace color_space;      
  303   
  304 public:
  305   YUVReader( FILE *fh, int width, int height, int bit_depth, ChromaFormat chromaFormat) : fh(fh), width( width ), height( height ), bit_depth( bit_depth ), chromaFormat( chromaFormat )
  306   {
  307     //initialize filesize
  308     fseek(fh, 0L, SEEK_END);
  309     fileSize = ftell(fh);
  310     //go back from where we came
  311     fseek(fh, 0L, SEEK_SET);
  312   }
  313   
  314   int getWidth() const 
  315     {
  316       return width;
  317     }
  318   
  319   int getHeight() const
  320     {
  321       return height;
  322     }
  323 
  324   unsigned long int getFileSize() const
  325     {
  326       return fileSize;
  327     }
  328   
  329   /*
  330   Advances a number of frames through the file without reading them */ 
  331 //  bool advanceFrames(int frameNo){
  332 //    int divisor = subsampling_420 ? 4 : 1; //this is the integer divisor of the green and blue channels (they are 4 times smaller if 4:2:0 subsampling is enabled) 
  333 //    long int offset = frameNo * (/*Red:*/ width * height * sizeof(STORE_FORMAT) + /*Green, Blue:*/ 2*width*height*sizeof(STORE_FORMAT)/divisor);
  334 //    return fseek(fh, offset, SEEK_CUR);
  335 //  }
  336 
  337   /* Seek to a given frame */
  338   bool seekToFrame(int frameNo){
  339     unsigned long int offset = getFileOffsetFromFrame(frameNo);
  340     if(offset > fileSize){
  341       throw pfs::Exception("Seeking past EOF, is your given frame range within the range of the input?");
  342     }
  343     else{
  344       return fseek(fh, offset, SEEK_SET);
  345     }
  346   }
  347 
  348   //gets the position in the file of the specified frame
  349   unsigned long int getFileOffsetFromFrame(int frameNo){
  350     if(chromaFormat == V210){
  351       /*TODO This*/
  352       return (unsigned long int) ceil(((float) width)/6)*16*height*frameNo;
  353     }
  354     else{
  355       unsigned long int divisor = chromaFormat == PL420 ? 4 : 1; //this is the integer divisor of the green and blue channels (they are 4 times smaller if 4:2:0 subsampling is enabled)
  356 
  357       if( frameNo <= 0 )
  358         throw pfs::Exception( "Invalid frame index. Frame are indexed from 1" );
  359 
  360       unsigned long int storeFormatSize = bit_depth <= 8 ? sizeof(unsigned char) : sizeof(unsigned short);
  361       unsigned long int offset = (frameNo-1) * (/*Luma:*/ width * height * storeFormatSize + /*CrCb:*/ 2*width*height*storeFormatSize/divisor);
  362       return offset;
  363     }
  364   }
  365   
  366   /**
  367    * Read a single frame from Yuv video file and store in 3 RGB channels of type pfs::Array. 
  368    * Integer values are converted into floating point values.
  369    * 
  370    * @return TRUE if the frame has been loaded successfully, FALSE if end-of-file. 
  371    * Exception is thrown if there is an issue reading a full frame.
  372    */
  373 
  374   bool readImage( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B ){  
  375     if(chromaFormat == V210){
  376       return read_v210_yuv(fh, width, height, R, G, B);
  377     }
  378     else{
  379       if(bit_depth <= 8){
  380         return readImageTyped<unsigned char>(R, G, B);
  381       }
  382       else{
  383         return readImageTyped<unsigned short>(R, G, B);
  384       }
  385     }
  386   }
  387 
  388   private: 
  389     template<typename T>
  390     bool readImageTyped( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B)
  391     {
  392 
  393       { // Read Y
  394       const float offset = 16.f/219.f;
  395       const float gain = 1.f/( (float)(1<<(bit_depth-8))*219.f);    
  396       if( !read_yuv_channel<T>( fh, width, height, 0, R, gain, offset, 0.f, 1.f ) )  {
  397         return false;
  398       } 
  399       }
  400       
  401       { // Read Cb, Cr
  402         const float offset = 128.f/224.f;
  403         const float gain = 1.f/( (float)(1<<(bit_depth-8))*224.f);
  404         unsigned int chroma_width = width;
  405         unsigned int chroma_height = height;
  406         unsigned int chroma_stride = 0;
  407         if (chromaFormat == PL420){
  408           chroma_width = width/2;
  409           chroma_height = height/2;
  410           chroma_stride = 1;  
  411         } 
  412 
  413         if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride, G, gain, offset, -0.5f, 0.5f ) ) 
  414         throw pfs::Exception( "EOF reached when reading the Cb portion of a frame" );
  415 
  416         if( !read_yuv_channel<T>( fh, chroma_width, chroma_height, chroma_stride, B, gain, offset, -0.5f, 0.5f ) ) 
  417         throw pfs::Exception( "EOF reached when reading the Cr portion of a frame" );
  418         if(chromaFormat == PL420){
  419           upscale_420to444( B ); 
  420           upscale_420to444( G );
  421         }
  422       }
  423     return true;    
  424     }
  425 };
  426 
  427 
  428 class QuietException 
  429 {
  430 };
  431 
  432 void printHelp()
  433 {
  434   std::cerr << PROG_NAME " [--verbose] [--quiet] [--width] [--height] [--colorspace] [--no-guess] [--chroma-format] [--bit-depth] [--frames] [--help]" << std::endl
  435             << "See man page for more information." << std::endl;
  436 }
  437 
  438 
  439 void readFrames( int argc, char* argv[] )
  440 {
  441   pfs::DOMIO pfsio;
  442 
  443   bool verbose = false;
  444   bool quiet = false;
  445   bool opt_noguess = false;
  446   int opt_width = -1;
  447   int opt_height = -1;
  448   int opt_bitdepth = -1;
  449   const char * opt_chroma_subsampling = NULL;
  450   int opt_frame_min = -1;
  451   int opt_frame_max = -1;
  452   int opt_frame_stride = 0;
  453   int opt_fps = -1;
  454   char * filename;
  455   pfs::ColorSpace opt_colorspace = pfs::CS_INVALID;
  456   
  457   // Parse command line parameters
  458   static struct option cmdLineOptions[] = {
  459     { "help", no_argument, NULL, 'p' },
  460     { "verbose", no_argument, NULL, 'v' },
  461     { "width", required_argument, NULL, 'w' },
  462     { "height", required_argument, NULL, 'h' },
  463     { "bit-depth", required_argument, NULL, 'b' },
  464     { "colorspace", required_argument, NULL, 'c' },           
  465     { "quiet", no_argument, NULL, 'q' },
  466     { "no-guess", no_argument, NULL, 'n'},
  467     { "fps", required_argument, NULL, 'f'},
  468     { "chroma-format", required_argument, NULL, 's'},
  469     { "frames", required_argument, NULL, 'r'},
  470     { NULL, 0, NULL, 0 }
  471   };
  472   static const char optstring[] = "-pw:h:vqb:c:f:nr:s:";
  473 
  474   int optionIndex = 0;
  475   while( 1 ) {
  476     int c = getopt_long (argc, argv, optstring, cmdLineOptions, &optionIndex);
  477     if(c == -1){ 
  478       break;
  479     }
  480     switch( c ) {
  481     case 'p':
  482       printHelp();
  483       throw QuietException();
  484     case 'v':
  485       verbose = true;
  486       break;
  487     case 'w':
  488       opt_width = strtol( optarg, NULL, 10 );
  489       break;
  490     case 'h':
  491       opt_height = strtol( optarg, NULL, 10 );
  492       break;
  493     case 'b':
  494       opt_bitdepth = strtol( optarg, NULL, 10 );
  495       break;
  496     case 's':
  497       opt_chroma_subsampling = optarg;
  498       break; 
  499     case 'n': 
  500       opt_noguess = true;
  501       break;
  502     case 'f':
  503       opt_fps = strtol(optarg, NULL, 10);
  504       break;
  505     case 'r': {
  506       pfs::parseFrameRange(optarg, opt_frame_min, opt_frame_max, opt_frame_stride);
  507       VERBOSE_STR << "Reading frames " << opt_frame_min << ":" << opt_frame_stride << ":"
  508                   << opt_frame_max << std::endl;      
  509       break;
  510     }
  511     case 'c':
  512       if( !strcasecmp( optarg, "pq2020" ) ) {
  513         opt_colorspace = pfs::CS_PQYCbCr2020;
  514       }
  515       else if( !strcasecmp( optarg, "bt709" )) {
  516       opt_colorspace = pfs::CS_YCbCr709;
  517       }
  518       else if( !strcasecmp( optarg, "hlg2020")){
  519         opt_colorspace = pfs::CS_HLGYCbCr2020;
  520       } 
  521       else {
  522         throw pfs::Exception( "Unrecognized colorspace name" );
  523       }
  524       break;
  525     case 'q':
  526       quiet = true;
  527       break;
  528     case '?':
  529       throw QuietException();
  530     case ':':
  531       throw QuietException();
  532     case '\1':
  533       filename = optarg;
  534     }
  535   }
  536 
  537   FILE * fh;
  538   if(filename != NULL){
  539     fh = fopen (filename, "rb");
  540   }
  541 
  542   if( fh == NULL ) {
  543     throw pfs::Exception("Couldn't find a filename to open, did you provide one");
  544   }; // No more frames
  545 
  546   int width = -1, height = -1, bitdepth = -1, frame_min = 1, frame_max = INT_MAX, frame_stride = 1, fps = -1;
  547   ChromaFormat chromaFormat = PL444;
  548   pfs::ColorSpace colorspace = pfs::CS_INVALID;
  549 
  550   VERBOSE_STR << "reading file '" << filename << "'" << std::endl;
  551   //we infer the metadata from the filename unless noguess is specified
  552   if(!opt_noguess){
  553     std::string rawName(filename); //these three lines extract the filename proper, without the containing directory prefix
  554     long unsigned int substring = rawName.find_last_of("\\/");
  555     rawName = rawName.substr(substring == std::string::npos ? 0 : substring);
  556     bool goodFileName = parseFileName(rawName.c_str(), &width, &height, &bitdepth, &colorspace, &chromaFormat, &fps);  
  557     if(!goodFileName){
  558       VERBOSE_STR << "Unable to parse filename: " << filename << "\n";
  559     }
  560   }
  561   // Over-ride the auto-recognized params with the ones specified as arguments
  562   if( opt_width > -1 )
  563     width = opt_width;
  564   if( opt_height > -1 )
  565     height = opt_height;
  566   if( opt_bitdepth > -1 )
  567     bitdepth = opt_bitdepth;
  568   if( opt_frame_min > -1 )
  569     frame_min = opt_frame_min;
  570   if( opt_frame_max > -1 )
  571     frame_max = opt_frame_max;
  572   if( opt_frame_stride != 0)
  573     frame_stride = opt_frame_stride;
  574   if( opt_colorspace != pfs::CS_INVALID )
  575     colorspace = opt_colorspace;
  576   if( opt_chroma_subsampling != NULL ){
  577     //set up our chromat supsampling, defaults to planar 444
  578     chromaFormat =
  579      strcmp(opt_chroma_subsampling, "420") == 0 ? PL420 :
  580      strcmp(opt_chroma_subsampling, "V210") == 0 ? V210 : 
  581      PL444;
  582  
  583   } 
  584   if( opt_fps > 0 ){
  585     fps = opt_fps;
  586   }
  587 
  588   VERBOSE_STR << "Yuv file " << width << "x" << height << " " << bitdepth << "bits" << std::endl;
  589   switch( colorspace ) {
  590     case pfs::CS_PQYCbCr2020:
  591       VERBOSE_STR << "colorspace: HDR PQ BT2020" << std::endl;
  592       break;
  593     case pfs::CS_YCbCr709:
  594       VERBOSE_STR << "colorspace: SDR BT709" << std::endl;
  595       break;
  596     case pfs::CS_HLGYCbCr2020:
  597       VERBOSE_STR << "colorspace: HDR HLG BT2020" << std::endl;
  598       break;
  599   }
  600 
  601   if( width <= 0 || height <= 0 )
  602     throw pfs::Exception( "Unspecified or incorrect resolution of the Yuv file" );
  603 
  604   if( bitdepth < 8 || bitdepth > 16 )
  605     throw pfs::Exception( "Unspecified or incorrect bit-depth of the Yuv file" );
  606   
  607   if( frame_min < 0 || frame_max < 0){
  608     throw pfs::Exception( "Frame range is invalid ");
  609   }
  610   if( colorspace == pfs::CS_INVALID ){
  611     throw pfs::Exception( "Unspecified colorspace of the Yuv file" );
  612   }
  613   if( fps > 0 ){
  614     VERBOSE_STR << "FPS: " << fps << std::endl;
  615   }
  616 
  617   YUVReader reader( fh, width, height, bitdepth, chromaFormat);
  618 
  619   
  620   int currentFrame = frame_min;
  621 
  622   while( (currentFrame <= frame_max && frame_stride > 0)
  623       || (currentFrame >= frame_max && frame_stride < 0) ) { //inclusive
  624     
  625     pfs::Frame *frame = pfsio.createFrame( reader.getWidth(),
  626                                            reader.getHeight() );
  627     pfs::Channel *X, *Y, *Z;
  628     frame->createXYZChannels( X, Y, Z );
  629 
  630     if(reader.seekToFrame(currentFrame)) {
  631       //some error occured in reading :(
  632       pfsio.freeFrame( frame ); 
  633       break;
  634     }
  635     
  636     //Store RGB data temporarily in XYZ channels
  637     bool success = reader.readImage( X, Y, Z );
  638     if( !success ) { // EOF reached
  639       pfsio.freeFrame( frame );
  640       break;
  641     }
  642 
  643     VERBOSE_STR << "Reading frame " << currentFrame << std::endl;    
  644 
  645     if( colorspace == pfs::CS_YCbCr709 ) {
  646       // The trick to get LDR data in the pfs stream
  647       pfs::transformColorSpace( colorspace, X, Y, Z, pfs::CS_SRGB, X, Y, Z );
  648       pfs::transformColorSpace( pfs::CS_RGB, X, Y, Z, pfs::CS_XYZ, X, Y, Z );
  649     } else {      
  650       pfs::transformColorSpace( colorspace, X, Y, Z, pfs::CS_XYZ, X, Y, Z );
  651     }
  652     
  653 
  654     switch( colorspace ) {
  655       case pfs::CS_PQYCbCr2020:
  656         frame->getTags()->setString("LUMINANCE", "ABSOLUTE");
  657         break;
  658       case pfs::CS_YCbCr709:
  659         frame->getTags()->setString("LUMINANCE", "DISPLAY");
  660         break;
  661       case pfs::CS_HLGYCbCr2020:
  662         frame->getTags()->setString("LUMINANCE", "ABSOLUTE");
  663         break;
  664     }
  665 
  666     if( fps > 0 ){
  667       frame->getTags()->setString("FPS", pfs::intToString(fps).c_str());
  668     }
  669     
  670     const char *fileNameTag = strcmp( "-", filename )==0 ? "stdin" : filename;
  671     frame->getTags()->setString( "FILE_NAME", fileNameTag );
  672 
  673     pfsio.writeFrame( frame, stdout );
  674     pfsio.freeFrame( frame );
  675     
  676     currentFrame += frame_stride;
  677   }
  678   fclose(fh);
  679 }
  680 
  681 
  682 int main( int argc, char* argv[] )
  683 {
  684   try {
  685     readFrames( argc, argv );
  686   }
  687   catch( pfs::Exception ex ) {
  688     fprintf( stderr, PROG_NAME " error: %s\n", ex.getMessage() );
  689     return EXIT_FAILURE;
  690   }        
  691   catch( QuietException ) {
  692     return EXIT_FAILURE;
  693   }        
  694   return EXIT_SUCCESS;
  695 }