View Full Version : Multithreaded development (from view of a Source Filter)
jordanh
7th June 2013, 23:35
You nailed it, 0x809E0EED is exactly the number that was asked from the getframe function of the changefps filter. It then just returned the last frame.
Still there is the question, how to test a source filter for MT compatibility?
From design, my CarbonSource Filter should be able to get any MT mode. All the frames being requested are already there and just need to be read from memory.
Still when i have errors when testing MT, i am never able to find out if it was my code or avisynth.
Is there a way to test a source filter on MT compatibility?
I guess there is the old standard, run a big encode in single thread mode, mode 1, mode 2 and mode 3 and make sure the 4 outputs are bit identical and the encodes do not hang or crash.
But no I don't currently have any MT thrashing harness.
A fundamentally sound design is probably the best defence.
With the modifications I suggested the Avisynth GetFrame routine is only ever reading externally maintained data structures (plucking a pointer) and as long as all the working variables are local stack based we would not expect many problems. The fragile element might be what happens when the Carbon Coder thread in recieveDecodedFrameFromCarbon modifies the std::deque<PVideoFrame> and updates the EXTERNAL_INPUT_LASTFRAMEINBUFFER.
If the std::deque<PVideoFrame> is unsuitable, perhaps a simple modulo ring buffer might be more robust. PVideoFrame RingBuffer[size];
...
return RingBuffer[n % size];The assumption being that the recieveDecodedFrameFromCarbon routine maintains an adequate window of frames around the anticipated frame number "n" being processed. Size could be large compared to the actual number of currently buffered frames, either stock idle entries with a blank PVideoFrame or copies of the lowest active PVideoFrame.
Gavino
8th June 2013, 08:01
1) the max. framenumber in avisynth is a 32bit signed int, so it is decimal: 2.147.483.648
2) we need to keep the number below this value. As far below as neccessary to be able to feed enough frames into avisynth and in the end, with all framerate conversions, the number of delivered frames may not be more than signed int (32bit)
Good advice.
@IanB: Perhaps filters like ChangeFPS should check for potential overflow when calculating the number of output frames and throw an error if so. (Alternatively, it could clamp the number of output frames to maxint.)
jordanh
9th June 2013, 01:51
@IanB: Perhaps filters like ChangeFPS should check for potential overflow when calculating the number of output frames and throw an error if so. (Alternatively, it could clamp the number of output frames to maxint.)
Hm, thats a good point. I think the problem is that in the end you cannot expect "any" fps changing plugin to follow rules like this.
In my mind, this restriction about maximum frame numer in avisynth environment is only there to be able to support the vfw interface of avisynth, the "avi", not the "synth". A more general approach may be to introduce a way to pass infinite frames through the env. 95% of existing plugins shouldnt care about the overall number of frames in the env anyway.
I would love to do some research on avisynth in those matters and implement it, but that will take really huge amounts of time.
This wording for the related feature request: support unlimited number of frames, at least when avisynth is invoked by API, instead of vfw.
@IanB & handing over PVideoFrame's instead of char * within the buffer queue:
I really want to limit the number of frame copies down to a minimum.
Acutally i already began the work 2 days ago, but i am unsure about how to deal with the allocated PVideoFrames.
// function:
// current(new) method of new frame initialisation in the HOST code (thats invoking the env)
unsigned char* pdst; // Frame buffer
PVideoFrame destinationFrame; // goes into the framebuffer_deque
destinationFrame = env->NewVideoFrame(m_avisynthInputVideoInfo); //frame with our Input properties
pdst = destinationFrame->GetWritePtr(); //frame buffer
This way i really save one copying of frames just like proposed by IanB.
In my old code, i freed the char* input frame buffers, whenever the size of buffer was more than 0.5 seconds or at the end of conversion.
Now my problem at porting from char* to PVideoFrame is, that i dont know anything about how and when Avisynth may free something automatically or not.
//new code: delete the buffers at initialisation, and whenever the buffer contains expandable frames too.
for (unsigned int i=0;i<framebuffer_deque.size();i++){//clears buffer completely
PVideoFrame lastSample = framebuffer_deque.back();
lastSample = 0;
framebuffer_deque.pop_back();//delete last sample from queue
}
Is it even neccessary to delete the stuff anymore when delivering PVideoFrames instead of char* ?
Maybe you could give some overview of how env->newVideoFrame is handled overall? do we ever need to set it to 0 for deleting it? does setting it to 0 even free the memory?
The maximum framenumber of 0x7FFFFFFF is built into the definition :-
virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
N is a 32 bit integer, but even at 120fps that allows over 200 days (nearly 5000 hours).
PVideoFrames are simply a sizeof(void*) object that is a pointer to a reference counted VideoFrame object, treat them like any other object pointer. The various operator overrides manage the reference counting and deleting when the use count goes to zero.
For your purge code, all you should need is this :-// delete the buffers at initialisation, and whenever the buffer contains expandable frames too.
for (unsigned int i=0;i<framebuffer_deque.size();i++){ / /clears buffer completely
framebuffer_deque.pop_back(); // delete last sample from queue
}doing the pop_back()'s will destruct each PVideoFrame which will have the referenced VideoFrame object decrement it's own use count, if it was the last one it will delete itself. Remember you are handing out references to the VideoFrame object, i.e. other PVideoFrames, from the source filter side, so various in progress filters and caches may have additional references to the object, but these will go as the various PVideoFrames exit their scopes.
Your posted code would have done the following :- // Do ++ use count stuff on VideoFrame object. Copy VideoFrame pointer from queue entry to lastSample.
PVideoFrame lastSample = framebuffer_deque.back();
lastSample = 0; // Do -- use count stuff on VideoFrame object. Assign 0 to lastSample.
framebuffer_deque.pop_back(); // Do use -- count stuff on VideoFrame object. Destruct PVideoFrame.
} // lastSample exits scope so runs ~PVideoFrame destructor. Pointer is 0 so no action.
One trap with PVideoFrame is the GetWritePtr() will return 0 if the use count is not 1. It is guaranteed to be 1 after a env->NewVideoFrame() and after a env->MakeWritable(). Your usage above is correct, but if you were to push it onto the queue first the use count would become 2 and a subsequent GetWritePtr() would then fail.
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.