Jump to content
Tom F

How to share data between apps

Recommended Posts

Posted (edited)

I'm planning to approach the author of another program that captures video data. 

 

I'm going to ask her to team up with me, sending the video frames that she's capturing in realtime to my 32-bit Delphi Rio VCL app running at the same time.

 

Her app is probably written in C# or C++.

 

The data will be video frames and maybe 10 or 15 megs each, 30 fps.

 

I don't mind dropping frames so I don't need more than one memory buffer. I don't want her app to block if my app is too busy to pick up his data. In other words, if she has a frame to send and I'm in the process of reading his data from shared memory, he can just skip sending me that frame.

 

Basically, I want some way she can fill a memory buffer and then signal me it's ready.  I'd receive a Windows message and read the buffer.  She and I would clearly need some locking semaphores so we can access the data safely. And there will be some messaging necessary at startup and shutdown.  And I need to have a way of rapidly copying the buffer she provides into a variable in my app.

 

I'd like to propose to her a reliable mechanism that's simple for both of us to implement. I'm okay using third-party tools if necessary for my end (in Delphi) for handshaking and messaging and data transfer.

I don't know much about interprocess communication, so I don't know what issues or tools I should be looking at.

 

I've thought a bit about some semaphores and shared memory and PostMessage notifications between our programs, but I don't have any experience in them or other similar tools. 

 

About all I know about these types of systems is that they can dramatically fail in unpredictable ways and that they're hard to do right. 😞 

 

I, of course, don't want to re-invent the wheel, so I'm asking here:

 

Can anyone give me suggestions on how to approach this? 

Edited by Tom F
Fixed typo in Title line

Share this post


Link to post

I'd probably use a combination of shared memory and global event object. I.e. the sender and receiver both create a shared memory using CreateFileMapping() and MapViewOfFile(), then the sender fills the buffer located in the shared memory and sets the event, the receiver waits for this event and when it is activated grabs the data from the buffer and resets the event. The sender can also check the state of the event and if it is set (i.e. the receiver has not read the data yet) skip writing the next frame, etc. I do not think that the implementation is going to be too difficult.

Share this post


Link to post
Posted (edited)

If that's acceptable, simply using files would probably be the easiest way.

Since we don't know what those programs actually do with the images, that's difficult to decide.

If files are too slow, I would probably go with memory mapped files with two buffers and a combination of mutexes and events (event as in syncobjs.TEvent). One buffer is always available for the sender to fill. It doesn't check, just locks it (using the mutex), writes to it, unlocks it and sets an event to signal that data is available. The receiver is the part that manages which buffer is currently to be written to. It then waits for the event, switches the write buffer to the other one and starts processing the data. If that takes longer, the sender will overwrite the data in the write buffer as long as necessary.

Yes, I think that would work. Needs a bit of polishing though.

Edited by dummzeuch
I really hate it when my smart phone garbles the text :-(
  • Like 1

Share this post


Link to post
Posted (edited)

I'd use two buffers. The other task would write into whichever one you have not flagged as busy, or the other one that what was just filled if neither are busy.

Edited by David Schwartz

Share this post


Link to post

The keyword is dropped frame is acceptable, and that will simplify the whole thing a lot and you can go without any locking or synchronizing but this will on the the account of size of memory used, so here a suggestion:
Allocate huge shared memory buffer ( to act as ring buffer) , and by huge it will be size of frame*fps ( or to simplify use 32, it will not matter) = 15mb*32 = 480mb (of course you can use less).
use something like this structure to access the shared memory ( by shared i mean the usage of CreateFileMapping and MapViewOfFile)

unit uSharedMemory;

interface

type
  PSharedVideoRec = ^TSharedVideoRec;

  TSharedVideoRec = packed record
    Identifier: Integer;                    // Some Unique value
    FrameWidth: Integer;                    // InPixel
    FrameHeight: Integer;                   // InPixel
    PixelSize: Integer;                     // inBits
    FramePerSecond: Integer;
    FrameLength: Integer;                   // InBytes
    FrameCount: Integer;                    // Length(FrameBuffer) in frames , max = 32
    FramesTime: array[0..31] of Cardinal;   // hold GetCurrentTickCount for each filled frame
    FilledFramesFlag32: UInt32;             // Indicate last/current filled frame in bits
    RawDataBuffer: array[0..0] of Byte;     // RawDataBuffer is huge buffer with allocated size = FrameLength * FrameCount
                                            // a frame can be accessed by RawDataBuffer[frameIndex*FrameLength]
    function CurrentFrame: Integer;         // will return current/last filled
    procedure SetFrame;
  end;

implementation

{ TSharedVideoRec }

function TSharedVideoRec.CurrentFrame: Integer;

  function NumberOfTrailingZeros(x: Cardinal): Cardinal; //inline;
  const
    MultiplyDeBruijnBitPosition: array[0..31] of Cardinal = (0, 1, 28, 2, 29, 14,
      24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12,
      18, 6, 11, 5, 10, 9);
  begin
    Result := MultiplyDeBruijnBitPosition[(Cardinal(x and  - x) * $077CB531) shr 27];
  end;

begin
  Result := NumberOfTrailingZeros(FilledFramesFlag32);
end;

end.

now you need the main thread with timer or for better performance you can use one or more background threads to check of the most important value here CurrentFrame, this check will pull the value of CurrentFrame for changes and act.
For the video producer app it will allocate the memory and fill that structure, as you can see the comments most values are fixed except :
FilledFramesFlag32 can be filled by using this formula

Quote

    FrameCount = 32;
    FilledFramesFlag32 = 1 shl (FrameCounter mod 32); // FrameCounter is increasing counter for the frames produced
    RawDataBuffer will be filled from this start point RawDataBuffer[(FrameCounter mod 32) * FrameLength)]


FramesTimes not needed really so can be dropped , here you can use the another/different array to hold the FrameCounter if that is in interest
That was if you want to go with 32 frames but you can go with less to save memory, so lets say you want to allocate only 10 then

Quote

    FrameCount = 10;
    FilledFramesFlag32 = 1 shl (FrameCounter mod FrameCount ); // FrameCounter is increasing counter for the frames produced
    RawDataBuffer will be filled from this start point RawDataBuffer[(FrameCounter mod FrameCount ) * FrameLength)]


and that was for the producer as for the consumer, i think you got the idea and how it does work is clear enough, for the consumer app it s now easy.
One thing though if you are going to use background thread which i recommend to let it loop with Sleep(1) to minimize the CPU utilization, "don't use Sleep(0)", if your code handling *might* take more than FrameCount/FramePerSecond in second ( FrameCount is 32 max) then you should copy the data to local memory before handling to be on safe side check against half or third of that time.

Share this post


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

Allocate huge shared memory buffer ( to act as ring buffer) , and by huge it will be size of frame*fps ( or to simplify use 32, it will not matter) = 15mb*32 = 480mb (of course you can use less).

If dropping frames is not an issue, why bother with such a large buffer? Two frames would be enough.

Share this post


Link to post
3 minutes ago, dummzeuch said:

If dropping frames is not an issue, why bother with such a large buffer? Two frames would be enough.

True, my suggestion is just to add more flexibility and control, also it might help someone with smaller buffers size .

Share this post


Link to post
3 hours ago, Fr0sT.Brutal said:

I'd vote against shared resources in favor of streaming via pipes or socket.

I definitely wouldn't, at least not if it is about transferring large amounts of data between programs on the same computer.

  • Like 1

Share this post


Link to post

But if you ever contact the author, is it not better to ask a dll which wrap directly the data acqusition mecanism ? (if it is a cpp solution...)
In fact, I had done such things, in order to get Panda3D "PBR" offscreen rendering image, and got pretty good result with sending data via UDP by 64k chunk, (App "from" was Python).

 

Share this post


Link to post
4 hours ago, dummzeuch said:

I definitely wouldn't, at least not if it is about transferring large amounts of data between programs on the same computer.

Topicstarter has stream of data; nothing suites better for transferring a stream than an another stream.

Share this post


Link to post
9 hours ago, Fr0sT.Brutal said:

I'd vote against shared resources in favor of streaming via pipes or socket.

Sockets without a doubt. Plus if the request comes in for source and destination on different computers, no or very few changes required.

 

  • 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

×