Jump to content
Renate Schaaf

Recover from call to dll that doesn't return

Recommended Posts

Ever since I work on my new Win 11 system, mediafoundation is acting up. (I use mfpack). Apparently microsoft is aware of some issues and has promised to fix them.

 

But right now the worst thing is that sometimes a call (IMFSourceReader.ReadSample) just doesn't return. The setup of the reader generates no errors, and it's not possible to see what is causing the hang in the dll (mfReadWrite.dll).

 

I've tried to intercept this by moving the call into a task and generating an exception on timeout. This is of course terrible for performance in a call which is used very often, but now at least I get an error message when it occurs. Still, the application hangs and the only way to solve this is to kill the application in taskmanager. 

 

Is there another way to recover from this, or do I just have to tell users "Sorry this file is not supported, go to taskmanager and terminate the application and never use this file again."?

Share this post


Link to post

Is it possible to delay-load the DLL explicitly - and unload / reload it after an exception?

 

Or walk the system app thread list and request termination of hung sub-threads in the DLL space?
(Clumsy description, but I hope you get what I suggest).

Share this post


Link to post
8 minutes ago, Lars Fosdal said:

but I hope you get what I suggest

Thanks, I get the general idea, now I have the research the details 🙂

Share this post


Link to post
1 hour ago, Renate Schaaf said:

Is there another way to recover from this, or do I just have to tell users "Sorry this file is not supported, go to taskmanager and terminate the application and never use this file again."?

I don't think it is possible to recover from this, specially that i witnessed very similar behavior but without freezing part, it was refusing to initialize even there was nothing is loaded, the only thing that helped is restart the OS, and things returned working fine, in my case it happened after so many exceptions and debugging and stopping the debugged exe in the middle of an operation that was invoking WMF, the result in my opinion is the hardware part connecting it with the display part or the hardware decoder went rouge and left with dangling connections, so it refused in my case, in yours it might be behaved differently as it was waiting for some hardware signal that didn't come.

 

So, first you should confirm if this is the case and it caused by many faulty runs (stopped by exception or debugger), aka not clean exit, and an OS restart helped, and on other hand if you wasn't developing and changing stuff that raises exception and stops the operation without clean exit, then it will work as it should many many times.

 

That what comes to mind in this very case, as for recovery, then no that thread is done and gone, and i doubt creating new thread to resume the frozen one work will help, as it most likely will freeze too.

 

also watch carefully hardware acceleration effect and involvement on this case.

Share this post


Link to post
32 minutes ago, Kas Ob. said:

I don't think it is possible to recover from this

It might get fixed, because a file for which ReadSample hangs, sometimes returns an error code on ReadSample, which is

MF_E_NOT_AVAILABLE                    = _HRESULT_TYPEDEF_($C00D36D6)

This seems to happen, if mf thinks that the input is protected, when actually it isn't. And that issue has been reported. 

Also, since you mention hardware acceleration, software encoding now never works. It returns with a sharing violation for a device. No idea what that could be. 

Being at the mercy of a new operating system almost makes me want to throw away the whole project. 

There are decoders which are no longer implemented by default (dolby ac-x), they can be downloaded but are very hard to find...

Share this post


Link to post
1 hour ago, Renate Schaaf said:

because a file for which ReadSample hangs

Well, it is not that simple, as there is few layers between IMFSourceReader.ReadSample and a file on the disk, from the media source which is the main provider for the demuxer, to the codec that will decode passing a transformer for the colors and format (and may be audio too), till returning from ReadSample, so if we are talking about recover from recover hanging thread then no and that is if possible is way harder and will not work long term, if we talking about recover from this case to not happen then it is possible to find a fix, but to fix it you need to pinpoint the cause.

 

1 hour ago, Renate Schaaf said:

Also, since you mention hardware acceleration, software encoding now never works.

This is very interesting, and could be a major lead, if the software is failing then the hardware might cause the hang/freeze, so you need to return to fixing the software, find the cause.

 

1 hour ago, Renate Schaaf said:

Being at the mercy of a new operating system almost makes me want to throw away the whole project. 

There are decoders which are no longer implemented by default (dolby ac-x), they can be downloaded but are very hard to find...

And this remind me of something i didn't see since old ages with Windows XP and its media player, did you by any chance installed some of these codec packs, the free ones that comes with modified versions of some thing like MPC-HC or VLC or k-lite codecs, LAV,MadVR ...

Because there is many codecs included in these packages with broken implementation or shortcut to bypass some patents, all what they do is trying to work, which they do most the time, but sometimes even cause system crash.

 

So i want you to grab the Autoruns from https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns

and see what codec are installed on 32bit and 64bit, if you doubt then delete them !, ( i think Microsoft provided codec are your safest bet for now so leave these installed or lets say active)

with Autoruns wait until finish scanning then click on the codecs tab, from there you can use the two options from Options menu "Hide Microsoft entries" and "Hide Windows entries", installing some of these mentioned above might be caused the default codec and its priority to change and might even be changing WMF own setting in Registry, 

Check what you have and clean them.

Share this post


Link to post
4 hours ago, Renate Schaaf said:

I've tried to intercept this by moving the call into a task and generating an exception on timeout. This is of course terrible for performance in a call which is used very often, but now at least I get an error message when it occurs. Still, the application hangs and the only way to solve this is to kill the application in taskmanager. 

Have you considered moving your audio processing into a separate process of its own that your main program can communicate with. If the processing times out then your main program can kill and restart that separate process.  That way, you don't have to kill your main program.

Share this post


Link to post
16 hours ago, Kas Ob. said:

see what codec are installed on 32bit and 64bit, if you doubt then delete them !

I had k-lite installed, but seeing it didn't contain anything for mf, I deinstalled it right away. The autorun-tool doesn't show anything in codecs, if I filter out microsoft. The ac-4 decoder is available in an appxbundle on microsoft store. For the rest of your comments, I'll have to do some more research.

 

Thanx!

Share this post


Link to post
16 hours ago, Remy Lebeau said:

Have you considered moving your audio processing into a separate process of its own that your main program can communicate with. If the processing times out then your main program can kill and restart that separate process.  That way, you don't have to kill your main program.

That's a good idea. But my project is a utility class which combines pictures and video-clips into a video, so there's no application per se. I could write a helper-procedure for testing an input-file in a separate process, and if that doesn't return, give the user an option to blacklist that file. (Once I figure out how to do that.) Otherwise the logic of what I have to run in a separate process is a bit beyond me 🙂

 

Thanx!

Share this post


Link to post
On 9/25/2025 at 5:51 PM, Kas Ob. said:

but to fix it you need to pinpoint the cause.

I think I found the setting with the potential for errors and hangs: It's the source-reader attribute

 

MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING.

 

It allows the source-reader to decode to a new size, change pixel-aspect and framerate. If I change it to

 

MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING,

 

all the videos that worked under Win10 can be processed under Win11 too. And the encoder does framerate-conversion automagically! Problem is, now I have to resize the output myself, which is causing a slowdown. I solved it for the time being by having a slow but safe procedure AddVideo and one which is fast but might fail. I'll also implement the test based on Remy's idea. And then I hope that the next Win-Update will fix the problems.

  • Like 1

Share this post


Link to post
15 hours ago, Renate Schaaf said:

 

all the videos that worked under Win10 can be processed under Win11 too. And the encoder does framerate-conversion automagically

Nice find, and it will allow deeper diagnose of the problem.

15 hours ago, Renate Schaaf said:

now I have to resize the output myself, which is causing a slowdown.

So try different path, in other words try to pinpoint the failure cause with using MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, from https://learn.microsoft.com/en-us/windows/win32/medfound/mf-source-reader-enable-advanced-video-processing

Quote

If this attribute is TRUE, the MF_READWRITE_DISABLE_CONVERTERS attribute must be FALSE.

The Source Reader looks for video processors that are registered in the MFT_CATEGORY_VIDEO_PROCESSOR category, including MFTs that are registered for the local process. (See MFTRegisterLocal for more information about local registration of MFTs.) The Source Reader uses the Transcode Video Processor (XVP) if no other suitable video processor is found.

This arbitrary "looks for video processors" is most likely the culprit, assuming the MF_READWRITE_DISABLE_CONVERTERS is disabled, so try to use see what the difference between the two OSs in selecting the transformer, so,

Use MFTEnumEx https://learn.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex with MFT_CATEGORY_VIDEO_PROCESSOR also compare how disabling hardware acceleration impacting this enumeration,

and MFTGetInfo https://learn.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftgetinfo

to get more in depth view of the failing or freezing cause, which seems wrong transformer or un-configured (more advanced that need extra settings) transformer.

 

Also you can build your own IMFTransform which is declared in WinApi.MediaFoundationApi.MfTransform and use MFTRegisterLocal to use it with registering the COM itself (as dll), this what got to mind when i asked if there is extra codecs installed and they might be registered them selves as transformers in this case.

 

In other words you are on the correct path now, and highly recommend to continue tweaking and fixing MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING instead of MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, if the problem is selecting different transformer then you need to fix that, the one that was/is working for both OSs, the one with high compatibility across WMF in Windows, see it could be as simple as HDR thingy (transformer) which got higher priority in Windows 11

 

 

ps; writing IMFTransform might be not an easy job but will greatly enhance you debugging and tweaking.

ps2; searching the Internet for something helps in tracing and i found this gem, never used it, but it looks brilliant and what doctor should recommend 

https://learn.microsoft.com/en-us/windows/win32/medfound/mftrace

https://learn.microsoft.com/en-us/windows/win32/medfound/using-mftrace

  • Thanks 1

Share this post


Link to post

One more thing, AI should and great helper in writing a passthrrough IMFTransform.

 

additional thing in mind, see, if there is a problem or conflict in codec and transformers for WMF, this it is easy to fix, it should be in the registry, and here is the dilemma, if you fixed it then it will work for you and might fail for many others, so trying to fix the code and making it sturdy before fixing the registry would be better, there is few ways to clean this, but i suggest to leave it to later.

Share this post


Link to post

I asked my free Claude with

Quote

Implement in Delphi a passthrough IMFTransformer, use the mfpack and the declaration from https://github.com/FactoryXCode/MfPack/blob/Master/MfPack/src/WinApi.MediaFoundationApi.MfTransform.pas

 

and the result was 

Quote

I'll help you implement a passthrough IMFTransform in Delphi using the mfpack library. A passthrough transform simply copies input data to output without modification, which is useful as a template for more complex transforms.

Delphi IMFTransform Passthrough Implementation
Code 
unit PassthroughTransform; interface uses WinApi.Windows, WinApi.ComBaseApi, WinApi.MediaFoundationApi.MfApi, WinApi.MediaFoundationApi.MfObjects, WinApi.MediaFoundationApi.MfTransform, WinApi.MediaFoundationApi.MfIdl, System.Classes,

I've implemented a complete passthrough IMFTransform in Delphi using the mfpack library. Here are the key features of this implementation:

Key Components:

  1. Class Structure: TPassthroughTransform implements the IMFTransform interface with proper COM reference counting.
  2. Stream Configuration:
    • Single input stream (ID 0)
    • Single output stream (ID 0)
    • Fixed stream topology (cannot add/remove streams)
  3. Media Type Handling:
    • Accepts most input media types
    • Output type must match input type exactly (passthrough behavior)
    • Proper type validation and error handling
  4. Data Processing:
    • ProcessInput: Stores incoming samples
    • ProcessOutput: Copies data from input to output buffer without modification
    • Handles buffer locking/unlocking safely
    • Copies sample attributes and metadata
  5. State Management:
    • Tracks whether input is available
    • Handles flush, drain, and streaming messages
    • Proper cleanup and resource management

Usage Example:

 
pascal

var
  Transform: IMFTransform;
begin
  Transform := TPassthroughTransform.Create;
  // Use the transform in your Media Foundation pipeline
  // Set input/output types, process samples, etc.
end;

Key Features:

  • Thread-safe: Uses proper COM reference counting
  • Error handling: Comprehensive error checking and appropriate HRESULT returns
  • Memory management: Safe buffer locking/unlocking with exception handling
  • Standards compliant: Follows Media Foundation transform patterns
  • Extensible: Easy to modify for actual data processing instead of passthrough

This implementation serves as an excellent foundation for creating custom Media Foundation transforms. You can modify the ProcessOutput method to perform actual data transformation instead of just copying the data.

The code itself is 

unit PassthroughTransform;

interface

uses
  WinApi.Windows,
  WinApi.ComBaseApi,
  WinApi.MediaFoundationApi.MfApi,
  WinApi.MediaFoundationApi.MfObjects,
  WinApi.MediaFoundationApi.MfTransform,
  WinApi.MediaFoundationApi.MfIdl,
  System.Classes,
  System.SysUtils;

type
  TPassthroughTransform = class(TInterfacedObject, IMFTransform)
  private
    FRefCount: Integer;
    FInputMediaType: IMFMediaType;
    FOutputMediaType: IMFMediaType;
    FInputSample: IMFSample;
    FAttributes: IMFAttributes;
    FStreamLimits: MFT_STREAM_LIMITS;
    FInputStreamInfo: MFT_INPUT_STREAM_INFO;
    FOutputStreamInfo: MFT_OUTPUT_STREAM_INFO;
    
    // Internal state
    FHasInput: Boolean;
    FDraining: Boolean;
    
  protected
    // Helper methods
    function IsValidInputStream(dwInputStreamID: DWORD): Boolean;
    function IsValidOutputStream(dwOutputStreamID: DWORD): Boolean;
    function OnCheckInputType(pmt: IMFMediaType): HResult;
    function OnSetInputType(pmt: IMFMediaType): HResult;
    function OnSetOutputType(pmt: IMFMediaType): HResult;
    
  public
    constructor Create;
    destructor Destroy; override;
    
    // IMFTransform methods
    function GetStreamLimits(out pdwInputMinimum: DWORD;
                           out pdwInputMaximum: DWORD;
                           out pdwOutputMinimum: DWORD;
                           out pdwOutputMaximum: DWORD): HResult; stdcall;
                           
    function GetStreamCount(out pcInputStreams: DWORD;
                          out pcOutputStreams: DWORD): HResult; stdcall;
                          
    function GetStreamIDs(dwInputIDArraySize: DWORD;
                         var pdwInputIDs: DWORD;
                         dwOutputIDArraySize: DWORD;
                         var pdwOutputIDs: DWORD): HResult; stdcall;
                         
    function GetInputStreamInfo(dwInputStreamID: DWORD;
                              out pStreamInfo: MFT_INPUT_STREAM_INFO): HResult; stdcall;
                              
    function GetOutputStreamInfo(dwOutputStreamID: DWORD;
                               out pStreamInfo: MFT_OUTPUT_STREAM_INFO): HResult; stdcall;
                               
    function GetAttributes(out pAttributes: IMFAttributes): HResult; stdcall;
    
    function GetInputStreamAttributes(dwInputStreamID: DWORD;
                                    out pAttributes: IMFAttributes): HResult; stdcall;
                                    
    function GetOutputStreamAttributes(dwOutputStreamID: DWORD;
                                     out pAttributes: IMFAttributes): HResult; stdcall;
                                     
    function DeleteInputStream(dwStreamID: DWORD): HResult; stdcall;
    
    function AddInputStreams(cStreams: DWORD;
                           var adwStreamIDs: DWORD): HResult; stdcall;
                           
    function GetInputAvailableType(dwInputStreamID: DWORD;
                                 dwTypeIndex: DWORD;
                                 out ppType: IMFMediaType): HResult; stdcall;
                                 
    function GetOutputAvailableType(dwOutputStreamID: DWORD;
                                  dwTypeIndex: DWORD;
                                  out ppType: IMFMediaType): HResult; stdcall;
                                  
    function SetInputType(dwInputStreamID: DWORD;
                        pType: IMFMediaType;
                        dwFlags: DWORD): HResult; stdcall;
                        
    function SetOutputType(dwOutputStreamID: DWORD;
                         pType: IMFMediaType;
                         dwFlags: DWORD): HResult; stdcall;
                         
    function GetInputCurrentType(dwInputStreamID: DWORD;
                               out ppType: IMFMediaType): HResult; stdcall;
                               
    function GetOutputCurrentType(dwOutputStreamID: DWORD;
                                out ppType: IMFMediaType): HResult; stdcall;
                                
    function GetInputStatus(dwInputStreamID: DWORD;
                          out pdwFlags: DWORD): HResult; stdcall;
                          
    function GetOutputStatus(out pdwFlags: DWORD): HResult; stdcall;
    
    function SetOutputBounds(hnsLowerBound: LONGLONG;
                           hnsUpperBound: LONGLONG): HResult; stdcall;
                           
    function ProcessEvent(dwInputStreamID: DWORD;
                        pEvent: IMFMediaEvent): HResult; stdcall;
                        
    function ProcessMessage(eMessage: MFT_MESSAGE_TYPE;
                          ulParam: ULONG_PTR): HResult; stdcall;
                          
    function ProcessInput(dwInputStreamID: DWORD;
                        pSample: IMFSample;
                        dwFlags: DWORD): HResult; stdcall;
                        
    function ProcessOutput(dwFlags: DWORD;
                         cOutputBufferCount: DWORD;
                         var pOutputSamples: MFT_OUTPUT_DATA_BUFFER;
                         out pdwStatus: DWORD): HResult; stdcall;
  end;

implementation

{ TPassthroughTransform }

constructor TPassthroughTransform.Create;
begin
  inherited Create;
  FRefCount := 0;
  FHasInput := False;
  FDraining := False;
  
  // Initialize stream limits - single input, single output
  FStreamLimits.cbSize := SizeOf(MFT_STREAM_LIMITS);
  FStreamLimits.dwInputMaximum := 1;
  FStreamLimits.dwInputMinimum := 1;
  FStreamLimits.dwOutputMaximum := 1;
  FStreamLimits.dwOutputMinimum := 1;
  
  // Initialize stream info
  FInputStreamInfo.cbSize := SizeOf(MFT_INPUT_STREAM_INFO);
  FInputStreamInfo.hnsMaxLatency := 0;
  FInputStreamInfo.dwFlags := 0; // No special requirements
  FInputStreamInfo.cbMaxLookahead := 0;
  FInputStreamInfo.cbAlignment := 0;
  
  FOutputStreamInfo.cbSize := SizeOf(MFT_OUTPUT_STREAM_INFO);
  FOutputStreamInfo.dwFlags := MFT_OUTPUT_STREAM_PROVIDES_SAMPLES or
                              MFT_OUTPUT_STREAM_WHOLE_SAMPLES;
  FOutputStreamInfo.cbAlignment := 0;
  FOutputStreamInfo.cbSize := 0;
  
  // Create attributes store
  MFCreateAttributes(FAttributes, 0);
end;

destructor TPassthroughTransform.Destroy;
begin
  FInputMediaType := nil;
  FOutputMediaType := nil;
  FInputSample := nil;
  FAttributes := nil;
  inherited Destroy;
end;

function TPassthroughTransform.IsValidInputStream(dwInputStreamID: DWORD): Boolean;
begin
  Result := (dwInputStreamID = 0);
end;

function TPassthroughTransform.IsValidOutputStream(dwOutputStreamID: DWORD): Boolean;
begin
  Result := (dwOutputStreamID = 0);
end;

function TPassthroughTransform.OnCheckInputType(pmt: IMFMediaType): HResult;
var
  majorType: TGUID;
begin
  if not Assigned(pmt) then
  begin
    Result := E_POINTER;
    Exit;
  end;
  
  // Get the major type
  Result := pmt.GetMajorType(majorType);
  if FAILED(Result) then
    Exit;
    
  // For a passthrough, we accept most media types
  // You can add specific validation here based on your needs
  Result := S_OK;
end;

function TPassthroughTransform.OnSetInputType(pmt: IMFMediaType): HResult;
begin
  FInputMediaType := pmt;
  Result := S_OK;
end;

function TPassthroughTransform.OnSetOutputType(pmt: IMFMediaType): HResult;
begin
  FOutputMediaType := pmt;
  Result := S_OK;
end;

function TPassthroughTransform.GetStreamLimits(out pdwInputMinimum,
  pdwInputMaximum, pdwOutputMinimum, pdwOutputMaximum: DWORD): HResult;
begin
  pdwInputMinimum := FStreamLimits.dwInputMinimum;
  pdwInputMaximum := FStreamLimits.dwInputMaximum;
  pdwOutputMinimum := FStreamLimits.dwOutputMinimum;
  pdwOutputMaximum := FStreamLimits.dwOutputMaximum;
  Result := S_OK;
end;

function TPassthroughTransform.GetStreamCount(out pcInputStreams,
  pcOutputStreams: DWORD): HResult;
begin
  pcInputStreams := 1;
  pcOutputStreams := 1;
  Result := S_OK;
end;

function TPassthroughTransform.GetStreamIDs(dwInputIDArraySize: DWORD;
  var pdwInputIDs: DWORD; dwOutputIDArraySize: DWORD;
  var pdwOutputIDs: DWORD): HResult;
begin
  // Fixed stream IDs, so this is not needed
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.GetInputStreamInfo(dwInputStreamID: DWORD;
  out pStreamInfo: MFT_INPUT_STREAM_INFO): HResult;
begin
  if not IsValidInputStream(dwInputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  pStreamInfo := FInputStreamInfo;
  Result := S_OK;
end;

function TPassthroughTransform.GetOutputStreamInfo(dwOutputStreamID: DWORD;
  out pStreamInfo: MFT_OUTPUT_STREAM_INFO): HResult;
begin
  if not IsValidOutputStream(dwOutputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  pStreamInfo := FOutputStreamInfo;
  Result := S_OK;
end;

function TPassthroughTransform.GetAttributes(out pAttributes: IMFAttributes): HResult;
begin
  pAttributes := FAttributes;
  if Assigned(pAttributes) then
    pAttributes._AddRef;
  Result := S_OK;
end;

function TPassthroughTransform.GetInputStreamAttributes(dwInputStreamID: DWORD;
  out pAttributes: IMFAttributes): HResult;
begin
  // No per-stream attributes
  pAttributes := nil;
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.GetOutputStreamAttributes(dwOutputStreamID: DWORD;
  out pAttributes: IMFAttributes): HResult;
begin
  // No per-stream attributes
  pAttributes := nil;
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.DeleteInputStream(dwStreamID: DWORD): HResult;
begin
  // Cannot delete streams - we have fixed streams
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.AddInputStreams(cStreams: DWORD;
  var adwStreamIDs: DWORD): HResult;
begin
  // Cannot add streams - we have fixed streams
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.GetInputAvailableType(dwInputStreamID,
  dwTypeIndex: DWORD; out ppType: IMFMediaType): HResult;
begin
  // Passthrough accepts any type, so we don't enumerate types
  ppType := nil;
  Result := MF_E_NO_MORE_TYPES;
end;

function TPassthroughTransform.GetOutputAvailableType(dwOutputStreamID,
  dwTypeIndex: DWORD; out ppType: IMFMediaType): HResult;
begin
  if not IsValidOutputStream(dwOutputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if not Assigned(FInputMediaType) then
  begin
    Result := MF_E_TRANSFORM_TYPE_NOT_SET;
    Exit;
  end;
  
  if dwTypeIndex > 0 then
  begin
    Result := MF_E_NO_MORE_TYPES;
    Exit;
  end;
  
  // Output type is the same as input type for passthrough
  Result := MFCreateMediaType(ppType);
  if SUCCEEDED(Result) then
    Result := FInputMediaType.CopyAllItems(ppType);
end;

function TPassthroughTransform.SetInputType(dwInputStreamID: DWORD;
  pType: IMFMediaType; dwFlags: DWORD): HResult;
begin
  if not IsValidInputStream(dwInputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if (dwFlags and MFT_SET_TYPE_TEST_ONLY) <> 0 then
  begin
    // Just test if we can accept this type
    Result := OnCheckInputType(pType);
    Exit;
  end;
  
  // Actually set the type
  Result := OnCheckInputType(pType);
  if SUCCEEDED(Result) then
    Result := OnSetInputType(pType);
end;

function TPassthroughTransform.SetOutputType(dwOutputStreamID: DWORD;
  pType: IMFMediaType; dwFlags: DWORD): HResult;
begin
  if not IsValidOutputStream(dwOutputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if not Assigned(FInputMediaType) then
  begin
    Result := MF_E_TRANSFORM_TYPE_NOT_SET;
    Exit;
  end;
  
  // For passthrough, output type must match input type
  if not Assigned(pType) then
  begin
    FOutputMediaType := nil;
    Result := S_OK;
    Exit;
  end;
  
  // Compare types - they should be identical for passthrough
  Result := pType.IsEqual(FInputMediaType, pdwFlags);
  if (Result <> S_OK) then
  begin
    Result := MF_E_INVALIDMEDIATYPE;
    Exit;
  end;
  
  if (dwFlags and MFT_SET_TYPE_TEST_ONLY) = 0 then
    Result := OnSetOutputType(pType)
  else
    Result := S_OK;
end;

function TPassthroughTransform.GetInputCurrentType(dwInputStreamID: DWORD;
  out ppType: IMFMediaType): HResult;
begin
  if not IsValidInputStream(dwInputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if not Assigned(FInputMediaType) then
  begin
    ppType := nil;
    Result := MF_E_TRANSFORM_TYPE_NOT_SET;
    Exit;
  end;
  
  ppType := FInputMediaType;
  ppType._AddRef;
  Result := S_OK;
end;

function TPassthroughTransform.GetOutputCurrentType(dwOutputStreamID: DWORD;
  out ppType: IMFMediaType): HResult;
begin
  if not IsValidOutputStream(dwOutputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if not Assigned(FOutputMediaType) then
  begin
    ppType := nil;
    Result := MF_E_TRANSFORM_TYPE_NOT_SET;
    Exit;
  end;
  
  ppType := FOutputMediaType;
  ppType._AddRef;
  Result := S_OK;
end;

function TPassthroughTransform.GetInputStatus(dwInputStreamID: DWORD;
  out pdwFlags: DWORD): HResult;
begin
  if not IsValidInputStream(dwInputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if FHasInput then
    pdwFlags := 0  // Cannot accept input
  else
    pdwFlags := MFT_INPUT_STATUS_ACCEPT_DATA;
    
  Result := S_OK;
end;

function TPassthroughTransform.GetOutputStatus(out pdwFlags: DWORD): HResult;
begin
  if FHasInput then
    pdwFlags := MFT_OUTPUT_STATUS_SAMPLE_READY
  else
    pdwFlags := 0;
    
  Result := S_OK;
end;

function TPassthroughTransform.SetOutputBounds(hnsLowerBound,
  hnsUpperBound: LONGLONG): HResult;
begin
  // Not implemented for passthrough
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.ProcessEvent(dwInputStreamID: DWORD;
  pEvent: IMFMediaEvent): HResult;
begin
  // Events are not processed in basic passthrough
  Result := E_NOTIMPL;
end;

function TPassthroughTransform.ProcessMessage(eMessage: MFT_MESSAGE_TYPE;
  ulParam: ULONG_PTR): HResult;
begin
  case eMessage of
    MFT_MESSAGE_COMMAND_FLUSH:
    begin
      FInputSample := nil;
      FHasInput := False;
      FDraining := False;
      Result := S_OK;
    end;
    
    MFT_MESSAGE_COMMAND_DRAIN:
    begin
      FDraining := True;
      Result := S_OK;
    end;
    
    MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
      Result := S_OK;
      
    MFT_MESSAGE_NOTIFY_END_STREAMING:
    begin
      FInputSample := nil;
      FHasInput := False;
      FDraining := False;
      Result := S_OK;
    end;
    
    MFT_MESSAGE_SET_D3D_MANAGER:
      Result := S_OK; // Ignore for passthrough
      
  else
    Result := E_NOTIMPL;
  end;
end;

function TPassthroughTransform.ProcessInput(dwInputStreamID: DWORD;
  pSample: IMFSample; dwFlags: DWORD): HResult;
begin
  if not IsValidInputStream(dwInputStreamID) then
  begin
    Result := MF_E_INVALIDSTREAMNUMBER;
    Exit;
  end;
  
  if not Assigned(pSample) then
  begin
    Result := E_POINTER;
    Exit;
  end;
  
  if not Assigned(FInputMediaType) then
  begin
    Result := MF_E_NOTACCEPTING;
    Exit;
  end;
  
  if FHasInput then
  begin
    Result := MF_E_NOTACCEPTING;
    Exit;
  end;
  
  // Store the input sample
  FInputSample := pSample;
  FHasInput := True;
  Result := S_OK;
end;

function TPassthroughTransform.ProcessOutput(dwFlags: DWORD;
  cOutputBufferCount: DWORD; var pOutputSamples: MFT_OUTPUT_DATA_BUFFER;
  out pdwStatus: DWORD): HResult;
var
  pBuffer: IMFMediaBuffer;
  pInputBuffer: IMFMediaBuffer;
  pData, pInputData: PByte;
  cbData, cbInputData: DWORD;
  cbMaxLength, cbInputMaxLength: DWORD;
begin
  pdwStatus := 0;
  
  if cOutputBufferCount < 1 then
  begin
    Result := E_INVALIDARG;
    Exit;
  end;
  
  if not FHasInput then
  begin
    Result := MF_E_TRANSFORM_NEED_MORE_INPUT;
    Exit;
  end;
  
  if not Assigned(pOutputSamples.pSample) then
  begin
    Result := E_INVALIDARG;
    Exit;
  end;
  
  try
    // Get the output buffer
    Result := pOutputSamples.pSample.GetBufferByIndex(0, pBuffer);
    if FAILED(Result) then
      Exit;
      
    // Get the input buffer
    Result := FInputSample.GetBufferByIndex(0, pInputBuffer);
    if FAILED(Result) then
      Exit;
    
    // Lock both buffers
    Result := pInputBuffer.Lock(pInputData, cbInputMaxLength, cbInputData);
    if FAILED(Result) then
      Exit;
      
    try
      Result := pBuffer.Lock(pData, cbMaxLength, cbData);
      if FAILED(Result) then
        Exit;
        
      try
        // Ensure output buffer is large enough
        if cbMaxLength < cbInputData then
        begin
          Result := MF_E_BUFFERTOOSMALL;
          Exit;
        end;
        
        // Copy data (passthrough)
        if cbInputData > 0 then
          Move(pInputData^, pData^, cbInputData);
          
        // Set the data length
        Result := pBuffer.SetCurrentLength(cbInputData);
        if FAILED(Result) then
          Exit;
          
      finally
        pBuffer.Unlock;
      end;
      
    finally
      pInputBuffer.Unlock;
    end;
    
    // Copy sample attributes
    Result := FInputSample.CopyAllItems(pOutputSamples.pSample);
    if FAILED(Result) then
      Exit;
    
    // Clear input
    FInputSample := nil;
    FHasInput := False;
    
    pOutputSamples.dwStatus := 0;
    Result := S_OK;
    
  except
    Result := E_UNEXPECTED;
  end;
end;

end.

And that is way time saving from going after researching each and every method, this is really nice, anyway, i am sure there is bugs and as always many tweaking is needed but the skeleton is right, also you can ask an AI to go and show how to register as Local instead of global and how to use it.

 

And good luck !

  • Thanks 1

Share this post


Link to post
4 hours ago, Kas Ob. said:

And good luck !

Well, I'm overwhelmed. Got something to study over the weekend. Thanks a lot, I'll be back with questions for sure, maybe in private mail, I'm not sure the drift this thread has taken is of common interest 🙂

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×