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(×tamp); 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 }