Jump to content

Rolf Kalbermatter

Members
  • Posts

    3,783
  • Joined

  • Last visited

  • Days Won

    243

Everything posted by Rolf Kalbermatter

  1. Sometimes I feel like I must be talking some obscure galactic slang or something! Setting Call Library Nodes to run in UI thread is indeed safe but I never said this is required here. What about that English sentence is not clear? Have you looked at the last attachment I added to one of my last messages?
  2. No, the opposite if you have a DLL version 6.1.1.17 or newer. In that case it SHOULD be safe to set all Call Library Nodes for the PlayCtrl.dll functions to reentrant. No again! The byte stream coming from PlayM4_GetJPEG() is already a JPEG formatted byte stream. Dump into a binary file and it is a JPEG image. For Windows make sure to give it the filename extension .jpg or .jpeg as otherwise Windows Explorer will not understand that it is a JPEG image, but that is just a Windows thing. Microsoft in its infinite wisdom decided loooooooooong ago that the file ending is the only reliable way to determine what type a file is. Of course that is a very unreliable assumption but hey they have gotten away with it for about 40 years. So what!
  3. The documentation for the PlayM4 shared library states explicitly that the DLL should be since version 6.1.1.17 multithreading safe. Of course that statement can be as true or false as anything but it is probably safe to assume for now that it SHOULD work if we don't do massive multithreading. And since we run pretty much everything actually in our test VI event structure it is actually executing very sequentially so the multithreading aspect shouldn't even come into play for now. For a more generic library there would need to be a lot more testing needed however. And I would actually pull the whole thing a bit apart. I have cleaned up a lot of stuff and put the PlayM4 functions in their own library. My idea was to push the image retrieval into the the timeout case as in included example. Adding that into the data callback may be a little too much taxing. Also Once we know that the callback data seems to be working I would actually remove the data array on the front panel. That does take quite a bit of performance as LabVIEW needs to create a copy of that array every time to display in the control. Depending on how much the callback event taxes the system it may however never arrive in the timeout case so possibly there is never any update of the BMP data. If that happens you have anyhow already reached the limit of what your system can do and there is simply no way to try to make this still work in this way. The main idea is that if the program does get into the timeout event case I try to retrieve the current played/decoded frame number and if that is different than the previous one, the stream should be able to provide a BMP image. Retrieve it and once we get there we can look further. I choose to use a BMP retrieval as that should be already in 32-bit RGBx format and that should then be fairly easy (but anything but performant) to pull into a LabVIEW pixmap. 184663212_Hikvision-labviewSDK-Test-v1.1.0.zip
  4. Well 2 means there is no data yet to work with, 34 means PLAYM4_NEED_LARGER_BUFFER. Seems obvious what's the problem here.
  5. No it may not be. Last error values are notoriously tricky to handle in multithreading code and LabVIEW is highly multithreading. Even if they are stored in thread local storage as do the Microsoft APIs, they are difficult to handle in LabVIEW as your call to GetLastError() is potentially executed in a different thread than the previous call that caused an error and voila you are reading the last error from some completely different function call. And if you force all calls into the UI thread you are in fact calling the functions all in the same thread but between the call to the error causing function and the retrieval of the last error there could be zillion other calls that might have overwritten that last error value already. Same problem exists independent of how the functions are called when the library only maintains a global last error value. If this library has a thread safe last error value or not is not documented as far as I remember. And are you sure that the PlayM4 APIs make use of the NET_DVR_GetLastError()? If so they are almost certainly thread local storage and you need to make sure that all the functions execute in the same thread. But with call backs going on in the background there could be all kinds of things going on in the time between calling the PlayM4_GetJPEG() function and the NET_DVR_GetLastError() one. No it has its own PlayM4_GetLastError(long nPort) function! And since it takes the port (handle) as a parameter it is most likely thread context safe as the error is apparently stored in the port somehow.
  6. I'm afraid that doesn't work. It was an idea based on the fact that this function does return a size in its last parameter. If it was a Microsoft API it would work like that, and there was a reasonable chance that they would have followed the model of Microsoft APIs but alas, it seems not the case. It would have been to simple. 😃 I think you have to use the PlayM4_GetPictureSize() to calculate a reasonably large buffer and allocate that and pass it directly to the PlayM4_GetJPEG() function to MAYBE make it work.
  7. What has happened to your Google? https://letmegooglethat.com/?q=BITMAPINFOHEADER
  8. Partly valid. This driver independence should have been there before SystemLink was a thing really. It might have made SystemLink a bit harder to make work more comfortable but still. As to SystemLink itself, when it came out it had this "SliverLight" feeling to it for me. It didn't quite go as bad so far, but still. And on top of that it is very expensive. I do often combine desktop and multiple real-time target projects. It works of course best with compiled code separated but I have worked that way before separate compiled code was a (reliably working) fact.
  9. It absolutely is needed. How should the decoder otherwise get the compressed data to actually get image data???? You have no idea what you are doing here and will never get anywhere in this way. You do not understand what data comes from where, where it should go and how that all relates to each other. 1) The NET_DVR functions are the functions that communicate with the camera. 2) You can let them draw directly into a window handle. Fairly easy and works as proven already. 3) You can instead (or in addition) install a callback function which is called whenever there is a data package from the camera and that data is passed to that callback. 3a) This data is compressed in the camera in some way, in your case it seems to be MPEG4 data but it could be also another compression. 3b) When you have determined that it is the HKMI (apparently their HikVision Media data Identifier) datastream in the SYSHEAD message, you need to pass this data to their decoder, this are your PlayM4 functions. If you do not pass those data packages to the MPEG4 decoder stream it has NOTHING to decode and therefore can NOT return any image data ever!!!!! 3c) This passing of the compressed data packages happens with the PlayM4_InputData() method. This function hands the received data do the decoder which then buffers it until it has a complete frame and then decodes that frame and probably, maybe, possibly, hopefully or something stores it in a different buffer in its port (handle) as a decoded frame buffer. 3d) Only once a complete frame has been received and decoded, can you hope to get something back from the PlayM4_GetJPEG() or one of the other PlayM4_Get****() functions. If you would not keep putting only images in your post, I might be tempted to give every now and then a fixed VI but I'm not feeling inclined to try to debug images and fix them. Something like this MIGHT work. But there is no guarantee and I can not find any documentation for those functions, not even in bad English. The idea is that the function is called with an empty array buffer and a 0 nBufSize, then on return it MIGHT return a TRUE status if there is a full frame available or it might NOT. If it returns true then the last parameter is hopefully valid and can be used to determine what size of buffer needs to be allocated and then the function is called again with that buffer. If it does not return true you should not even try to interpret the returned last parameter. Another possibility MIGHT be to use the PlayM4_GetPictureSize() function. It returns a height and width value. Multiply those and multiply the result with 4 which hopefully should be enough for an entire picture buffer. The code in the Client Demo application does a bit more elaborate calculation for this, naemly: size = 3 * (4 * lWidth * lHeight + 100) It then calls PlayM4_GetJPEG() with a buffer allocated to that size, and the size value. And if PlayM4_GetJPEG() does not return true it did not return an image, no matter what. Maybe because there is not yet a full data frame to decode, maybe because one of the maybes up to now in above text didn't hold true, or some other bad luck or whatever. However even if this eventually works you have another problem. You have data in JPEG format, yet another compressed format that you still can't do much with. Sure you can decompress it with one of the earlier mentioned JPEG Stream to LabVIEW pixmap functions but it is getting now pretty complicated and multi-decoding and multi layer and therefore slooooooooooooooooow. There seems to be another method which uses yet another callback!! Yupee!! Looking at the Client Example JPEGPicDownload.cpp file you may have to actually use PlayM4_SetDecCallBack() to install a new callback function that is called when a full frame has been decoded. Fuuuuuuuuuun! This full frame seems to be fully decoded, so this COULD be converted into an IMAQ Vision image for instance. How? How many more weeks do you have for this available? PS: The StandardCallback I tried to use in my original code only works if the camera supports RTP according to the SDK documentation, so yours may not support that.
  10. That would work, but you also need to wire the nBufSize parameter. Can you guess with what value?
  11. I think the separate compiled code is only a by-product. It could have been done even without the VIs having been set to use separate compiled code. In the worst case the VIs would have been compiled the first time you open a project that uses them. So what? Inconvenient? I don't think so, at least not nearly as much as having to always chase the right drivers.
  12. Definitely agree that it should have been done like that many moons ago already. But it was in a way maybe easier to do it like that as there was limited testing needed and maybe there was also a bit of an intention behind it as if you wanted to use new hardware you had to install the new driver and that made also a new IDE version necessary if the previous one was to old. So a bit of a hidden upgrade nudge. Now with the new NI structure where the different software departments are very much decoupled and independent from each other it proved to be an unmanageable modus operandi. It's not feasible anymore to have to pester DAQmx (and cRIO and System Control and, and, and) folks to release a new version of their software simply because there is a new LabVIEW version.
  13. Unless your data strream consists of very large packets in the beginning, there is absolutely no guarantee that your PlayM4 stream already has enough data the first time around to produce a valid JPG image. It is very well possible that you must feed it with a dozen or more compressed data packages before it is able to even create a single full frame image. And until then the GetJPEG function will likely return false as it has no data to return yet. Once it has enough data stored internally it may return a valid image but not in the buffer size that you passed to the IntputData method. That image will typically require quite a bit more bytes. What I did mean to do is to call GetJPEG() first with a null pointer for the data and 0 for nBufSize and then use the return value of pJPEGSize to allocate a big enough buffer to call the function again This may or may not work (again!) depending on the PlayM4 API functionality. Currently you are anyhow creating a crasho-mobil (again!!) since you pass in an empty byte array and have not configured that parameter to have a Minumum Size that is nBufSize. So you tell the functon: Look I have here a buffer and it contains at least nBufSize bytes, but you really pass in a buffer that contains 0 bytes. Kabom! Crash! as soon as the function has something to write into that buffer and tries to do that, since you told it that there is a buffer of a certain size it can write into. I have no idea what you try to say with this. If you mean to create an array constant with that size, you can but it is useless. Either you use Initialize Array to create an array of the necessary size or you configure in the Call Library Node the Minimum Size parameter of your array to be that of one of the other parameters, (why not use the nBufSize parameter here, as it very accidentally tells the function what size the byte array parameter has been allocated to). I mean it is just an idea, but it would seem logical to me. 😃
  14. You definitely will need the PlayM4_InputData(). Somehow you must provide the decoder with the continuous stream of data packages. Then it may or may not choke on the NULL HWND and it may or may not store the decoded data somewhere internal in the port and if it doesn't choke on the NULL HWND AND does store the decoded data somewhere in the port, you can call the PlayM4_GetJPEG() data. But of course you need to provide a large enough buffer to it, so MAYBE you can first call it with a NULL pJpeg and nBufSize = 0 and use the pJpegSize to call it again with a properly allocated buffer. Lots of may and may not and the only one who can find out about that is you!
  15. Not sure why you respond in this thread with this. But yes that has always been like this. Except that the locking goes away if you remove the reference to the class, or library from any but one of the targets.
  16. What's the calling convention you set for these functions? And no, you can calculate that multiplication by hand, your hand calculator, an abacus or explicitly in LabVIEW and wire that result to the Call Library Node. It does NOT matter. But you will want to store that "port" value somewhere (maybe in a shift register, it's just a suggestion) to use when sending the actual data packages to the decoder.
  17. NO! Configure that parameter simply as Array, Element Type U8, Pass as Data Pointer! But seriously you should first learn more C before trying to go further on this! And there is no need to convert the byte array into a string to determine its length to pass to the DLL function. There is also an Array Size function, you know?
  18. I'm saying you send NOTHING there. This is the definition of the user event datatype and what you "send" there into that cluster makes absolutely no difference. You could just as well leave the entire Bundle by Name away. What is important is the data type that the LVEventData element defines, nothing else. And this data type must at ALL times match the data type setup on the C side for the user event data structure or VERY BAD things will happen.
  19. Of course, and where did you get that header from? It can never compile in any C compiler that I know of. But it would help if you use generally accepted terms in your communication. A CRASH is something that occurs AFTER you have COMPILED and LINKED your application and start it. I usually means that your process dies with or without an according error message. What you encountered was a COMPILE ERROR since the compiler could not understand the code statement.
  20. That makes doubly no sense. Or you need to specify which dwDatatype you mean. First as dadreamer already explained and I tried to explain at least twice before. Second,which dwDataType are you talking about? The dwDataType in the callback routine is not something you should change at all. It is what the SDK driver returns to you and that's it. That is the definition of a callback, it is called by the other software component with values that you should use, not manipulate unless the callback function documentation states explicitly that you are expected to return some data in one or more of the passed in buffers.
  21. And as you can see in the code, the error value is 5000 + whatever the NET_DVR_GetLastError() returned. Now you go into the SDK documentation and read what error 17 means. So yes the function thinks that one of the parameters is not valid. The callback function pointer can't really be it, neither can it say anything about the callback data parameter, so that leaves only the session handle. I have no idea why it thinks that is invalid.
  22. The exact value of the session variable is not to be interpreted in any way other than what the API documentation states. And in there it says: It's a bit unclear thanks to the non-native people who wrote that SDK. But I interprete that as follows. This specifically means that a value of 0 should be also valid. Typically these "handles" are simply an index into an API private array of structures that contain the actual data to manage the session/connection/resource. Just changing the definition of the Call Library Node is NOT sufficient. You also need to change the C code to actually return a specific value!!!!! When you define the Call Library node for SetCbState() to return a value, the implementation of that funciton in your C code must explicitly return a value or the Call Library Node will simply return a random value (more specifically: whatever the function left dangling in the eax register of the CPU).
  23. Did you change the function to actually return a value? In the last code you posted the SetCbState() function returns void. That means you can configure the Call Library Node to return an integer value, but what it will return is random data, or more precisely whatever the function was last using the eax register for. Since it is declared to have a void return value, the compiler does not make sure to return anything meaningful in there. I would change the actual InstallCallback function as follows: //If the above if condition does not occur, the LVUserEventRef here does not take a value. typedef BOOL(__stdcall* Type_SetStandardDataCallBack)(LONG lRealHandle, void(CALLBACK* fStdDataCallBack) (LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, DWORD dwUser), DWORD dwUser); extern "C" __declspec(dllexport) MgErr __cdecl InstallStandardCallback(LONG lRealHandle, LVUserEventRef * refnum) { HMODULE hDLL = LoadLibraryW(L"HCNetSDK.dll"); if (hDLL) { Type_SetStandardDataCallBack installFunc = (Type_SetStandardDataCallBack)GetProcAddress(hDLL, "NET_DVR_SetStandardDataCallBack"); if (installFunc) { BOOL retval; if (refnum && *refnum) retval = installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum)); else retval = installFunc(lRealHandle, NULL, 0); if (retval) return mgNoErr; else return 5000 + NET_DVR_GetLastError(); } FreeLibrary(hDLL); } return mgNotSupported; }
  24. I had some suspicion in this direction. 😃 Anyone doing this for money would either have given up long ago because the available budget was running out very quickly or having been shot by the client for not delivering on his promises. 😆 But it doesn't mean that it makes the work more feasible.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.