nativeaudio

audio playback for Go
Log | Files | Refs | README | LICENSE

mf.go (35639B)


      1 //go:build windows
      2 
      3 // Package mf wraps the minimal subset of Windows Media Foundation needed
      4 // to decode compressed audio into PCM.
      5 //
      6 // Only Startup, Shutdown, Decode and Format are meant for callers. The
      7 // remaining identifiers mirror the SDK headers so they can be checked
      8 // against them, and are exported only for that readability; this package
      9 // is internal and they are not part of the public API.
     10 package mf
     11 
     12 import (
     13 	"fmt"
     14 	"io"
     15 	"runtime"
     16 	"sync"
     17 	"syscall"
     18 	"time"
     19 	"unicode/utf16"
     20 	"unsafe"
     21 
     22 	"golang.org/x/sys/windows"
     23 )
     24 
     25 // Format describes the PCM produced by Decode.
     26 type Format struct {
     27 	SampleRate     int // samples per second.
     28 	Channels       int // number channels.
     29 	BytesPerSample int // bytes per sample.
     30 }
     31 
     32 // Startup initialises Media Foundation and resolves the entry points
     33 // this package uses. Call Shutdown once per successful Startup.
     34 func Startup() error {
     35 	return MFStartup(MF_VERSION, MFSTARTUP_LITE)
     36 }
     37 
     38 // Shutdown releases Media Foundation.
     39 func Shutdown() error {
     40 	return MFShutdown()
     41 }
     42 
     43 // Stream decodes audio incrementally, implementing [io.ReadCloser] over
     44 // s16le PCM. It owns the Media Foundation objects backing the decode,
     45 // so Close must be called to release them.
     46 type Stream struct {
     47 	// compressed is retained so the caller's buffer cannot be collected
     48 	// while the memory stream built from it is still alive.
     49 	compressed []byte
     50 
     51 	istream    *IStream
     52 	byteStream *IMFByteStream
     53 	attributes *IMFAttributes
     54 	reader     *IMFSourceReader
     55 	samples    *SourceReader
     56 	cb         *callback
     57 
     58 	format Format
     59 	closed bool
     60 }
     61 
     62 // Open prepares a decode of compressed audio held in memory. The
     63 // returned Stream yields s16le PCM and must be closed.
     64 func Open(compressed []byte) (_ *Stream, err error) {
     65 	s := &Stream{compressed: compressed}
     66 
     67 	// Anything already allocated is released if a later step fails.
     68 	defer func() {
     69 		if err != nil {
     70 			s.release()
     71 		}
     72 	}()
     73 
     74 	s.istream = SHCreateMemStream(unsafe.SliceData(compressed), len(compressed))
     75 	if s.istream == nil {
     76 		return nil, fmt.Errorf("could not allocate IStream")
     77 	}
     78 
     79 	// We need to adapt the generic IStream to a Media Foundation stream type.
     80 	if err := MFCreateMFByteStreamOnStream(s.istream, &s.byteStream); err != nil {
     81 		return nil, fmt.Errorf("creating MFByteStream from IStream: %w", err)
     82 	}
     83 
     84 	// Attributes to configure the source reader with: hardware codecs,
     85 	// and the callback that puts the reader in asynchronous mode.
     86 	if err := MFCreateAttributes(&s.attributes, 2); err != nil {
     87 		return nil, fmt.Errorf("creating attributes to apply to source reader: %w", err)
     88 	}
     89 	if err := s.attributes.SetUINT32(&MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1); err != nil {
     90 		return nil, fmt.Errorf("enabling hardware transforms: %w", err)
     91 	}
     92 
     93 	// Without a callback the reader is synchronous, and a synchronous
     94 	// ReadSample on a malformed stream can block forever with no way to
     95 	// interrupt it.
     96 	s.cb = newCallback()
     97 	if err := s.attributes.SetUnknown(&MF_SOURCE_READER_ASYNC_CALLBACK, unsafe.Pointer(s.cb)); err != nil {
     98 		return nil, fmt.Errorf("setting async callback: %w", err)
     99 	}
    100 
    101 	// Create the source reader using the byte stream.
    102 	if err := MFCreateSourceReaderFromByteStream(s.byteStream, s.attributes, &s.reader); err != nil {
    103 		return nil, fmt.Errorf("creating IMFSourceReader from IMFByteStream: %w", err)
    104 	}
    105 
    106 	if err := configureAudioStream(s.reader); err != nil {
    107 		return nil, fmt.Errorf("configuring audio stream: %w", err)
    108 	}
    109 
    110 	s.format, err = getSourceReaderFormat(s.reader)
    111 	if err != nil {
    112 		return nil, fmt.Errorf("getting format: %w", err)
    113 	}
    114 
    115 	s.samples = NewSourceReader(s.reader, s.cb)
    116 
    117 	return s, nil
    118 }
    119 
    120 // Format describes the PCM this stream produces. It is known as soon as
    121 // the stream is opened.
    122 func (s *Stream) Format() Format {
    123 	return s.format
    124 }
    125 
    126 // SetDeadline bounds how long the stream may go on decoding. Reads fail
    127 // once it passes, including a read already waiting on the decoder. A
    128 // zero time clears it.
    129 //
    130 // Without this the only bound is the per-read backstop, which a file
    131 // that decodes slowly but steadily never trips.
    132 func (s *Stream) SetDeadline(t time.Time) {
    133 	if s.samples != nil {
    134 		s.samples.deadline = t
    135 	}
    136 }
    137 
    138 // Read fills p with decoded PCM, returning [io.EOF] once the source is
    139 // exhausted.
    140 func (s *Stream) Read(p []byte) (int, error) {
    141 	if s.closed {
    142 		return 0, fmt.Errorf("read on closed stream")
    143 	}
    144 	n, err := s.samples.Read(p)
    145 	runtime.KeepAlive(s.compressed)
    146 	return n, err
    147 }
    148 
    149 // Close releases the Media Foundation objects backing the stream. It is
    150 // idempotent.
    151 //
    152 // Releasing a source reader waits for Media Foundation to finish what it
    153 // is doing. After a clean end of stream that is immediate, and after the
    154 // caller simply stops reading early it is still prompt.
    155 //
    156 // It is not prompt when the decoder stopped answering, which malformed
    157 // audio can cause: the release blocks with no way to cancel it. Flushing
    158 // the reader and closing the byte stream underneath it were both measured
    159 // to make no difference, and one blocked release was watched for ten
    160 // minutes without returning, so treat it as permanent.
    161 //
    162 // Such a reader is leaked rather than released on a goroutine. A release
    163 // that never returns never frees anything either, so waiting on it leaks
    164 // the same objects and adds a thread: waiting is a superset of not
    165 // waiting, which is the whole argument. The thread itself costs no CPU,
    166 // since one parked in a blocking system call is never scheduled, and it
    167 // does not slow the scheduler, which tracks processors rather than
    168 // threads. What it does is count against the runtime limit of 10000
    169 // operating system threads, and crossing that is a fatal thread
    170 // exhaustion rather than a slowdown. At roughly one thread per bad file
    171 // that ceiling is reachable by a program decoding untrusted input.
    172 //
    173 // Everything else is released either way, and the reader keeps its own
    174 // references to what it still needs.
    175 func (s *Stream) Close() error {
    176 	if s.closed {
    177 		return nil
    178 	}
    179 	s.closed = true
    180 	if s.samples != nil && s.samples.gaveUp && s.reader != nil {
    181 		s.reader = nil
    182 	}
    183 	s.release()
    184 	return nil
    185 }
    186 
    187 // release drops every object the stream holds, in reverse order of
    188 // acquisition. Safe to call on a partially constructed Stream.
    189 func (s *Stream) release() {
    190 	if s.samples != nil {
    191 		s.samples.Close()
    192 		s.samples = nil
    193 	}
    194 	if s.reader != nil {
    195 		s.reader.Release()
    196 		s.reader = nil
    197 	}
    198 	if s.attributes != nil {
    199 		s.attributes.Release()
    200 		s.attributes = nil
    201 	}
    202 	if s.byteStream != nil {
    203 		s.byteStream.Release()
    204 		s.byteStream = nil
    205 	}
    206 	if s.istream != nil {
    207 		s.istream.Release()
    208 		s.istream = nil
    209 	}
    210 	// Last: the reader is gone by now, so Media Foundation is finished
    211 	// calling us. Any reference it still holds keeps the object alive.
    212 	if s.cb != nil {
    213 		s.cb.Release()
    214 		s.cb = nil
    215 	}
    216 	runtime.KeepAlive(s.compressed)
    217 	s.compressed = nil
    218 }
    219 
    220 // configureAudioStream selects the first audio stream and configures it output PCM.
    221 func configureAudioStream(pReader *IMFSourceReader) error {
    222 	var pPartialType *IMFMediaType
    223 	// Create a partial media type that specifies uncompressed PCM audio.
    224 	if err := MFCreateMediaType(&pPartialType); err != nil {
    225 		return fmt.Errorf("creating media type: %w", err)
    226 	}
    227 	defer pPartialType.Release()
    228 
    229 	if err := pPartialType.SetGUID(&MF_MT_MAJOR_TYPE, &MFMediaType_Audio); err != nil {
    230 		return fmt.Errorf("setting major type: %w", err)
    231 	}
    232 
    233 	if err := pPartialType.SetGUID(&MF_MT_SUBTYPE, &MFAudioFormat_PCM); err != nil {
    234 		return fmt.Errorf("setting sub type: %w", err)
    235 	}
    236 
    237 	// Pin the sample size. Asking only for PCM lets the reader hand back
    238 	// whatever width the source happens to use, so a 24-bit file decodes
    239 	// to 24-bit and breaks the s16le output this package promises.
    240 	if err := pPartialType.SetUINT32(&MF_MT_AUDIO_BITS_PER_SAMPLE, 16); err != nil {
    241 		return fmt.Errorf("setting bits per sample: %w", err)
    242 	}
    243 
    244 	// Select the first audio stream, and deselect all other streams.
    245 	if err := pReader.SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false); err != nil {
    246 		return fmt.Errorf("deselecting audio streams: %w", err)
    247 	}
    248 	if err := pReader.SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); err != nil {
    249 		return fmt.Errorf("selecting first audio stream: %w", err)
    250 	}
    251 
    252 	// Set this type on the source reader. The source reader will load the necessary decoder.
    253 	if err := pReader.SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, pPartialType); err != nil {
    254 		return fmt.Errorf("setting media type on source reader: %w", err)
    255 	}
    256 
    257 	return nil
    258 }
    259 
    260 // getSourceReaderFormat returns the audio format configured for the source reader.
    261 func getSourceReaderFormat(sr *IMFSourceReader) (f Format, _ error) {
    262 	var mfMediaType *IMFMediaType
    263 	// Get the complete uncompressed format.
    264 	if err := sr.GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &mfMediaType); err != nil {
    265 		return f, fmt.Errorf("getting the current media type: %w", err)
    266 	}
    267 	defer mfMediaType.Release()
    268 
    269 	return getFormat(mfMediaType)
    270 }
    271 
    272 // getFormat extracts the audio format from a media type.
    273 func getFormat(mt *IMFMediaType) (f Format, _ error) {
    274 	var (
    275 		numChannels   uint32
    276 		sampleRate    uint32
    277 		bitsPerSample uint32
    278 	)
    279 
    280 	if err := mt.GetUINT32(&MF_MT_AUDIO_NUM_CHANNELS, &numChannels); err != nil {
    281 		return f, fmt.Errorf("getting numChannels for the media type: %w", err)
    282 	}
    283 	if err := mt.GetUINT32(&MF_MT_AUDIO_SAMPLES_PER_SECOND, &sampleRate); err != nil {
    284 		return f, fmt.Errorf("getting sampleRate for the media type: %w", err)
    285 	}
    286 	if err := mt.GetUINT32(&MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample); err != nil {
    287 		return f, fmt.Errorf("getting bitsPerSample for the media type: %w", err)
    288 	}
    289 
    290 	f = Format{
    291 		SampleRate:     int(sampleRate),
    292 		Channels:       int(numChannels),
    293 		BytesPerSample: int(bitsPerSample / 8),
    294 	}
    295 
    296 	return f, nil
    297 }
    298 
    299 // SourceReader wraps an IMFSourceReader and implements [io.Reader].
    300 type SourceReader struct {
    301 	source *IMFSourceReader
    302 
    303 	sample  *IMFSample      // sample object containing one or more streams
    304 	buffer  *IMFMediaBuffer // buffer object containing the raw buffer
    305 	chunk   *byte           // start of chunk of audio data
    306 	chunkSz uint32          // size of chunk
    307 
    308 	prevTimestamp int64 // timestamp of the last emitted sample.
    309 	hasPrev       bool  // whether any sample has been emitted yet.
    310 
    311 	cb *callback // receives asynchronous reads.
    312 
    313 	// gaveUp records that a read was abandoned because the decoder
    314 	// stopped answering, as opposed to the caller simply stopping early.
    315 	gaveUp bool
    316 
    317 	// deadline bounds the whole decode, not just one read. Zero means
    318 	// only the per-read backstop applies.
    319 	deadline time.Time
    320 
    321 	data []byte // Go view of the audio data, backed by native memory.
    322 }
    323 
    324 // NewSourceReader allocates a [SourceReader] that pulls samples from r,
    325 // which must have been created with cb as its asynchronous callback.
    326 // The caller is responsible for releasing both.
    327 func NewSourceReader(r *IMFSourceReader, cb *callback) *SourceReader {
    328 	return &SourceReader{source: r, cb: cb}
    329 }
    330 
    331 func (s *SourceReader) Read(p []byte) (int, error) {
    332 	if len(p) == 0 {
    333 		return 0, nil
    334 	}
    335 
    336 	// Write residual data into p before requesting more.
    337 	if len(s.data) > 0 {
    338 		return s.copyInto(p), nil
    339 	}
    340 
    341 	ok, err := s.next()
    342 	if err != nil {
    343 		return 0, err
    344 	}
    345 	if !ok {
    346 		return 0, io.EOF
    347 	}
    348 
    349 	return s.copyInto(p), nil
    350 }
    351 
    352 // next reads the next audio sample into s, returning false at end of
    353 // stream. Any error from the source reader ends the stream.
    354 // maxEmptyReads bounds how many times next will ask for a sample and
    355 // be given nothing usable.
    356 //
    357 // ReadSample can legitimately return success with no sample and no
    358 // terminal flag while a decoder primes, and it can repeat a timestamp,
    359 // so a few unproductive reads are normal. An unbounded number is a
    360 // hang: fuzzing found malformed streams that spin here forever,
    361 // burning a core and never returning. The limit sits far above what any
    362 // healthy stream needs.
    363 const maxEmptyReads = 1024
    364 
    365 func (s *SourceReader) next() (bool, error) {
    366 	empty := 0
    367 	for {
    368 		if empty > maxEmptyReads {
    369 			return false, fmt.Errorf("reading sample: gave up after %d reads without a usable sample", empty)
    370 		}
    371 		if err := s.source.ReadSampleAsync(MF_SOURCE_READER_FIRST_AUDIO_STREAM); err != nil {
    372 			return false, fmt.Errorf("requesting sample: %w", err)
    373 		}
    374 
    375 		// Wait no longer than whichever of the caller's deadline and the
    376 		// backstop comes first.
    377 		wait := readTimeout
    378 		if !s.deadline.IsZero() {
    379 			remaining := time.Until(s.deadline)
    380 			if remaining <= 0 {
    381 				s.gaveUp = true
    382 				return false, fmt.Errorf("reading sample: %w", errReadTimeout)
    383 			}
    384 			if remaining < wait {
    385 				wait = remaining
    386 			}
    387 		}
    388 
    389 		res, err := s.cb.wait(wait)
    390 		if err != nil {
    391 			s.gaveUp = true
    392 			return false, fmt.Errorf("reading sample: %w", err)
    393 		}
    394 		flags, sample := res.flags, res.sample
    395 
    396 		// A sample can accompany a status or flag we treat as terminal,
    397 		// so release it before deciding whether to stop.
    398 		if failed(res.status) || flags&(MF_SOURCE_READERF_ERROR|MF_SOURCE_READERF_ENDOFSTREAM|MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) != 0 {
    399 			sample.Release()
    400 		}
    401 		if failed(res.status) {
    402 			return false, fmt.Errorf("reading sample: %w", MFErr{Code: res.status})
    403 		}
    404 		if flags&MF_SOURCE_READERF_ERROR != 0 {
    405 			return false, fmt.Errorf("reading sample: source reader reported an error")
    406 		}
    407 		if flags&MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED != 0 {
    408 			return false, fmt.Errorf("reading sample: media type changed mid-stream")
    409 		}
    410 		if flags&MF_SOURCE_READERF_ENDOFSTREAM != 0 {
    411 			return false, nil
    412 		}
    413 
    414 		// The reader can legitimately return no sample and no terminal
    415 		// flag (for example, while a decoder is buffering). Ask again,
    416 		// up to the bound above.
    417 		if sample == nil {
    418 			empty++
    419 			continue
    420 		}
    421 
    422 		var timestamp int64
    423 		if err := sample.GetSampleTime(&timestamp); err != nil {
    424 			sample.Release()
    425 			return false, fmt.Errorf("reading sample timestamp: %w", err)
    426 		}
    427 
    428 		// Skip samples that repeat the previous timestamp. The reader can
    429 		// produce more than one sample at time 0; emitting all of them
    430 		// produces larger output and audible artifacts.
    431 		if s.hasPrev && timestamp == s.prevTimestamp {
    432 			sample.Release()
    433 			empty++
    434 			continue
    435 		}
    436 
    437 		s.sample = sample
    438 		s.prevTimestamp = timestamp
    439 		s.hasPrev = true
    440 		break
    441 	}
    442 
    443 	if err := s.sample.ConvertToContiguousBuffer(&s.buffer); err != nil {
    444 		s.sample.Release()
    445 		s.sample = nil
    446 		return false, fmt.Errorf("converting sample to contiguous buffer: %w", err)
    447 	}
    448 	if err := s.buffer.Lock(&s.chunk, nil, &s.chunkSz); err != nil {
    449 		s.buffer.Release()
    450 		s.sample.Release()
    451 		s.buffer, s.sample = nil, nil
    452 		return false, fmt.Errorf("locking sample buffer: %w", err)
    453 	}
    454 
    455 	// Make a Go slice view of the data for easy consumption.
    456 	s.data = unsafe.Slice(s.chunk, s.chunkSz)
    457 
    458 	return true, nil
    459 }
    460 
    461 // copyInto copies audio data into p, unlocking the memory once fully copied.
    462 func (s *SourceReader) copyInto(p []byte) int {
    463 	n := copy(p, s.data)
    464 
    465 	s.data = s.data[n:]
    466 
    467 	if len(s.data) == 0 {
    468 		s.buffer.Unlock()
    469 		s.buffer.Release()
    470 		s.sample.Release()
    471 		s.buffer, s.sample = nil, nil
    472 		s.data = nil
    473 	}
    474 
    475 	return n
    476 }
    477 
    478 // Close releases any sample the reader is still holding. A stream that
    479 // is abandoned before EOF leaves a locked buffer and a live sample
    480 // behind, so this is not merely tidiness.
    481 func (s *SourceReader) Close() error {
    482 	if s.buffer != nil {
    483 		s.buffer.Unlock()
    484 		s.buffer.Release()
    485 		s.buffer = nil
    486 	}
    487 	if s.sample != nil {
    488 		s.sample.Release()
    489 		s.sample = nil
    490 	}
    491 	s.data = nil
    492 	s.chunk = nil
    493 	return nil
    494 }
    495 
    496 /*
    497 	The following contains the minimal set of definitions we need to decode
    498 	audio using Media Framework.
    499 
    500 	Definitions are derived from appropriate SDK headers.
    501 */
    502 
    503 type GUID struct {
    504 	Data1 uint32
    505 	Data2 uint16
    506 	Data3 uint16
    507 	Data4 [8]uint8
    508 }
    509 
    510 type HRESULT = uintptr
    511 
    512 const (
    513 	S_OK                                      = 0x0
    514 	MFSTARTUP_LITE                            = 0x1
    515 	MF_VERSION                                = 0x20070
    516 	MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED = 0x20
    517 	MF_SOURCE_READERF_ENDOFSTREAM             = 0x2
    518 	MF_SOURCE_READERF_ERROR                   = 0x1
    519 	MF_SOURCE_READER_ALL_STREAMS              = 0xfffffffe
    520 	MF_SOURCE_READER_FIRST_AUDIO_STREAM       = 0xfffffffd
    521 )
    522 
    523 var (
    524 	MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS = GUID{0xa634a91c, 0x822b, 0x41b9, [8]uint8{0xa4, 0x94, 0x4d, 0xe4, 0x64, 0x36, 0x12, 0xb0}}
    525 
    526 	MF_MT_AUDIO_NUM_CHANNELS             = GUID{0x37e48bf5, 0x645e, 0x4c5b, [8]uint8{0x89, 0xde, 0xad, 0xa9, 0xe2, 0x9b, 0x69, 0x6a}}
    527 	MF_MT_AUDIO_SAMPLES_PER_SECOND       = GUID{0x5faeeae7, 0x0290, 0x4c31, [8]uint8{0x9e, 0x8a, 0xc5, 0x34, 0xf6, 0x8d, 0x9d, 0xba}}
    528 	MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND = GUID{0xfb3b724a, 0xcfb5, 0x4319, [8]uint8{0xae, 0xfe, 0x6e, 0x42, 0xb2, 0x40, 0x61, 0x32}}
    529 	MF_MT_AUDIO_AVG_BYTES_PER_SECOND     = GUID{0x1aab75c8, 0xcfef, 0x451c, [8]uint8{0xab, 0x95, 0xac, 0x03, 0x4b, 0x8e, 0x17, 0x31}}
    530 	MF_MT_AUDIO_BLOCK_ALIGNMENT          = GUID{0x322de230, 0x9eeb, 0x43bd, [8]uint8{0xab, 0x7a, 0xff, 0x41, 0x22, 0x51, 0x54, 0x1d}}
    531 	MF_MT_AUDIO_BITS_PER_SAMPLE          = GUID{0xf2deb57f, 0x40fa, 0x4764, [8]uint8{0xaa, 0x33, 0xed, 0x4f, 0x2d, 0x1f, 0xf6, 0x69}}
    532 	MF_MT_AUDIO_VALID_BITS_PER_SAMPLE    = GUID{0xd9bf8d6a, 0x9530, 0x4b7c, [8]uint8{0x9d, 0xdf, 0xff, 0x6f, 0xd5, 0x8b, 0xbd, 0x06}}
    533 	MF_MT_AUDIO_SAMPLES_PER_BLOCK        = GUID{0xaab15aac, 0xe13a, 0x4995, [8]uint8{0x92, 0x22, 0x50, 0x1e, 0xa1, 0x5c, 0x68, 0x77}}
    534 	MF_MT_AUDIO_CHANNEL_MASK             = GUID{0x55fb5765, 0x644a, 0x4caf, [8]uint8{0x84, 0x79, 0x93, 0x89, 0x83, 0xbb, 0x15, 0x88}}
    535 
    536 	MF_MT_MAJOR_TYPE = GUID{0x48eba18e, 0xf8c9, 0x4687, [8]uint8{0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f}}
    537 	MF_MT_SUBTYPE    = GUID{0xf7e34c9a, 0x42e8, 0x4714, [8]uint8{0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5}}
    538 
    539 	MFMediaType_Audio = GUID{0x73647561, 0x0000, 0x0010, [8]uint8{0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}}
    540 	MFAudioFormat_PCM = GUID{0x00000001, 0x0000, 0x0010, [8]uint8{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}
    541 )
    542 
    543 type IMFByteStream struct {
    544 	VTable *IMFByteStreamVTable
    545 }
    546 
    547 type IMFByteStreamVTable struct {
    548 	QueryInterface     uintptr
    549 	AddRef             uintptr
    550 	Release            uintptr
    551 	GetCapabilities    uintptr
    552 	GetLength          uintptr
    553 	SetLength          uintptr
    554 	GetCurrentPosition uintptr
    555 	SetCurrentPosition uintptr
    556 	IsEndOfStream      uintptr
    557 	Read               uintptr
    558 	BeginRead          uintptr
    559 	EndRead            uintptr
    560 	Write              uintptr
    561 	BeginWrite         uintptr
    562 	EndWrite           uintptr
    563 	Seek               uintptr
    564 	Flush              uintptr
    565 	Close              uintptr
    566 }
    567 
    568 func (v *IMFByteStream) Release() error {
    569 	if v == nil {
    570 		return nil
    571 	}
    572 	r, _, _ := syscall.SyscallN(
    573 		v.VTable.Release,
    574 		uintptr(unsafe.Pointer(v)),
    575 	)
    576 	if r != S_OK {
    577 		return MFErr{Code: r}
    578 	}
    579 	return nil
    580 }
    581 
    582 // Close closes the byte stream, failing any I/O the media source has in
    583 // flight against it.
    584 func (v *IMFByteStream) Close() error {
    585 	if v == nil {
    586 		return nil
    587 	}
    588 	r, _, _ := syscall.SyscallN(
    589 		v.VTable.Close,
    590 		uintptr(unsafe.Pointer(v)),
    591 	)
    592 	if r != S_OK {
    593 		return MFErr{Code: r}
    594 	}
    595 	return nil
    596 }
    597 
    598 type IStream struct {
    599 	VTable *IStreamVTable
    600 }
    601 
    602 type IStreamVTable struct {
    603 	QueryInterface uintptr
    604 	AddRef         uintptr
    605 	Release        uintptr
    606 	Read           uintptr
    607 	Write          uintptr
    608 	Seek           uintptr
    609 	SetSize        uintptr
    610 	CopyTo         uintptr
    611 	Commit         uintptr
    612 	Revert         uintptr
    613 	LockRegion     uintptr
    614 	UnlockRegion   uintptr
    615 	Stat           uintptr
    616 	Clone          uintptr
    617 }
    618 
    619 func (v *IStream) Release() error {
    620 	if v == nil {
    621 		return nil
    622 	}
    623 	r, _, _ := syscall.SyscallN(
    624 		v.VTable.Release,
    625 		uintptr(unsafe.Pointer(v)),
    626 	)
    627 	if r != S_OK {
    628 		return MFErr{Code: r}
    629 	}
    630 	return nil
    631 }
    632 
    633 type IMFAttributes struct {
    634 	VTable *IMFAttributesVTable
    635 }
    636 
    637 type IMFAttributesVTable struct {
    638 	QueryInterface     uintptr
    639 	AddRef             uintptr
    640 	Release            uintptr
    641 	GetItem            uintptr
    642 	GetItemType        uintptr
    643 	CompareItem        uintptr
    644 	Compare            uintptr
    645 	GetUINT32          uintptr
    646 	GetUINT64          uintptr
    647 	GetDouble          uintptr
    648 	GetGUID            uintptr
    649 	GetStringLength    uintptr
    650 	GetString          uintptr
    651 	GetAllocatedString uintptr
    652 	GetBlobSize        uintptr
    653 	GetBlob            uintptr
    654 	GetAllocatedBlob   uintptr
    655 	GetUnknown         uintptr
    656 	SetItem            uintptr
    657 	DeleteItem         uintptr
    658 	DeleteAllItems     uintptr
    659 	SetUINT32          uintptr
    660 	SetUINT64          uintptr
    661 	SetDouble          uintptr
    662 	SetGUID            uintptr
    663 	SetString          uintptr
    664 	SetBlob            uintptr
    665 	SetUnknown         uintptr
    666 	LockStore          uintptr
    667 	UnlockStore        uintptr
    668 	GetCount           uintptr
    669 	GetItemByIndex     uintptr
    670 	CopyAllItems       uintptr
    671 }
    672 
    673 func (v *IMFAttributes) Release() error {
    674 	if v == nil {
    675 		return nil
    676 	}
    677 	r, _, _ := syscall.SyscallN(
    678 		v.VTable.Release,
    679 		uintptr(unsafe.Pointer(v)),
    680 	)
    681 	if r != S_OK {
    682 		return MFErr{Code: r}
    683 	}
    684 	return nil
    685 }
    686 
    687 func (v *IMFAttributes) SetUINT32(guid *GUID, unValue uint32) error {
    688 	r, _, _ := syscall.SyscallN(
    689 		v.VTable.SetUINT32,
    690 		uintptr(unsafe.Pointer(v)),
    691 		uintptr(unsafe.Pointer(guid)),
    692 		uintptr(unValue),
    693 	)
    694 	if r != S_OK {
    695 		return MFErr{Code: r}
    696 	}
    697 	return nil
    698 }
    699 
    700 // SetUnknown stores a COM interface pointer under guid, retaining it.
    701 func (v *IMFAttributes) SetUnknown(guid *GUID, unknown unsafe.Pointer) error {
    702 	r, _, _ := syscall.SyscallN(
    703 		v.VTable.SetUnknown,
    704 		uintptr(unsafe.Pointer(v)),
    705 		uintptr(unsafe.Pointer(guid)),
    706 		uintptr(unknown),
    707 	)
    708 	if r != S_OK {
    709 		return MFErr{Code: r}
    710 	}
    711 	return nil
    712 }
    713 
    714 type IMFMediaType struct {
    715 	VTable *IMFMediaTypeVTable
    716 }
    717 
    718 type IMFMediaTypeVTable struct {
    719 	QueryInterface     uintptr
    720 	AddRef             uintptr
    721 	Release            uintptr
    722 	GetItem            uintptr
    723 	GetItemType        uintptr
    724 	CompareItem        uintptr
    725 	Compare            uintptr
    726 	GetUINT32          uintptr
    727 	GetUINT64          uintptr
    728 	GetDouble          uintptr
    729 	GetGUID            uintptr
    730 	GetStringLength    uintptr
    731 	GetString          uintptr
    732 	GetAllocatedString uintptr
    733 	GetBlobSize        uintptr
    734 	GetBlob            uintptr
    735 	GetAllocatedBlob   uintptr
    736 	GetUnknown         uintptr
    737 	SetItem            uintptr
    738 	DeleteItem         uintptr
    739 	DeleteAllItems     uintptr
    740 	SetUINT32          uintptr
    741 	SetUINT64          uintptr
    742 	SetDouble          uintptr
    743 	SetGUID            uintptr
    744 	SetString          uintptr
    745 	SetBlob            uintptr
    746 	SetUnknown         uintptr
    747 	LockStore          uintptr
    748 	UnlockStore        uintptr
    749 	GetCount           uintptr
    750 	GetItemByIndex     uintptr
    751 	CopyAllItems       uintptr
    752 	GetMajorType       uintptr
    753 	IsCompressedFormat uintptr
    754 	IsEqual            uintptr
    755 	GetRepresentation  uintptr
    756 	FreeRepresentation uintptr
    757 }
    758 
    759 func (v *IMFMediaType) Release() error {
    760 	if v == nil {
    761 		return nil
    762 	}
    763 	r, _, _ := syscall.SyscallN(
    764 		v.VTable.Release,
    765 		uintptr(unsafe.Pointer(v)),
    766 	)
    767 	if r != S_OK {
    768 		return MFErr{Code: r}
    769 	}
    770 	return nil
    771 }
    772 
    773 func (v *IMFMediaType) SetGUID(guid *GUID, value *GUID) error {
    774 	r, _, _ := syscall.SyscallN(
    775 		v.VTable.SetGUID,
    776 		uintptr(unsafe.Pointer(v)),
    777 		uintptr(unsafe.Pointer(guid)),
    778 		uintptr(unsafe.Pointer(value)),
    779 	)
    780 	if r != S_OK {
    781 		return MFErr{Code: r}
    782 	}
    783 	return nil
    784 }
    785 
    786 // SetUINT32 stores an unsigned 32-bit attribute on the media type.
    787 func (v *IMFMediaType) SetUINT32(guid *GUID, value uint32) error {
    788 	r, _, _ := syscall.SyscallN(
    789 		v.VTable.SetUINT32,
    790 		uintptr(unsafe.Pointer(v)),
    791 		uintptr(unsafe.Pointer(guid)),
    792 		uintptr(value),
    793 	)
    794 	if r != S_OK {
    795 		return MFErr{Code: r}
    796 	}
    797 	return nil
    798 }
    799 
    800 func (v *IMFMediaType) GetUINT32(guid *GUID, punValue *uint32) error {
    801 	r, _, _ := syscall.SyscallN(
    802 		v.VTable.GetUINT32,
    803 		uintptr(unsafe.Pointer(v)),
    804 		uintptr(unsafe.Pointer(guid)),
    805 		uintptr(unsafe.Pointer(punValue)),
    806 	)
    807 	if r != S_OK {
    808 		return MFErr{Code: r}
    809 	}
    810 	return nil
    811 }
    812 
    813 type IMFSourceReader struct {
    814 	VTable *IMFSourceReaderVtbl
    815 }
    816 
    817 type IMFSourceReaderVtbl struct {
    818 	QueryInterface           uintptr
    819 	AddRef                   uintptr
    820 	Release                  uintptr
    821 	GetStreamSelection       uintptr
    822 	SetStreamSelection       uintptr
    823 	GetNativeMediaType       uintptr
    824 	GetCurrentMediaType      uintptr
    825 	SetCurrentMediaType      uintptr
    826 	SetCurrentPosition       uintptr
    827 	ReadSample               uintptr
    828 	Flush                    uintptr
    829 	GetServiceForStream      uintptr
    830 	GetPresentationAttribute uintptr
    831 }
    832 
    833 func (v *IMFSourceReader) Release() error {
    834 	if v == nil {
    835 		return nil
    836 	}
    837 	r, _, _ := syscall.SyscallN(
    838 		v.VTable.Release,
    839 		uintptr(unsafe.Pointer(v)),
    840 	)
    841 	if r != S_OK {
    842 		return MFErr{Code: r}
    843 	}
    844 	return nil
    845 }
    846 
    847 func (v *IMFSourceReader) SetStreamSelection(index uint32, selected bool) error {
    848 	r, _, _ := syscall.SyscallN(
    849 		v.VTable.SetStreamSelection,
    850 		uintptr(unsafe.Pointer(v)),
    851 		uintptr(index),
    852 		uintptr(boolToInt(selected)),
    853 	)
    854 	if r != S_OK {
    855 		return MFErr{Code: r}
    856 	}
    857 	return nil
    858 }
    859 
    860 func (v *IMFSourceReader) SetCurrentMediaType(index uint32, mt *IMFMediaType) error {
    861 	r, _, _ := syscall.SyscallN(
    862 		v.VTable.SetCurrentMediaType,
    863 		uintptr(unsafe.Pointer(v)),
    864 		uintptr(index),
    865 		uintptr(0),
    866 		uintptr(unsafe.Pointer(mt)),
    867 	)
    868 	if r != S_OK {
    869 		return MFErr{Code: r}
    870 	}
    871 	return nil
    872 }
    873 
    874 func (v *IMFSourceReader) GetCurrentMediaType(index uint32, mt **IMFMediaType) error {
    875 	r, _, _ := syscall.SyscallN(
    876 		v.VTable.GetCurrentMediaType,
    877 		uintptr(unsafe.Pointer(v)),
    878 		uintptr(index),
    879 		uintptr(unsafe.Pointer(mt)),
    880 	)
    881 	if r != S_OK {
    882 		return MFErr{Code: r}
    883 	}
    884 	return nil
    885 }
    886 
    887 func (v *IMFSourceReader) ReadSample(index, controlFlags uint32, actualIndex *uint32, streamFlags *uint32, timestamp *int64, sample **IMFSample) error {
    888 	r, _, _ := syscall.SyscallN(
    889 		v.VTable.ReadSample,
    890 		uintptr(unsafe.Pointer(v)),
    891 		uintptr(index),
    892 		uintptr(controlFlags),
    893 		uintptr(unsafe.Pointer(actualIndex)),
    894 		uintptr(unsafe.Pointer(streamFlags)),
    895 		uintptr(unsafe.Pointer(timestamp)),
    896 		uintptr(unsafe.Pointer(sample)),
    897 	)
    898 	if r != S_OK {
    899 		return MFErr{Code: r}
    900 	}
    901 	return nil
    902 }
    903 
    904 // ReadSampleAsync requests the next sample without waiting for it.
    905 //
    906 // A reader configured with a callback rejects the synchronous form, and
    907 // every out-parameter must be nil: the result is delivered to
    908 // IMFSourceReaderCallback::OnReadSample instead. Only one read may be
    909 // outstanding at a time.
    910 func (v *IMFSourceReader) ReadSampleAsync(index uint32) error {
    911 	r, _, _ := syscall.SyscallN(
    912 		v.VTable.ReadSample,
    913 		uintptr(unsafe.Pointer(v)),
    914 		uintptr(index),
    915 		0,
    916 		0,
    917 		0,
    918 		0,
    919 		0,
    920 	)
    921 	if r != S_OK {
    922 		return MFErr{Code: r}
    923 	}
    924 	return nil
    925 }
    926 
    927 // Flush discards queued samples and cancels pending reads on a stream.
    928 // With a callback attached it completes asynchronously, through OnFlush.
    929 func (v *IMFSourceReader) Flush(index uint32) error {
    930 	r, _, _ := syscall.SyscallN(
    931 		v.VTable.Flush,
    932 		uintptr(unsafe.Pointer(v)),
    933 		uintptr(index),
    934 	)
    935 	if r != S_OK {
    936 		return MFErr{Code: r}
    937 	}
    938 	return nil
    939 }
    940 
    941 type IMFSample struct {
    942 	VTable *IMFSampleVTable
    943 }
    944 
    945 type IMFSampleVTable struct {
    946 	QueryInterface            uintptr
    947 	AddRef                    uintptr
    948 	Release                   uintptr
    949 	GetItem                   uintptr
    950 	GetItemType               uintptr
    951 	CompareItem               uintptr
    952 	Compare                   uintptr
    953 	GetUINT32                 uintptr
    954 	GetUINT64                 uintptr
    955 	GetDouble                 uintptr
    956 	GetGUID                   uintptr
    957 	GetStringLength           uintptr
    958 	GetString                 uintptr
    959 	GetAllocatedString        uintptr
    960 	GetBlobSize               uintptr
    961 	GetBlob                   uintptr
    962 	GetAllocatedBlob          uintptr
    963 	GetUnknown                uintptr
    964 	SetItem                   uintptr
    965 	DeleteItem                uintptr
    966 	DeleteAllItems            uintptr
    967 	SetUINT32                 uintptr
    968 	SetUINT64                 uintptr
    969 	SetDouble                 uintptr
    970 	SetGUID                   uintptr
    971 	SetString                 uintptr
    972 	SetBlob                   uintptr
    973 	SetUnknown                uintptr
    974 	LockStore                 uintptr
    975 	UnlockStore               uintptr
    976 	GetCount                  uintptr
    977 	GetItemByIndex            uintptr
    978 	CopyAllItems              uintptr
    979 	GetSampleFlags            uintptr
    980 	SetSampleFlags            uintptr
    981 	GetSampleTime             uintptr
    982 	SetSampleTime             uintptr
    983 	GetSampleDuration         uintptr
    984 	SetSampleDuration         uintptr
    985 	GetBufferCount            uintptr
    986 	GetBufferByIndex          uintptr
    987 	ConvertToContiguousBuffer uintptr
    988 	AddBuffer                 uintptr
    989 	RemoveBufferByIndex       uintptr
    990 	RemoveAllBuffers          uintptr
    991 	GetTotalLength            uintptr
    992 	CopyToBuffer              uintptr
    993 }
    994 
    995 func (v *IMFSample) Release() error {
    996 	if v == nil {
    997 		return nil
    998 	}
    999 	r, _, _ := syscall.SyscallN(
   1000 		v.VTable.Release,
   1001 		uintptr(unsafe.Pointer(v)),
   1002 	)
   1003 	if r != S_OK {
   1004 		return MFErr{Code: r}
   1005 	}
   1006 	return nil
   1007 }
   1008 
   1009 // AddRef implements IUnknown::AddRef, retaining a sample past the
   1010 // callback that delivered it.
   1011 func (v *IMFSample) AddRef() uint32 {
   1012 	if v == nil {
   1013 		return 0
   1014 	}
   1015 	r, _, _ := syscall.SyscallN(
   1016 		v.VTable.AddRef,
   1017 		uintptr(unsafe.Pointer(v)),
   1018 	)
   1019 	return uint32(r)
   1020 }
   1021 
   1022 // GetSampleTime returns the presentation time of the sample.
   1023 //
   1024 // Asynchronous delivery also carries a timestamp, but reading it back
   1025 // from the sample keeps the callback signature free of a 64-bit argument
   1026 // whose slot count varies by word size.
   1027 func (v *IMFSample) GetSampleTime(t *int64) error {
   1028 	r, _, _ := syscall.SyscallN(
   1029 		v.VTable.GetSampleTime,
   1030 		uintptr(unsafe.Pointer(v)),
   1031 		uintptr(unsafe.Pointer(t)),
   1032 	)
   1033 	if r != S_OK {
   1034 		return MFErr{Code: r}
   1035 	}
   1036 	return nil
   1037 }
   1038 
   1039 func (v *IMFSample) ConvertToContiguousBuffer(b **IMFMediaBuffer) error {
   1040 	r, _, _ := syscall.SyscallN(
   1041 		v.VTable.ConvertToContiguousBuffer,
   1042 		uintptr(unsafe.Pointer(v)),
   1043 		uintptr(unsafe.Pointer(b)),
   1044 	)
   1045 	if r != S_OK {
   1046 		return MFErr{Code: r}
   1047 	}
   1048 	return nil
   1049 }
   1050 
   1051 type IMFMediaBuffer struct {
   1052 	VTable *IMFMediaBufferVTable
   1053 }
   1054 
   1055 type IMFMediaBufferVTable struct {
   1056 	QueryInterface   uintptr
   1057 	AddRef           uintptr
   1058 	Release          uintptr
   1059 	Lock             uintptr
   1060 	Unlock           uintptr
   1061 	GetCurrentLength uintptr
   1062 	SetCurrentLength uintptr
   1063 	GetMaxLength     uintptr
   1064 }
   1065 
   1066 func (v *IMFMediaBuffer) Release() error {
   1067 	if v == nil {
   1068 		return nil
   1069 	}
   1070 	r, _, _ := syscall.SyscallN(
   1071 		v.VTable.Release,
   1072 		uintptr(unsafe.Pointer(v)),
   1073 	)
   1074 	if r != S_OK {
   1075 		return MFErr{Code: r}
   1076 	}
   1077 	return nil
   1078 }
   1079 
   1080 func (v *IMFMediaBuffer) Lock(buf **byte, maxLength, currentLength *uint32) error {
   1081 	r, _, _ := syscall.SyscallN(
   1082 		v.VTable.Lock,
   1083 		uintptr(unsafe.Pointer(v)),
   1084 		uintptr(unsafe.Pointer(buf)),
   1085 		uintptr(unsafe.Pointer(maxLength)),
   1086 		uintptr(unsafe.Pointer(currentLength)),
   1087 	)
   1088 	if r != S_OK {
   1089 		return MFErr{Code: r}
   1090 	}
   1091 	return nil
   1092 }
   1093 
   1094 func (v *IMFMediaBuffer) Unlock() error {
   1095 	r, _, _ := syscall.SyscallN(
   1096 		v.VTable.Unlock,
   1097 		uintptr(unsafe.Pointer(v)),
   1098 	)
   1099 	if r != S_OK {
   1100 		return MFErr{Code: r}
   1101 	}
   1102 	return nil
   1103 }
   1104 
   1105 /*
   1106 	The following defines exactly the functions required.
   1107 */
   1108 
   1109 var (
   1110 	_mfplat      *windows.DLL
   1111 	_shlwapi     *windows.DLL
   1112 	_mfreadwrite *windows.DLL
   1113 
   1114 	_SHCreateMemStream *windows.Proc
   1115 
   1116 	_MFStartup                    *windows.Proc
   1117 	_MFShutdown                   *windows.Proc
   1118 	_MFCreateMediaType            *windows.Proc
   1119 	_MFCreateAttributes           *windows.Proc
   1120 	_MFCreateMFByteStreamOnStream *windows.Proc
   1121 
   1122 	_MFCreateSourceReaderFromByteStream *windows.Proc
   1123 )
   1124 
   1125 var loadOnce sync.Once
   1126 
   1127 // loadProcs resolves the DLLs and entry points once per process. The
   1128 // handles stay valid for the life of the process, so repeated Startup
   1129 // and Shutdown cycles reuse them.
   1130 func loadProcs() (err error) {
   1131 	_mfplat, err = windows.LoadDLL("Mfplat.dll")
   1132 	if err != nil {
   1133 		return fmt.Errorf("Mfplat.dll: %w", err)
   1134 	}
   1135 	_shlwapi, err = windows.LoadDLL("Shlwapi.dll")
   1136 	if err != nil {
   1137 		return fmt.Errorf("Shlwapi.dll: %w", err)
   1138 	}
   1139 	_mfreadwrite, err = windows.LoadDLL("Mfreadwrite.dll")
   1140 	if err != nil {
   1141 		return fmt.Errorf("Mfreadwrite.dll: %w", err)
   1142 	}
   1143 	_SHCreateMemStream, err = _shlwapi.FindProc("SHCreateMemStream")
   1144 	if err != nil {
   1145 		return fmt.Errorf("SHCreateMemStream: %w", err)
   1146 	}
   1147 	_MFStartup, err = _mfplat.FindProc("MFStartup")
   1148 	if err != nil {
   1149 		return fmt.Errorf("MFStartup: %w", err)
   1150 	}
   1151 	_MFShutdown, err = _mfplat.FindProc("MFShutdown")
   1152 	if err != nil {
   1153 		return fmt.Errorf("MFShutdown: %w", err)
   1154 	}
   1155 	_MFCreateMediaType, err = _mfplat.FindProc("MFCreateMediaType")
   1156 	if err != nil {
   1157 		return fmt.Errorf("MFCreateMediaType: %w", err)
   1158 	}
   1159 	_MFCreateAttributes, err = _mfplat.FindProc("MFCreateAttributes")
   1160 	if err != nil {
   1161 		return fmt.Errorf("MFCreateAttributes: %w", err)
   1162 	}
   1163 	_MFCreateMFByteStreamOnStream, err = _mfplat.FindProc("MFCreateMFByteStreamOnStream")
   1164 	if err != nil {
   1165 		return fmt.Errorf("MFCreateMFByteStreamOnStream: %w", err)
   1166 	}
   1167 	_MFCreateSourceReaderFromByteStream, err = _mfreadwrite.FindProc("MFCreateSourceReaderFromByteStream")
   1168 	if err != nil {
   1169 		return fmt.Errorf("MFCreateSourceReaderFromByteStream: %w", err)
   1170 	}
   1171 
   1172 	return nil
   1173 }
   1174 
   1175 // MFStartup initialises Media Foundation. The platform refcounts this
   1176 // against MFShutdown, so callers must pair them.
   1177 func MFStartup(version, flags uintptr) error {
   1178 	var loadErr error
   1179 	loadOnce.Do(func() { loadErr = loadProcs() })
   1180 	if loadErr != nil {
   1181 		return loadErr
   1182 	}
   1183 
   1184 	r, _, _ := _MFStartup.Call(version, flags)
   1185 	if r != S_OK {
   1186 		return MFErr{Code: r}
   1187 	}
   1188 
   1189 	return nil
   1190 }
   1191 
   1192 func MFShutdown() error {
   1193 	r, _, _ := _MFShutdown.Call()
   1194 	if r != S_OK {
   1195 		return MFErr{Code: r}
   1196 	}
   1197 	return nil
   1198 }
   1199 
   1200 func SHCreateMemStream(pInit *byte, cbInit int) *IStream {
   1201 	r, _, _ := _SHCreateMemStream.Call(
   1202 		uintptr(unsafe.Pointer(pInit)),
   1203 		uintptr(uint32(cbInit)),
   1204 	)
   1205 	if r == 0 {
   1206 		return nil
   1207 	}
   1208 	// r is a COM interface pointer owned by the shell, not Go memory, so
   1209 	// the round trip through uintptr is safe. Converting via the address
   1210 	// of r keeps go vet from flagging a possible misuse of unsafe.Pointer.
   1211 	return *(**IStream)(unsafe.Pointer(&r))
   1212 }
   1213 
   1214 func MFCreateAttributes(out **IMFAttributes, size uint64) error {
   1215 	r, _, _ := _MFCreateAttributes.Call(
   1216 		uintptr(unsafe.Pointer(out)),
   1217 		uintptr(size),
   1218 	)
   1219 	if r != S_OK {
   1220 		return MFErr{Code: r}
   1221 	}
   1222 	return nil
   1223 }
   1224 
   1225 func MFCreateMFByteStreamOnStream(s *IStream, out **IMFByteStream) error {
   1226 	r, _, _ := _MFCreateMFByteStreamOnStream.Call(
   1227 		uintptr(unsafe.Pointer(s)),
   1228 		uintptr(unsafe.Pointer(out)),
   1229 	)
   1230 	if r != S_OK {
   1231 		return MFErr{Code: r}
   1232 	}
   1233 	return nil
   1234 }
   1235 
   1236 func MFCreateSourceReaderFromByteStream(bs *IMFByteStream, attributes *IMFAttributes, out **IMFSourceReader) error {
   1237 	r, _, _ := _MFCreateSourceReaderFromByteStream.Call(
   1238 		uintptr(unsafe.Pointer(bs)),
   1239 		uintptr(unsafe.Pointer(attributes)),
   1240 		uintptr(unsafe.Pointer(out)),
   1241 	)
   1242 	if r != S_OK {
   1243 		return MFErr{Code: r}
   1244 	}
   1245 	return nil
   1246 }
   1247 
   1248 func MFCreateMediaType(out **IMFMediaType) error {
   1249 	r, _, _ := _MFCreateMediaType.Call(
   1250 		uintptr(unsafe.Pointer(out)),
   1251 	)
   1252 	if r != S_OK {
   1253 		return MFErr{Code: r}
   1254 	}
   1255 	return nil
   1256 }
   1257 
   1258 func boolToInt(b bool) int {
   1259 	const False = 0
   1260 	const True = 1
   1261 	if b {
   1262 		return True
   1263 	}
   1264 	return False
   1265 }
   1266 
   1267 // MFErr is a Media Foundation error that can render a formatted message.
   1268 type MFErr struct {
   1269 	Code HRESULT
   1270 }
   1271 
   1272 func (e MFErr) Error() string {
   1273 	out := make([]uint16, 300)
   1274 	size, err := windows.FormatMessage(
   1275 		windows.FORMAT_MESSAGE_FROM_SYSTEM|windows.FORMAT_MESSAGE_FROM_HMODULE|windows.FORMAT_MESSAGE_ARGUMENT_ARRAY,
   1276 		uintptr(_mfplat.Handle),
   1277 		uint32(e.Code),
   1278 		0,
   1279 		out,
   1280 		nil,
   1281 	)
   1282 	if err != nil {
   1283 		return fmt.Sprintf("code %x (<format message: %v>)", e.Code, err.Error())
   1284 	}
   1285 	// trim terminating \r and \n
   1286 	for ; size > 0 && (out[size-1] == '\n' || out[size-1] == '\r'); size-- {
   1287 	}
   1288 	return fmt.Sprintf("%s (code %x)", string(utf16.Decode(out[:size])), e.Code)
   1289 }