class WahWah::AsfTag

Constants

AUDIO_MEDIA_OBJECT_GUID
CONTENT_DESCRIPTION_OBJECT_GUID
EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID
EXTENDED_CONTENT_DESCRIPTOR_NAME_MAPPING
FILE_PROPERTIES_OBJECT_GUID
HEADER_OBJECT_CONTENT_SIZE
HEADER_OBJECT_GUID
STREAM_PROPERTIES_OBJECT_GUID

Private Instance Methods

parse() click to toggle source

ASF files are logically composed of three types of top-level objects: the Header Object, the Data Object, and the Index Object(s). The Header Object is mandatory and must be placed at the beginning of every ASF file. Of the three top-level ASF objects, the Header Object is the only one that contains other ASF objects. All Unicode strings in ASF uses UTF-16, little endian, and the Byte-Order Marker (BOM) character is not present.

# File lib/wahwah/asf_tag.rb, line 29
def parse
  header_object = Asf::Object.new(@file_io)
  return unless header_object.valid?

  total_header_object_size = header_object.size + Asf::Object::HEADER_SIZE

  return unless header_object.guid == HEADER_OBJECT_GUID

  # Header Object contains 6 bytes useless data, so skip it.
  @file_io.seek(HEADER_OBJECT_CONTENT_SIZE, IO::SEEK_CUR)

  until total_header_object_size <= @file_io.pos
    sub_object = Asf::Object.new(@file_io)
    parse_sub_object(sub_object)
  end
end
parse_content_description_object(object) click to toggle source

Content Description Object structure:

Field name Field type Size (bits)

Object ID GUID 128 Object Size QWORD 64 Title Length WORD 16 Author Length WORD 16 Copyright Length WORD 16 Description Length WORD 16 Rating Length WORD 16 Title WCHAR Varies Author WCHAR Varies Copyright WCHAR Varies Description WCHAR Varies Rating WCHAR Varies

# File lib/wahwah/asf_tag.rb, line 210
def parse_content_description_object(object)
  object_data = StringIO.new(object.data)
  title_length, author_length, copyright_length, description_length, _ = object_data.read(10).unpack('v' * 5)

  @title = Helper.encode_to_utf8(object_data.read(title_length), source_encoding: 'UTF-16LE')
  @artist = Helper.encode_to_utf8(object_data.read(author_length), source_encoding: 'UTF-16LE')
  object_data.seek(copyright_length, IO::SEEK_CUR)
  @comments.push(Helper.encode_to_utf8(object_data.read(description_length), source_encoding: 'UTF-16LE'))
end
parse_extended_content_description_object(object) click to toggle source

Extended Content Description Object structure:

Field name Field type Size (bits)

Object ID GUID 128 Object Size QWORD 64 Content Descriptors Count WORD 16 Content Descriptors See text varies

The structure of each Content Descriptor:

Field Name Field Type Size (bits)

Descriptor Name Length WORD 16 Descriptor Name WCHAR varies Descriptor Value Data Type WORD 16 Descriptor Value Length WORD 16 Descriptor Value See text varies

Specifies the type of data stored in the Descriptor Value field. The types are defined in the following table.

Value Type Descriptor value length

0x0000 Unicode string varies 0x0001 BYTE array varies 0x0002 BOOL 32 0x0003 DWORD 32 0x0004 QWORD 64 0x0005 WORD 16

# File lib/wahwah/asf_tag.rb, line 124
def parse_extended_content_description_object(object)
  object_data = StringIO.new(object.data)
  descriptors_count = object_data.read(2).unpack('v').first

  descriptors_count.times do
    name_length = object_data.read(2).unpack('v').first
    name = Helper.encode_to_utf8(object_data.read(name_length), source_encoding: 'UTF-16LE')
    value_type, value_length = object_data.read(4).unpack('vv')
    value = object_data.read(value_length)

    attr_value = case value_type
                 when 0
                   Helper.encode_to_utf8(value, source_encoding: 'UTF-16LE')
                 when 1
                   value
                 when 2, 3
                   value.unpack('V').first
                 when 4
                   value.unpack('Q<').first
                 when 5
                   value.unpack('v').first
    end

    attr_name = EXTENDED_CONTENT_DESCRIPTOR_NAME_MAPPING[name]
    instance_variable_set("@#{attr_name}", attr_value) unless attr_name.nil?
  end
end
parse_file_properties_object(object) click to toggle source

File Properties Object structure:

Field name Field type Size (bits)

Object ID GUID 128 Object Size QWORD 64 File ID GUID 128 File Size QWORD 64 Creation Date QWORD 64 Data Packets Count QWORD 64 Play Duration QWORD 64 Send Duration QWORD 64 Preroll QWORD 64 Flags DWORD 32

Broadcast Flag                      1 (LSB)
Seekable Flag                       1
Reserved                            30

Minimum Data Packet Size DWORD 32 Maximum Data Packet Size DWORD 32 Maximum Bitrate DWORD 32

Play Duration Specifies the time needed to play the file in 100-nanosecond units. The value of this field is invalid if the Broadcast Flag bit in the Flags field is set to 1.

Preroll Specifies the amount of time to buffer data before starting to play the file, in millisecond units. If this value is nonzero, the Play Duration field and all of the payload Presentation Time fields have been offset by this amount.

# File lib/wahwah/asf_tag.rb, line 87
def parse_file_properties_object(object)
  play_duration, preroll, flags = object.data.unpack('x40Q<x8Q<b32')
  @duration = (play_duration / 10000000.0 - preroll / 1000.0).round if flags[0] == '0'
end
parse_stream_properties_object(object) click to toggle source

Stream Properties Object structure:

Field Name Field Type Size (bits) Object ID GUID 128 Object Size QWORD 64 Stream Type GUID 128 Error Correction Type GUID 128 Time Offset QWORD 64 Type-Specific Data Length DWORD 32 Error Correction Data Length DWORD 32 Flags WORD 16

Stream Number                           7 (LSB)
Reserved                                8
Encrypted Content Flag                  1

Reserved DWORD 32 Type-Specific Data BYTE varies Error Correction Data BYTE varies

Stream Type specifies the type of the stream (for example, audio, video, and so on). Any streams with unrecognized Stream Type values should be ignored.

Audio media type Object structure:

Field name Field type Size (bits)

Codec ID / Format Tag WORD 16 Number of Channels WORD 16 Samples Per Second DWORD 32 Average Number of Bytes Per Second DWORD 32 Block Alignment WORD 16 Bits Per Sample WORD 16

# File lib/wahwah/asf_tag.rb, line 183
def parse_stream_properties_object(object)
  object_data = StringIO.new(object.data)
  stream_type, type_specific_data_length = object_data.read(54).unpack('a16x24V')
  stream_type_guid = Helper.byte_string_to_guid(stream_type)

  return unless stream_type_guid == AUDIO_MEDIA_OBJECT_GUID

  @sample_rate, bytes_per_second, @bit_depth = object_data.read(type_specific_data_length).unpack('x4VVx2v')
  @bitrate = (bytes_per_second * 8.0 / 1000).round
end
parse_sub_object(sub_object) click to toggle source
# File lib/wahwah/asf_tag.rb, line 46
def parse_sub_object(sub_object)
  case sub_object.guid
  when FILE_PROPERTIES_OBJECT_GUID
    parse_file_properties_object(sub_object)
  when EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID
    parse_extended_content_description_object(sub_object)
  when STREAM_PROPERTIES_OBJECT_GUID
    parse_stream_properties_object(sub_object)
  when CONTENT_DESCRIPTION_OBJECT_GUID
    parse_content_description_object(sub_object)
  else
    sub_object.skip
  end
end