Reading Data From External Program

New to FreeBASIC? Post your questions here.
Post Reply
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Reading Data From External Program

Post by chris319 »

How does one read data returned by an external program?

Do I want to use exec or open pipe? Specifically, I want to read frames of video using ffmpeg.

Tnank you.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Reading Data From External Program

Post by grindstone »

I regulary use ffmpeg for audio conversion. The easiest way of access is by a pipe:

Code: Select all

#Include "crt\stdio.bi"

Dim As String ffmpeg = "C:\programs\ffmpeg.exe" 'replace with the full path of your ffmpeg.exe
Dim As String order = ffmpeg + " ?" 'replace the question mark with the command string you wish to send to ffmpeg

Dim As Any Ptr ffmpg = popen(StrPtr(order), @"r")

Sleep

pclose(ffmpg)

End
To see how to capture video frames have a look at this page.
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

Thanks, but how do I read the frame data into an array in FB?

I already have:

const H = 720: const W = 1280

dim shared as ubyte frame(H*W*3) 'Allocate a buffer To store one frame

In my PureBasic version I use:

pixct = ReadProgramData(readFrame, @frame(0), H * W * 3)

If pixct = 0: Break: EndIf ;NO MORE FRAMES TO READ

How is this accomplished in FB? I'm looking for the equivalent of "ReadProgramData".

There is also this:

https://batchloaf.wordpress.com/2017/02 ... ment-24886
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Reading Data From External Program

Post by D.J.Peters »

Normally you send/write a command and receive/read the result but FreeBASIC pipes are not bidirectional !
(so far I know)

Joshy
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

FreeBASIC pipes are not bidirectional !
(so far I know)
This is gotten around by using separate input and output pipes.
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

It seems I can send commands to ffmpeg, and it responds, but I need to set up a second pipe to read in the received data which I don't know how to do.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Reading Data From External Program

Post by grindstone »

chris319 wrote:Thanks, but how do I read the frame data into an array in FB?
The simplest way is:
  • -Open the output file
    -Transfer the data to the array
    -Close/kill the output file
chris319 wrote:It seems I can send commands to ffmpeg, and it responds, but I need to set up a second pipe to read in the received data which I don't know how to do.
In all modesty: https://www.freebasic-portal.de/code-be ... i-297.html
If required I could translate the description to english, but I think the (english) comments should be sufficient to get along with it.

I've never tried , but the piping should work if ffmpeg's output is redirected to stdout.
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

The library presented here provides 5 functions - integrated via #Include:

1. bipOpen (ProgrammName, [showmode]) - Opens the (console) program ProgrammName and sets up two unidirectional pipes for communication. The optional parameter showmode defines the appearance of the called program. For possible parameters see https://docs.microsoft.com/en-us/window ... dfrom=MSDN. The default is SW_NORMAL. The return value is a pointer to the corresponding HANDLE array (HandlePointer).

2. bipClose (HandlePointer) - Ends the program, closes the pipes, clears the handles, releases the memory and sets the HandlePointer to 0.

3. bipWrite (HandlePointer, text, ["b"]) - Sends a string for standard input of the opened program. If the optional parameter "b" (for binary) is set, the string is sent unchanged, otherwise a Chr (13,10) is added.

4. bipRead (HandlePointer, [timeout]) - Reads the standard output of the program. The default for timeout is 100ms.

5. bipReadLine (HandlePointer, [separator], [timeout]) - Reads a line from the started program, analogous to the "Line Input" command. The optional separator parameter is either an "a" (for any) followed by a list of characters, each of which ends the line, or an "e" (for exact), followed by one or more characters to represent the separator , The default is "a" & Chr (13,10). The default for timeout is 100ms.

Code: Select all

#Include Once "windows.bi"

Type bipdata
    hProcessHandle As HANDLE
    hWritePipe As HANDLE
    hReadPipe As HANDLE
End Type

Function bipOpen(PrgName As String, showmode As Short = SW_NORMAL) As bipdata Ptr

    Dim As STARTUPINFO si
    Dim As PROCESS_INFORMATION pi
    Dim As SECURITY_ATTRIBUTES sa
    Dim As HANDLE hReadPipe, hWritePipe, hReadChildPipe, hWriteChildPipe
    Dim pPipeHandles As bipdata Ptr

    'set security attributes
    sa.nLength = SizeOf(SECURITY_ATTRIBUTES)
    sa.lpSecurityDescriptor = NULL 'use default descriptor
    sa.bInheritHandle = TRUE

    'create one pipe for each direction
    CreatePipe(@hReadChildPipe,@hWritePipe,@sa,0) 'parent to child
    CreatePipe(@hReadPipe,@hWriteChildPipe,@sa,0) 'child to parent

    GetStartupInfo(@si)

    si.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
    si.wShowWindow = showmode 'appearance of child process window
    si.hStdOutput  = hWriteChildPipe
    si.hStdError   = hWriteChildPipe
    si.hStdInput   = hReadChildPipe

    CreateProcess(0,PrgName,0,0,TRUE,CREATE_NEW_CONSOLE,0,0,@si,@pi)

    CloseHandle(hWriteChildPipe)
    CloseHandle(hReadChildPipe)

    pPipeHandles = Allocate (SizeOf(bipdata)) 'area for storing the handles
    pPipeHandles->hProcessHandle = pi.hProcess 'handle to child process
    pPipeHandles->hWritePipe = hWritePipe
    pPipeHandles->hReadPipe = hReadPipe

    Return pPipeHandles 'pointer to handle array

End Function

Sub bipClose(ByRef pPipeHandles As bipdata Ptr)

    If pPipeHandles = 0 Then Return
    TerminateProcess(pPipeHandles->hProcessHandle, 0)
    CloseHandle(pPipeHandles->hWritePipe)
    CloseHandle(pPipeHandles->hReadPipe)
    DeAllocate(pPipeHandles)
    pPipeHandles = 0

End Sub

Function bipWrite(pPipeHandles As bipdata Ptr, text As String, mode As String = "") As Integer
    Dim As Integer iNumberOfBytesWritten
    'Dim As String txt = text

    '? Len(text);" ";
    If pPipeHandles = 0 Then Return 0
    If LCase(mode) <> "b" Then 'not binary mode
        text += Chr(13,10)
    EndIf

    WriteFile(pPipeHandles->hWritePipe,StrPtr(text),Len(text),@iNumberOfBytesWritten,0)
    Return iNumberOfBytesWritten

End Function


Function bipRead(pPipeHandles As bipdata Ptr, timeout As UInteger = 100) As String
    'returns the whole pipe content until the pipe is empty or timeout occurs.
    ' timeout default is 100ms to prevent a deadlock

    Dim As Integer iNumberOfBytesRead, iTotalBytesAvail, iBytesLeftThisMessage
    Dim As String buffer, retText
    Dim As Double tout = Timer + Cast(Double,timeout) / 1000

    If pPipeHandles = 0 Then Return "" 'no valid pointer

    Do
        PeekNamedPipe(pPipeHandles->hReadPipe,0,0,0,@iTotalBytesAvail,0)
        If iTotalBytesAvail Then
            buffer = String(iTotalBytesAvail,Chr(0))
            ReadFile(pPipeHandles->hReadPipe,StrPtr(buffer),Len(buffer),@iNumberOfBytesRead,0)
            retText &= buffer
        ElseIf Len(retText) Then
            Exit Do
        EndIf
    Loop Until Timer > tout

    Return retText

End Function

Function bipReadLine(pPipeHandles As bipdata Ptr, separator As String = "a" & Chr(13,10), timeout As UInteger = 100) As String
    'returns the pipe content till the first separator if any, or otherwise the whole pipe
    ' content on timeout. timeout default is 100ms to prevent a deadlock

    Dim As Integer iNumberOfBytesRead, iTotalBytesAvail, iBytesLeftThisMessage, endPtr
    Dim As String buffer, retText, mode
    Dim As Double tout = Timer + Cast(Double,timeout) / 1000

    If pPipeHandles = 0 Then Return "" 'no valid pointer

    mode = LCase(Left(separator,1))
    separator = Mid(separator,2)

    Do
        PeekNamedPipe(pPipeHandles->hReadPipe,0,0,0,@iTotalBytesAvail,0)
        If iTotalBytesAvail Then
            buffer = String(iTotalBytesAvail,Chr(0))
            PeekNamedPipe(pPipeHandles->hReadPipe,StrPtr(buffer),Len(buffer),@iNumberOfBytesRead, _
                          @iTotalBytesAvail,@iBytesLeftThisMessage) 'copy pipe content to buffer
            Select Case mode
                Case "a" 'any
                    endPtr = InStr(buffer, Any separator) 'look for line end sign
                Case "e" 'exact
                    endPtr = InStr(buffer, separator) 'look for line end sign
            End Select
            If endPtr Then 'return pipe content till line end
                Select Case mode
                    Case "a"
                        Do While (InStr(separator,Chr(buffer[endPtr - 1]))) And (endPtr < Len(buffer))
                            endPtr += 1
                        Loop
                        endPtr -= 1
                    Case "e"
                        endPtr += Len(separator)
                End Select
                retText = Left(buffer,endPtr)
                ReadFile(pPipeHandles->hReadPipe,StrPtr(buffer),endPtr,@iNumberOfBytesRead,0) 'remove read bytes from pipe
                Select Case mode
                    Case "a"
                        Return RTrim(retText,Any separator) 'remove line end sign from returned string
                    Case "e"
                        Return Left(retText,Len(retText) - Len(separator))
                End Select
            EndIf
        EndIf
    Loop Until Timer > tout

    If iTotalBytesAvail Then 'return all pipe content
        buffer = String(iTotalBytesAvail,Chr(0))
        ReadFile(pPipeHandles->hReadPipe,StrPtr(buffer),Len(buffer),@iNumberOfBytesRead,0)
        Return buffer
    EndIf

    Return ""

End Function
Last edited by chris319 on Mar 03, 2020 21:11, edited 1 time in total.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Reading Data From External Program

Post by grindstone »

@chris319: Thank you for taking the work out of my hands to translate the description. May I add your translation to the code example?
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

grindstone wrote:@chris319: Thank you for taking the work out of my hands to translate the description. May I add your translation to the code example?
Yes, of course.
chris319
Posts: 119
Joined: Jun 10, 2005 18:23

Re: Reading Data From External Program

Post by chris319 »

The difference between your code and what I use in the PureBasic version is that in the PB version I specify the number of bytes to be read into an array from ffmpeg:

Code: Select all

ReadProgramData(readFrame, @frame(0), #H*#W*3)
Post Reply