Blame src/asfvideo.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
/*
Packit 01d647
 * Copyright (C) 2004-2018 Exiv2 authors
Packit 01d647
 * This program is part of the Exiv2 distribution.
Packit 01d647
 *
Packit 01d647
 * This program is free software; you can redistribute it and/or
Packit 01d647
 * modify it under the terms of the GNU General Public License
Packit 01d647
 * as published by the Free Software Foundation; either version 2
Packit 01d647
 * of the License, or (at your option) any later version.
Packit 01d647
 *
Packit 01d647
 * This program is distributed in the hope that it will be useful,
Packit 01d647
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 01d647
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 01d647
 * GNU General Public License for more details.
Packit 01d647
 *
Packit 01d647
 * You should have received a copy of the GNU General Public License
Packit 01d647
 * along with this program; if not, write to the Free Software
Packit 01d647
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
Packit 01d647
 */
Packit 01d647
/*
Packit 01d647
  File:      asfvideo.cpp
Packit 01d647
  Author(s): Abhinav Badola for GSoC 2012 (AB) <mail.abu.to@gmail.com>
Packit 01d647
  History:   08-Aug-12, AB: created
Packit 01d647
  Credits:   See header file
Packit 01d647
 */
Packit 01d647
// *****************************************************************************
Packit 01d647
// included header files
Packit 01d647
#include "config.h"
Packit 01d647
Packit 01d647
#ifdef EXV_ENABLE_VIDEO
Packit 01d647
#include "error.hpp"
Packit 01d647
#include "tags.hpp"
Packit 01d647
#include "tags_int.hpp"
Packit 01d647
#include "asfvideo.hpp"
Packit 01d647
#include "futils.hpp"
Packit 01d647
#include "basicio.hpp"
Packit 01d647
#include "types.hpp"
Packit 01d647
#include "riffvideo.hpp"
Packit 01d647
#include "convert.hpp"
Packit 01d647
Packit 01d647
// + standard includes
Packit 01d647
#include <cmath>
Packit 01d647
#include <cstring>
Packit 01d647
#include <ctype.h>
Packit 01d647
#include <cassert>
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// class member definitions
Packit 01d647
namespace Exiv2 {
Packit 01d647
    namespace Internal {
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      Tag Look-up list for ASF Type Video Files
Packit 01d647
      Associates the GUID of a Tag with its Tag Name(i.e. Human Readable Form)
Packit 01d647
      Tags have been diferentiated into Various Categories.
Packit 01d647
      The categories have been listed above the Tag Groups
Packit 01d647
Packit 01d647
     */
Packit 01d647
    extern const TagVocabulary GUIDReferenceTags[] =  {
Packit 01d647
        /// Top-level ASF object GUIDS
Packit 01d647
        {   "75B22630-668E-11CF-A6D9-00AA0062CE6C", "Header" },
Packit 01d647
        {   "75B22636-668E-11CF-A6D9-00AA0062CE6C", "Data" },
Packit 01d647
        {   "33000890-E5B1-11CF-89F4-00A0C90349CB", "Simple_Index" },
Packit 01d647
        {   "D6E229D3-35DA-11D1-9034-00A0C90349BE", "Index" },
Packit 01d647
        {   "FEB103F8-12AD-4C64-840F-2A1D2F7AD48C", "Media_Index" },
Packit 01d647
        {   "3CB73FD0-0C4A-4803-953D-EDF7B6228F0C", "Timecode_Index" },
Packit 01d647
Packit 01d647
        /// Header Object GUIDs
Packit 01d647
        {   "8CABDCA1-A947-11CF-8EE4-00C00C205365", "File_Properties" },
Packit 01d647
        {   "B7DC0791-A9B7-11CF-8EE6-00C00C205365", "Stream_Properties" },
Packit 01d647
        {   "5FBF03B5-A92E-11CF-8EE3-00C00C205365", "Header_Extension" },
Packit 01d647
        {   "86D15240-311D-11D0-A3A4-00A0C90348F6", "Codec_List" },
Packit 01d647
        {   "1EFB1A30-0B62-11D0-A39B-00A0C90348F6", "Script_Command" },
Packit 01d647
        {   "F487CD01-A951-11CF-8EE6-00C00C205365", "Marker" },
Packit 01d647
        {   "D6E229DC-35DA-11D1-9034-00A0C90349BE", "Bitrate_Mutual_Exclusion" },
Packit 01d647
        {   "75B22635-668E-11CF-A6D9-00AA0062CE6C", "Error_Correction" },
Packit 01d647
        {   "75B22633-668E-11CF-A6D9-00AA0062CE6C", "Content_Description" },
Packit 01d647
        {   "D2D0A440-E307-11D2-97F0-00A0C95EA850", "Extended_Content_Description" },
Packit 01d647
        {   "2211B3FA-BD23-11D2-B4B7-00A0C955FC6E", "Content_Branding" },
Packit 01d647
        {   "7BF875CE-468D-11D1-8D82-006097C9A2B2", "Stream_Bitrate_Properties" },
Packit 01d647
        {   "2211B3FB-BD23-11D2-B4B7-00A0C955FC6E", "Content_Encryption" },
Packit 01d647
        {   "298AE614-2622-4C17-B935-DAE07EE9289C", "Extended_Content_Encryption" },
Packit 01d647
        {   "2211B3FC-BD23-11D2-B4B7-00A0C955FC6E", "Digital_Signature" },
Packit 01d647
        {   "1806D474-CADF-4509-A4BA-9AABCB96AAE8", "Padding" },
Packit 01d647
Packit 01d647
        /// Header Extension Object GUIDs
Packit 01d647
        {   "14E6A5CB-C672-4332-8399-A96952065B5A", "Extended_Stream_Properties" },
Packit 01d647
        {   "A08649CF-4775-4670-8A16-6E35357566CD", "Advanced_Mutual_Exclusion" },
Packit 01d647
        {   "D1465A40-5A79-4338-B71B-E36B8FD6C249", "Group_Mutual_Exclusion" },
Packit 01d647
        {   "D4FED15B-88D3-454F-81F0-ED5C45999E24", "Stream_Prioritization" },
Packit 01d647
        {   "A69609E6-517B-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing" },
Packit 01d647
        {   "7C4346A9-EFE0-4BFC-B229-393EDE415C85", "Language_List" },
Packit 01d647
        {   "C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA", "Metadata" },
Packit 01d647
        {   "44231C94-9498-49D1-A141-1D134E457054", "Metadata_Library" },
Packit 01d647
        {   "D6E229DF-35DA-11D1-9034-00A0C90349BE", "Index_Parameters" },
Packit 01d647
        {   "6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7", "Media_Index_Parameters" },
Packit 01d647
        {   "F55E496D-9797-4B5D-8C8B-604DFE9BFB24", "Timecode_Index_Parameters" },
Packit 01d647
        {   "26F18B5D-4584-47EC-9F5F-0E651F0452C9", "Compatibility" },
Packit 01d647
        {   "43058533-6981-49E6-9B74-AD12CB86D58C", "Advanced_Content_Encryption" },
Packit 01d647
Packit 01d647
        /// Stream Properties Object Stream Type GUIDs
Packit 01d647
        {   "F8699E40-5B4D-11CF-A8FD-00805F5C442B", "Audio_Media" },
Packit 01d647
        {   "BC19EFC0-5B4D-11CF-A8FD-00805F5C442B", "Video_Media" },
Packit 01d647
        {   "59DACFC0-59E6-11D0-A3AC-00A0C90348F6", "Command_Media" },
Packit 01d647
        {   "B61BE100-5B4E-11CF-A8FD-00805F5C442B", "JFIF_Media" },
Packit 01d647
        {   "35907DE0-E415-11CF-A917-00805F5C442B", "Degradable_JPEG_Media" },
Packit 01d647
        {   "91BD222C-F21C-497A-8B6D-5AA86BFC0185", "File_Transfer_Media" },
Packit 01d647
        {   "3AFB65E2-47EF-40F2-AC2C-70A90D71D343", "Binary_Media" },
Packit 01d647
Packit 01d647
        /// Web stream Type-Specific Data GUIDs
Packit 01d647
        {   "776257D4-C627-41CB-8F81-7AC7FF1C40CC", "Web_Stream_Media_Subtype" },
Packit 01d647
        {   "DA1E6B13-8359-4050-B398-388E965BF00C", "Web_Stream_Format" },
Packit 01d647
Packit 01d647
        /// Stream Properties Object Error Correction Type GUIDs
Packit 01d647
        {   "20FB5700-5B55-11CF-A8FD-00805F5C442B", "No_Error_Correction" },
Packit 01d647
        {   "BFC3CD50-618F-11CF-8BB2-00AA00B4E220", "Audio_Spread" },
Packit 01d647
Packit 01d647
        /// Header Extension Object GUIDs
Packit 01d647
        {   "ABD3D211-A9BA-11cf-8EE6-00C00C205365", "Reserved_1" },
Packit 01d647
Packit 01d647
        /// Advanced Content Encryption Object System ID GUIDs
Packit 01d647
        {   "7A079BB6-DAA4-4e12-A5CA-91D38DC11A8D", "Content_Encryption_System_Windows_Media_DRM_Network_Devices" },
Packit 01d647
Packit 01d647
        /// Codec List Object GUIDs
Packit 01d647
        {   "86D15241-311D-11D0-A3A4-00A0C90348F6", "Reserved_2" },
Packit 01d647
Packit 01d647
        /// Script Command Object GUIDs
Packit 01d647
        {   "4B1ACBE3-100B-11D0-A39B-00A0C90348F6", "Reserved_3" },
Packit 01d647
Packit 01d647
        /// Marker Object GUIDs
Packit 01d647
        {   "4CFEDB20-75F6-11CF-9C0F-00A0C90349CB", "Reserved_4" },
Packit 01d647
Packit 01d647
        /// Mutual Exclusion Object Exclusion Type GUIDs
Packit 01d647
        {   "D6E22A00-35DA-11D1-9034-00A0C90349BE", "Mutex_Language" },
Packit 01d647
        {   "D6E22A01-35DA-11D1-9034-00A0C90349BE", "Mutex_Bitrate" },
Packit 01d647
        {   "D6E22A02-35DA-11D1-9034-00A0C90349BE", "Mutex_Unknown" },
Packit 01d647
Packit 01d647
        /// Bandwidth Sharing Object GUIDs
Packit 01d647
        {   "AF6060AA-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Exclusive" },
Packit 01d647
        {   "AF6060AB-5197-11D2-B6AF-00C04FD908E9", "Bandwidth_Sharing_Partial" },
Packit 01d647
Packit 01d647
        /// Standard Payload Extension System GUIDs
Packit 01d647
        {   "399595EC-8667-4E2D-8FDB-98814CE76C1E", "Payload_Extension_System_Timecode" },
Packit 01d647
        {   "E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B", "Payload_Extension_System_File_Name" },
Packit 01d647
        {   "D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC", "Payload_Extension_System_Content_Type" },
Packit 01d647
        {   "1B1EE554-F9EA-4BC8-821A-376B74E4C4B8", "Payload_Extension_System_Pixel_Aspect_Ratio" },
Packit 01d647
        {   "C6BD9450-867F-4907-83A3-C77921B733AD", "Payload_Extension_System_Sample_Duration" },
Packit 01d647
        {   "6698B84E-0AFA-4330-AEB2-1C0A98D7A44D", "Payload_Extension_System_Encryption_Sample_ID" },
Packit 01d647
        {   "00E1AF06-7BEC-11D1-A582-00C04FC29CFB", "Payload_Extension_System_Degradable_JPEG" }
Packit 01d647
    };
Packit 01d647
Packit 01d647
    //! Audio codec type-specific data in ASF
Packit 01d647
    extern const TagDetails audioCodec[] =  {
Packit 01d647
        {    0x161, "Windows Media Audio (7, 8, and 9 Series)" },
Packit 01d647
        {    0x162, "Windows Media Audio 9 Professional" },
Packit 01d647
        {    0x163, "Windows Media Audio 9 Lossless" },
Packit 01d647
        {   0x7A21, "GSM-AMR (CBR, no SID)" },
Packit 01d647
        {   0x7A22, "GSM-AMR (VBR including SID)" }
Packit 01d647
    };
Packit 01d647
Packit 01d647
    extern const TagDetails filePropertiesTags[] =  {
Packit 01d647
        {    7, "Xmp.video.FileLength" },
Packit 01d647
        {    6, "Xmp.video.CreationDate" },
Packit 01d647
        {    5, "Xmp.video.DataPackets" },
Packit 01d647
        {    4, "Xmp.video.Duration" },
Packit 01d647
        {    3, "Xmp.video.SendDuration" },
Packit 01d647
        {    2, "Xmp.video.Preroll" },
Packit 01d647
        {    1, "Xmp.video.MaxBitRate" }
Packit 01d647
    };
Packit 01d647
Packit 01d647
    extern const TagDetails contentDescriptionTags[] =  {
Packit 01d647
        {    0, "Xmp.video.Title" },
Packit 01d647
        {    1, "Xmp.video.Author" },
Packit 01d647
        {    2, "Xmp.video.Copyright" },
Packit 01d647
        {    3, "Xmp.video.Description" },
Packit 01d647
        {    4, "Xmp.video.Rating" }
Packit 01d647
    };
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Function used to read data from data buffer, reads 16-bit character
Packit 01d647
          array and stores it in std::string object.
Packit 01d647
      @param buf Exiv2 data buffer, which stores the information
Packit 01d647
      @return Returns std::string object .
Packit 01d647
     */
Packit 01d647
    std::string toString16(Exiv2::DataBuf& buf)
Packit 01d647
    {
Packit 01d647
        std::ostringstream os; char t;
Packit 01d647
Packit 01d647
        for(int i = 0; i <= buf.size_; i += 2 ) {
Packit 01d647
            t = buf.pData_[i] + 16 * buf.pData_[i + 1];
Packit 01d647
            if(t == 0) {
Packit 01d647
                if(i)
Packit 01d647
                    os << '\0';
Packit 01d647
                break;
Packit 01d647
            }
Packit 01d647
            os<< t;
Packit 01d647
        }
Packit 01d647
        return os.str();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Function used to check equality of two Tags (ignores case).
Packit 01d647
      @param str1 char* Pointer to First Tag
Packit 01d647
      @param str2 char* Pointer to Second Tag
Packit 01d647
      @return Returns true if both are equal.
Packit 01d647
     */
Packit 01d647
    bool compareTag(const char* str1, const char* str2) {
Packit 01d647
        if ( strlen(str1) != strlen(str2))
Packit 01d647
            return false;
Packit 01d647
Packit 01d647
        for ( uint64_t i = 0 ; i < strlen(str1); ++i )
Packit 01d647
            if (tolower(str1[i]) != tolower(str2[i]))
Packit 01d647
                return false;
Packit 01d647
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Function used to convert a decimal number to its Hexadecimal
Packit 01d647
          equivalent, then parsed into a character
Packit 01d647
      @param n Integer which is to be parsed as Hexadecimal character
Packit 01d647
      @return Return a Hexadecimal number, in character
Packit 01d647
     */
Packit 01d647
    char returnHEX(int n) {
Packit 01d647
        if(n >= 0 && n <= 9)
Packit 01d647
            return (char)(n + 48);
Packit 01d647
        else
Packit 01d647
            return (char)(n + 55);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Function used to calculate GUID, Tags comprises of 16 bytes.
Packit 01d647
          The Buffer contains the Tag in Binary Form. The information is then
Packit 01d647
          parsed into a character array GUID.
Packit 01d647
     */
Packit 01d647
    void getGUID (byte buf[], char GUID[]) {
Packit 01d647
        int i;
Packit 01d647
        for (i = 0; i < 4; ++i) {
Packit 01d647
            GUID[(3 - i) * 2]      = returnHEX(buf[i] / 0x10);
Packit 01d647
            GUID[(3 - i) * 2 + 1]  = returnHEX(buf[i] % 0x10);
Packit 01d647
        }
Packit 01d647
        for (i = 4; i < 6; ++i) {
Packit 01d647
            GUID[(9 - i) * 2 + 1]  = returnHEX(buf[i] / 0x10);
Packit 01d647
            GUID[(9 - i) * 2 + 2]  = returnHEX(buf[i] % 0x10);
Packit 01d647
        }
Packit 01d647
        for (i = 6; i < 8; ++i) {
Packit 01d647
            GUID[(14 - i) * 2]     = returnHEX(buf[i] / 0x10);
Packit 01d647
            GUID[(14 - i) * 2 + 1] = returnHEX(buf[i] % 0x10);
Packit 01d647
        }
Packit 01d647
        for (i = 8; i < 10; ++i) {
Packit 01d647
            GUID[ i * 2 + 3]       = returnHEX(buf[i] / 0x10);
Packit 01d647
            GUID[ i * 2 + 4]       = returnHEX(buf[i] % 0x10);
Packit 01d647
        }
Packit 01d647
        for (i = 10; i < 16; ++i) {
Packit 01d647
            GUID[ i * 2 + 4]       = returnHEX(buf[i] / 0x10);
Packit 01d647
            GUID[ i * 2 + 5]       = returnHEX(buf[i] % 0x10);
Packit 01d647
        }
Packit 01d647
        GUID[36] = '\0'; GUID[8] = GUID[13] = GUID[18] = GUID[23] = '-';
Packit 01d647
    }
Packit 01d647
Packit 01d647
    /*!
Packit 01d647
      @brief Function used to check if data stored in buf is equivalent to
Packit 01d647
          ASF Header Tag's GUID.
Packit 01d647
      @param buf Exiv2 byte buffer
Packit 01d647
      @return Returns true if the buffer data is equivalent to Header GUID.
Packit 01d647
     */
Packit 01d647
    bool isASFType (byte buf[]) {
Packit 01d647
Packit 01d647
        if(buf[0]  == 0x30   && buf[1]  == 0x26   && buf[2]  == 0xb2   && buf[3]  == 0x75 &&
Packit 01d647
           buf[4]  == 0x8e   && buf[5]  == 0x66   && buf[6]  == 0xcf   && buf[7]  == 0x11 &&
Packit 01d647
           buf[8]  == 0xa6   && buf[9]  == 0xd9   && buf[10] == 0x00   && buf[11] == 0xaa &&
Packit 01d647
           buf[12] == 0x00   && buf[13] == 0x62   && buf[14] == 0xce   && buf[15] == 0x6c )
Packit 01d647
                return true;
Packit 01d647
Packit 01d647
        return false;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    //! Function used to convert buffer data into 64-bit Integer, information stored in littleEndian format
Packit 01d647
    uint64_t getUint64_t(Exiv2::DataBuf& buf) {
Packit 01d647
        uint64_t temp = 0;
Packit 01d647
Packit 01d647
        for(int i = 0; i < 8; ++i){
Packit 01d647
            temp = temp + static_cast<uint64_t>(buf.pData_[i]*(pow(static_cast<float>(256), i)));
Packit 01d647
        }
Packit 01d647
        return temp;
Packit 01d647
    }
Packit 01d647
Packit 01d647
}}                                      // namespace Internal, Exiv2
Packit 01d647
Packit 01d647
namespace Exiv2 {
Packit 01d647
Packit 01d647
    using namespace Exiv2::Internal;
Packit 01d647
Packit 01d647
    AsfVideo::AsfVideo(BasicIo::AutoPtr io)
Packit 01d647
        : Image(ImageType::asf, mdNone, io)
Packit 01d647
    {
Packit 01d647
    } // AsfVideo::AsfVideo
Packit 01d647
Packit 01d647
    std::string AsfVideo::mimeType() const
Packit 01d647
    {
Packit 01d647
        return "video/asf";
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void AsfVideo::writeMetadata()
Packit 01d647
    {
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void AsfVideo::readMetadata()
Packit 01d647
    {
Packit 01d647
        if (io_->open() != 0) throw Error(kerDataSourceOpenFailed, io_->path(), strError());
Packit 01d647
Packit 01d647
        // Ensure that this is the correct image type
Packit 01d647
        if (!isAsfType(*io_, false)) {
Packit 01d647
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
Packit 01d647
            throw Error(kerNotAnImage, "ASF");
Packit 01d647
        }
Packit 01d647
Packit 01d647
        IoCloser closer(*io_);
Packit 01d647
        clearMetadata();
Packit 01d647
        continueTraversing_ = true;
Packit 01d647
        io_->seek(0, BasicIo::beg);
Packit 01d647
        height_ = width_ = 1;
Packit 01d647
Packit 01d647
        xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576;
Packit 01d647
        xmpData_["Xmp.video.FileName"] = io_->path();
Packit 01d647
        xmpData_["Xmp.video.MimeType"] = mimeType();
Packit 01d647
Packit 01d647
        while (continueTraversing_) decodeBlock();
Packit 01d647
Packit 01d647
        aspectRatio();
Packit 01d647
    } // AsfVideo::readMetadata
Packit 01d647
Packit 01d647
    void AsfVideo::decodeBlock()
Packit 01d647
    {
Packit 01d647
        const long bufMinSize = 9;
Packit 01d647
        DataBuf buf(bufMinSize);
Packit 01d647
        uint64_t size = 0;
Packit 01d647
        buf.pData_[8] = '\0' ;
Packit 01d647
        const TagVocabulary* tv;
Packit 01d647
        uint64_t cur_pos = io_->tell();
Packit 01d647
Packit 01d647
        byte guidBuf[16];
Packit 01d647
        io_->read(guidBuf, 16);
Packit 01d647
Packit 01d647
        if(io_->eof()) {
Packit 01d647
            continueTraversing_ = false;
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        char GUID[37] = ""; //the getGUID function write the GUID[36],
Packit 01d647
Packit 01d647
        getGUID(guidBuf, GUID);
Packit 01d647
        tv = find( GUIDReferenceTags, GUID);
Packit 01d647
Packit 01d647
        std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
        io_->read(buf.pData_, 8);
Packit 01d647
        size = getUint64_t(buf);
Packit 01d647
Packit 01d647
        if(tv) {
Packit 01d647
            tagDecoder(tv,size-24);
Packit 01d647
        }
Packit 01d647
        else
Packit 01d647
            io_->seek(cur_pos + size, BasicIo::beg);
Packit 01d647
Packit 01d647
        localPosition_ = io_->tell();
Packit 01d647
    } // AsfVideo::decodeBlock
Packit 01d647
Packit 01d647
    void AsfVideo::tagDecoder(const TagVocabulary *tv, uint64_t size)
Packit 01d647
    {
Packit 01d647
        uint64_t cur_pos = io_->tell();
Packit 01d647
        DataBuf buf(1000);
Packit 01d647
        unsigned long count = 0, tempLength = 0;
Packit 01d647
        buf.pData_[4] = '\0' ;
Packit 01d647
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
Packit 01d647
Packit 01d647
        if(compareTag( exvGettext(tv->label_), "Header")) {
Packit 01d647
            localPosition_ = 0;
Packit 01d647
            io_->read(buf.pData_, 4);
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
Packit 01d647
            while(localPosition_ < cur_pos + size) decodeBlock();
Packit 01d647
        }
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "File_Properties"))
Packit 01d647
            fileProperties();
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Stream_Properties"))
Packit 01d647
            streamProperties();
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Metadata"))
Packit 01d647
            metadataHandler(1);
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Extended_Content_Description"))
Packit 01d647
            metadataHandler(2);
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Metadata_Library"))
Packit 01d647
            metadataHandler(3);
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Codec_List"))
Packit 01d647
            codecList();
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Content_Description"))
Packit 01d647
            contentDescription(size);
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Extended_Stream_Properties"))
Packit 01d647
            extendedStreamProperties(size);
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Header_Extension")) {
Packit 01d647
            localPosition_ = 0;
Packit 01d647
            headerExtension(size);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Language_List")) {
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
            count = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
            while(count--) {
Packit 01d647
                std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
                io_->read(buf.pData_, 1);   tempLength = (int)buf.pData_[0];
Packit 01d647
Packit 01d647
                io_->read(buf.pData_, tempLength);
Packit 01d647
                v->read(toString16(buf));
Packit 01d647
            }
Packit 01d647
            xmpData_.add(Exiv2::XmpKey("Xmp.video.TrackLang"), v.get());
Packit 01d647
        }
Packit 01d647
Packit 01d647
        io_->seek(cur_pos + size, BasicIo::beg);
Packit 01d647
        localPosition_ = io_->tell();
Packit 01d647
    } // AsfVideo::tagDecoder
Packit 01d647
Packit 01d647
    void AsfVideo::extendedStreamProperties(uint64_t size)
Packit 01d647
    {
Packit 01d647
        uint64_t cur_pos = io_->tell(), avgTimePerFrame = 0;
Packit 01d647
        DataBuf buf(8);
Packit 01d647
        static int previousStream;
Packit 01d647
        io_->seek(cur_pos + 48, BasicIo::beg);
Packit 01d647
Packit 01d647
        std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
        io_->read(buf.pData_, 2);
Packit 01d647
        streamNumber_ = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 2);
Packit 01d647
        io_->read(buf.pData_, 8);
Packit 01d647
        avgTimePerFrame = getUint64_t(buf);
Packit 01d647
Packit 01d647
        if(previousStream < streamNumber_  &&  avgTimePerFrame != 0)
Packit 01d647
            xmpData_["Xmp.video.FrameRate"] = (double)10000000/(double)avgTimePerFrame;
Packit 01d647
Packit 01d647
        previousStream = streamNumber_;
Packit 01d647
        io_->seek(cur_pos + size, BasicIo::beg);
Packit 01d647
    } // AsfVideo::extendedStreamProperties
Packit 01d647
Packit 01d647
    void AsfVideo::contentDescription(uint64_t size)
Packit 01d647
    {
Packit 01d647
        const long pos = io_->tell();
Packit 01d647
        if (pos == -1) throw Error(kerFailedToReadImageData);
Packit 01d647
        long length[5];
Packit 01d647
        for (int i = 0 ; i < 5 ; ++i) {
Packit 01d647
            byte buf[2];
Packit 01d647
            io_->read(buf, 2);
Packit 01d647
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
Packit 01d647
            length[i] = getUShort(buf, littleEndian);
Packit 01d647
        }
Packit 01d647
        for (int i = 0 ; i < 5 ; ++i) {
Packit 01d647
            DataBuf buf(length[i]);
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
            io_->read(buf.pData_, length[i]);
Packit 01d647
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
Packit 01d647
            const TagDetails* td = find(contentDescriptionTags, i);
Packit 01d647
            assert(td);
Packit 01d647
            std::string str((const char*)buf.pData_, length[i]);
Packit 01d647
            if (convertStringCharset(str, "UCS-2LE", "UTF-8")) {
Packit 01d647
                xmpData_[td->label_] = str;
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                xmpData_[td->label_] = toString16(buf);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        if (io_->seek(pos + size, BasicIo::beg)) throw Error(kerFailedToReadImageData);
Packit 01d647
    } // AsfVideo::contentDescription
Packit 01d647
Packit 01d647
    void AsfVideo::streamProperties()
Packit 01d647
    {
Packit 01d647
        DataBuf buf(20);
Packit 01d647
        buf.pData_[8] = '\0' ;
Packit 01d647
        byte guidBuf[16]; int stream = 0;
Packit 01d647
        io_->read(guidBuf, 16);
Packit 01d647
        char streamType[37] = "";
Packit 01d647
        Exiv2::RiffVideo *test = NULL;
Packit 01d647
Packit 01d647
        getGUID(guidBuf, streamType);
Packit 01d647
        const TagVocabulary* tv;
Packit 01d647
        tv = find( GUIDReferenceTags, streamType);
Packit 01d647
        io_->read(guidBuf, 16);
Packit 01d647
Packit 01d647
        if(compareTag( exvGettext(tv->label_), "Audio_Media"))
Packit 01d647
            stream = 1;
Packit 01d647
        else if(compareTag( exvGettext(tv->label_), "Video_Media"))
Packit 01d647
            stream = 2;
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 8);
Packit 01d647
        if(stream == 2)
Packit 01d647
            xmpData_["Xmp.video.TimeOffset"] = getUint64_t(buf);
Packit 01d647
        else if(stream == 1)
Packit 01d647
            xmpData_["Xmp.audio.TimeOffset"] = getUint64_t(buf);
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 8);
Packit 01d647
        std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
        io_->read(buf.pData_, 1);
Packit 01d647
        streamNumber_ = (int)buf.pData_[0] & 127;
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 5);
Packit 01d647
        std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
        io_->read(buf.pData_, 2);
Packit 01d647
        long temp = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
        if(stream == 2) {
Packit 01d647
            xmpData_["Xmp.video.Width"] = temp;
Packit 01d647
            width_ = temp;
Packit 01d647
        }
Packit 01d647
        else if(stream == 1) {
Packit 01d647
            xmpData_["Xmp.audio.Codec"] = test->printAudioEncoding(temp);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 2);
Packit 01d647
        temp = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
        if(stream == 1)
Packit 01d647
            xmpData_["Xmp.audio.ChannelType"] = temp;
Packit 01d647
Packit 01d647
        io_->read(buf.pData_, 4);
Packit 01d647
        temp = Exiv2::getULong(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
        if(stream == 2) {
Packit 01d647
            xmpData_["Xmp.video.Height"] = temp;
Packit 01d647
            height_ = temp;
Packit 01d647
        }
Packit 01d647
        else if(stream == 1) {
Packit 01d647
            xmpData_["Xmp.audio.SampleRate"] = temp;
Packit 01d647
        }
Packit 01d647
    } // AsfVideo::streamProperties
Packit 01d647
Packit 01d647
    void AsfVideo::codecList()
Packit 01d647
    {
Packit 01d647
        DataBuf buf(200);
Packit 01d647
        io_->read(buf.pData_, 16);
Packit 01d647
        std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
        io_->read(buf.pData_, 4);
Packit 01d647
        int codecCount = Exiv2::getULong(buf.pData_, littleEndian), descLength = 0, codecType = 0;
Packit 01d647
Packit 01d647
        while(codecCount--) {
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
            codecType = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
            descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2;
Packit 01d647
Packit 01d647
            if (descLength < 0) {
Packit 01d647
            #ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR   << " Description found in this ASF file is not of valid size ."
Packit 01d647
                                << " Entries considered invalid. Not Processed.\n";
Packit 01d647
            #endif
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                io_->read(buf.pData_, descLength);
Packit 01d647
                if(codecType == 1)
Packit 01d647
                    xmpData_["Xmp.video.Codec"] = toString16(buf);
Packit 01d647
                else if(codecType == 2)
Packit 01d647
                    xmpData_["Xmp.audio.Compressor"] = toString16(buf);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
            descLength = Exiv2::getUShort(buf.pData_, littleEndian) * 2;
Packit 01d647
Packit 01d647
            if (descLength < 0) {
Packit 01d647
            #ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR   << " Description found in this ASF file is not of valid size ."
Packit 01d647
                                << " Entries considered invalid. Not Processed.\n";
Packit 01d647
            #endif
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                io_->read(buf.pData_, descLength);
Packit 01d647
Packit 01d647
                if(codecType == 1)
Packit 01d647
                    xmpData_["Xmp.video.CodecDescription"] = toString16(buf);
Packit 01d647
                else if(codecType == 2)
Packit 01d647
                    xmpData_["Xmp.audio.CodecDescription"] = toString16(buf);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
            io_->read(buf.pData_, 2);
Packit 01d647
            descLength = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
            if (descLength < 0) {
Packit 01d647
            #ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR   << " Description found in this ASF file is not of valid size ."
Packit 01d647
                                << " Entries considered invalid. Not Processed.\n";
Packit 01d647
            #endif
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                io_->read(buf.pData_, descLength);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
    } // AsfVideo::codecList
Packit 01d647
Packit 01d647
    void AsfVideo::headerExtension(uint64_t size)
Packit 01d647
    {
Packit 01d647
        uint64_t cur_pos = io_->tell();
Packit 01d647
        DataBuf buf(20);
Packit 01d647
        io_->read(buf.pData_, 18);
Packit 01d647
        buf.pData_[4] = '\0' ;
Packit 01d647
        io_->read(buf.pData_, 4);
Packit 01d647
Packit 01d647
        while(localPosition_ < cur_pos + size) decodeBlock();
Packit 01d647
Packit 01d647
        io_->seek(cur_pos + size, BasicIo::beg);
Packit 01d647
    } // AsfVideo::headerExtension
Packit 01d647
Packit 01d647
    void AsfVideo::metadataHandler(int meta)
Packit 01d647
    {
Packit 01d647
        DataBuf buf(5000);
Packit 01d647
        io_->read(buf.pData_, 2);
Packit 01d647
        int recordCount = Exiv2::getUShort(buf.pData_, littleEndian), nameLength = 0, dataLength = 0, dataType = 0;
Packit 01d647
        Exiv2::Value::AutoPtr v = Exiv2::Value::create(Exiv2::xmpSeq);
Packit 01d647
        byte guidBuf[16];   char fileID[37] = "";
Packit 01d647
Packit 01d647
        while(recordCount--) {
Packit 01d647
            std::memset(buf.pData_, 0x0, buf.size_);
Packit 01d647
Packit 01d647
            if(meta == 1 || meta == 3) {
Packit 01d647
                io_->read(buf.pData_, 4);
Packit 01d647
                io_->read(buf.pData_, 2);
Packit 01d647
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
                io_->read(buf.pData_, 2);
Packit 01d647
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
                io_->read(buf.pData_, 4);
Packit 01d647
                dataLength = Exiv2::getULong(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
                if (nameLength > 5000) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
Packit 01d647
                              << " entries considered invalid; not read.\n";
Packit 01d647
#endif
Packit 01d647
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
Packit 01d647
                } else {
Packit 01d647
                    io_->read(buf.pData_, nameLength);
Packit 01d647
                }
Packit 01d647
Packit 01d647
                v->read(toString16(buf));
Packit 01d647
                if(dataType == 6) {
Packit 01d647
                    io_->read(guidBuf, 16);
Packit 01d647
                    getGUID(guidBuf, fileID);
Packit 01d647
                }
Packit 01d647
                else
Packit 01d647
                    // Sanity check with an "unreasonably" large number
Packit 01d647
                    if (dataLength > 5000) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                        EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
Packit 01d647
                                  << " entries considered invalid; not read.\n";
Packit 01d647
#endif
Packit 01d647
                        io_->seek(io_->tell() + dataLength, BasicIo::beg);
Packit 01d647
                    }
Packit 01d647
                else
Packit 01d647
                        io_->read(buf.pData_, dataLength);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            else if(meta == 2) {
Packit 01d647
                io_->read(buf.pData_, 2);
Packit 01d647
                nameLength = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
                if (nameLength > 5000) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR << "Xmp.video.Metadata nameLength was found to be larger than 5000 "
Packit 01d647
                              << " entries considered invalid; not read.\n";
Packit 01d647
#endif
Packit 01d647
                    io_->seek(io_->tell() + nameLength, BasicIo::beg);
Packit 01d647
                } else {
Packit 01d647
                    io_->read(buf.pData_, nameLength);
Packit 01d647
                }
Packit 01d647
Packit 01d647
                v->read(toString16(buf));
Packit 01d647
Packit 01d647
                io_->read(buf.pData_, 2);
Packit 01d647
                dataType = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
                io_->read(buf.pData_, 2);
Packit 01d647
                dataLength = Exiv2::getUShort(buf.pData_, littleEndian);
Packit 01d647
Packit 01d647
                // Sanity check with an "unreasonably" large number
Packit 01d647
                if (dataLength > 5000) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_ERROR << "Xmp.video.Metadata dataLength was found to be larger than 5000 "
Packit 01d647
                              << " entries considered invalid; not read.\n";
Packit 01d647
#endif
Packit 01d647
                    io_->seek(io_->tell() + dataLength, BasicIo::beg);
Packit 01d647
                }
Packit 01d647
            else
Packit 01d647
                io_->read(buf.pData_, dataLength);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            if(dataType == 0) {                       // Unicode String
Packit 01d647
                v->read(toString16(buf));
Packit 01d647
            }
Packit 01d647
            else if(dataType == 2 || dataType == 5) { // 16-bit Unsigned Integer
Packit 01d647
                v->read( Exiv2::toString( Exiv2::getUShort(buf.pData_, littleEndian)));
Packit 01d647
            }
Packit 01d647
            else if(dataType == 3) {                  // 32-bit Unsigned Integer
Packit 01d647
                v->read( Exiv2::toString( Exiv2::getULong( buf.pData_, littleEndian)));
Packit 01d647
            }
Packit 01d647
            else if(dataType == 4) {                  // 64-bit Unsigned Integer
Packit 01d647
                v->read(Exiv2::toString(getUint64_t(buf)));
Packit 01d647
            }
Packit 01d647
            else if(dataType == 6) {                  // 128-bit GUID
Packit 01d647
                v->read(Exiv2::toString(fileID));
Packit 01d647
            }
Packit 01d647
            else {                                    // Byte array
Packit 01d647
                v->read( Exiv2::toString(buf.pData_));
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if(meta == 1) {
Packit 01d647
            xmpData_.add(Exiv2::XmpKey("Xmp.video.Metadata"), v.get());
Packit 01d647
        }
Packit 01d647
        else if(meta == 2) {
Packit 01d647
            xmpData_.add(Exiv2::XmpKey("Xmp.video.ExtendedContentDescription"), v.get());
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            xmpData_.add(Exiv2::XmpKey("Xmp.video.MetadataLibrary"), v.get());
Packit 01d647
        }
Packit 01d647
    } // AsfVideo::metadataHandler
Packit 01d647
Packit 01d647
    void AsfVideo::fileProperties()
Packit 01d647
    {
Packit 01d647
        DataBuf buf(9);
Packit 01d647
        buf.pData_[8] = '\0' ;
Packit 01d647
Packit 01d647
        byte guidBuf[16];
Packit 01d647
        io_->read(guidBuf, 16);
Packit 01d647
        char fileID[37] = ""; int count = 7;
Packit 01d647
        getGUID(guidBuf, fileID);
Packit 01d647
        xmpData_["Xmp.video.FileID"] = fileID;
Packit 01d647
Packit 01d647
        const TagDetails* td;
Packit 01d647
Packit 01d647
        while(count--) {
Packit 01d647
            td = find(filePropertiesTags , (count + 1));
Packit 01d647
            io_->read(buf.pData_, 8);
Packit 01d647
Packit 01d647
            if(count == 0) {
Packit 01d647
                buf.pData_[4] = '\0' ;
Packit 01d647
                io_->read(buf.pData_, 4); io_->read(buf.pData_, 4);
Packit 01d647
            }
Packit 01d647
Packit 01d647
            if(count == 3 || count == 2) {
Packit 01d647
                xmpData_[exvGettext(td->label_)] = getUint64_t(buf) / 10000;
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                xmpData_[exvGettext(td->label_)] = getUint64_t(buf);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
    } // AsfVideo::fileProperties
Packit 01d647
Packit 01d647
    void AsfVideo::aspectRatio()
Packit 01d647
    {
Packit 01d647
        //TODO - Make a better unified method to handle all cases of Aspect Ratio
Packit 01d647
Packit 01d647
        double aspectRatio = (double)width_ / (double)height_;
Packit 01d647
        aspectRatio = floor(aspectRatio*10) / 10;
Packit 01d647
        xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
Packit 01d647
Packit 01d647
        int aR = (int) ((aspectRatio*10.0)+0.1);
Packit 01d647
Packit 01d647
        switch  (aR) {
Packit 01d647
            case 13 : xmpData_["Xmp.video.AspectRatio"] = "4:3"     ; break;
Packit 01d647
            case 17 : xmpData_["Xmp.video.AspectRatio"] = "16:9"    ; break;
Packit 01d647
            case 10 : xmpData_["Xmp.video.AspectRatio"] = "1:1"     ; break;
Packit 01d647
            case 16 : xmpData_["Xmp.video.AspectRatio"] = "16:10"   ; break;
Packit 01d647
            case 22 : xmpData_["Xmp.video.AspectRatio"] = "2.21:1"  ; break;
Packit 01d647
            case 23 : xmpData_["Xmp.video.AspectRatio"] = "2.35:1"  ; break;
Packit 01d647
            case 12 : xmpData_["Xmp.video.AspectRatio"] = "5:4"     ; break;
Packit 01d647
            default : xmpData_["Xmp.video.AspectRatio"] = aspectRatio;break;
Packit 01d647
        }
Packit 01d647
    } // AsfVideo::aspectRatio
Packit 01d647
Packit 01d647
Packit 01d647
    Image::AutoPtr newAsfInstance(BasicIo::AutoPtr io, bool /*create*/)
Packit 01d647
    {
Packit 01d647
        Image::AutoPtr image(new AsfVideo(io));
Packit 01d647
        if (!image->good()) {
Packit 01d647
            image.reset();
Packit 01d647
        }
Packit 01d647
        return image;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool isAsfType(BasicIo& iIo, bool advance)
Packit 01d647
    {
Packit 01d647
        const int32_t len = 16;
Packit 01d647
        byte buf[len];
Packit 01d647
        iIo.read(buf, len);
Packit 01d647
Packit 01d647
        if (iIo.error() || iIo.eof()) {
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        bool matched = isASFType(buf);
Packit 01d647
        if (!advance || !matched) {
Packit 01d647
            iIo.seek(0, BasicIo::beg);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        return matched;
Packit 01d647
    }
Packit 01d647
Packit 01d647
}                                       // namespace Exiv2
Packit 01d647
#endif // EXV_ENABLE_VIDEO