Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple levels of pointers #82

Open
ychakiris opened this issue Aug 9, 2017 · 10 comments
Open

multiple levels of pointers #82

ychakiris opened this issue Aug 9, 2017 · 10 comments

Comments

@ychakiris
Copy link

I have been able to autowrap all (or most all) of the ffmpeg libraries that are used in ffplay. Took a few attempts to get it right but it works!

The next issue is that ffplay uses a number of expressions that look like:

pointer_1->pointer_2->pointer_3
another_ptr_1->another_ptr_2[3]->another_ptr_3
some_function(&some_pointer, ...)

I am sure these can be handled--somehow--however an easy way to do so has not emerged from my experimentation. Is there an easy way to do this?

@rpav
Copy link
Owner

rpav commented Aug 9, 2017

This is generally where the plus-c stuff is meant to help out, so instead of a large inverted chain of accessors, you have something like:

(c-with ((x some-type)
         (y some-type))
  (setf (x :ref1 :ref2) 0)
  (setf (y :array 3 :ref2) 1)

  (some-function (x &)))

This works mostly like C, but note the few cases where the "unified" syntax has different semantics.

@ychakiris
Copy link
Author

Thanks for the response!!

Sorry, still a bit confused.

Here are some excerpts from a tutorial to ffplay. The code was fixed a bit since I am using a later version of ffmpeg.

Here are some Declarations

AVFormatContext    *pFormatCtx;
int  i, videoStreamIdx;
AVCodecContext        *pCodecCtx;
AVCodecParameters  *pCodecPar;
AVCodec            *pCodec;
AVFrame            *pFrame;
AVFrame            *pFrameRGB;
AVPacket           packet;

Now I already have wrapped the AVFormatContext, AVCodecContext, ... using c-include. Worked great!

Easy code to imitate in Common Lisp (using autowrap)

Here is the first part of the code I am trying to imitate within Common Lisp that is very straightforward.

// Register all formats and codecs
    av_register_all();
    pFormatCtx = avformat_alloc_context();
    if (!pFormatCtx) {
        return -1;
    }

    /// Open video file
    //if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0) // Deprecated
    // if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
    //    return -1; // Couldn't open file
   // had to use a "fake" function so easier to imitate in Common Lisp
    if(fake_avformat_open_input(pFormatCtx, argv[1]) != 0)
      return -1;

    /// Retrieve stream information
    //if(av_find_stream_info(pFormatCtx)<0) // Deprecated
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
        return -1; // Couldn't find stream information


    /// Dump information about file onto standard error
    av_dump_format(pFormatCtx, 0, argv[1], 0);

Lisp Imitation of "easy part"

(av-register-all)
(setq pFormatCtx  (avformat-alloc-context))

(fake-avformat-open-input pformatctx "/home/yitzchok/quicklisp/local-projects/cl-autowrap/t-ffmpeg/tutorial/test_data/1.avi")
(avformat-find-stream-info pformatctx (cffi:null-pointer))
(av-dump-format pformatctx 0 "tutorial01" 0)

Harder part to Imitate

Now for the first part of the problematic part:

  videoStreamIdx=-1;
    for(i=0; i<pFormatCtx->nb_streams; i++)
        if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { //CODEC_TYPE_VIDEO
            videoStreamIdx=i;
            break;
        }

The hard part for me is this part: pFormatCtx->streams[i]->codecpar->codec_type

@rpav
Copy link
Owner

rpav commented Aug 9, 2017

Try something like the following; of course I'm making assumptions about constant/field/type naming and similar based on autowrap conventions:

;;; Note this uses :ptr to use the allocation right here; you could
;;; pass around a wrapper and use :from or `c-val`, see the docs
(let ((video-stream-idx -1))
  (c-let ((p-format-ctx av-format-context :ptr (avformat-alloc-context)))
    (loop for i from 0 below (p-format-ctx :nb-streams)
          do (when (eql
                    (p-format-ctx :streams i :codecpar :codec-type)
                    +AVMEDIA-TYPE-VIDEO+)
               (setf video-stream-idx i)
               (return)))
    (format t "I found ~A" video-stream-idx)))


;;; As a function, presumably:
(defun find-video-stream-idx (ctx)
  (c-val ((ctx av-format-context))
    (loop for i from 0 below (ctx :nb-streams)
          do (when (eql (ctx :streams i :codecpar :codec-type) +AVMEDIA-TYPE-VIDEO+)
               (return i)))))

@ychakiris
Copy link
Author

Didn't work. Will try to post the error messages next week.

@ychakiris
Copy link
Author

ychakiris commented Aug 11, 2017

Having looked at the source code for SBCL and CFFI, it seems the SB-ALIEN package has a richer set of operations on foreign types. For example SB-ALIEN:addr that mirrors &some_variable in c. CFFI doesn't seem to use SB-ALIEN directly but rather SB-SYS (that may in turn use SB-ALIEN). I don't think CFFI has something comparable to SB-ALIEN:addr.

Since ffmpeg has such complex c-ish manipulations perhaps the best approach for me is:

  • simplify the c code so that cl-autowrap works better with it or, alternatively,
  • use SB-ALIEN directly

@rpav
Copy link
Owner

rpav commented Aug 11, 2017

Using cl-plus-c, this is exactly what (foo &) does. Note, you need to either (use-package :plus-c) or directly reference &, since it's not a symbol in CL.

In CFFI directly, you're always working with pointers, so you don't need to take the address of anything .. that's all you've got. With cl-plus-c, it makes things act a bit more like C values, so taking the address works analogously.

@ychakiris
Copy link
Author

ychakiris commented Feb 14, 2018

This is the answer I was looking for: https://stackoverflow.com/questions/35841771/common-lisp-cffi-pointer-to-the-pointer. See the last paragraph

(let* (;; a float
       (v0 32s0)

       ;; a pointer to a float foreign memory
       (p0 (cffi:foreign-alloc :float :initial-element v0))) 

  ;; a new pointer
  (cffi:with-foreign-object (p1 :pointer)

    ;; make the new pointer point to the first pointer
    (setf (cffi:mem-aref p1 :pointer) p0)

    ;; dereferencing twice should give you the original number
    (cffi:mem-aref (cffi:mem-aref p1 :pointer) :float)))

I noticed in an earlier version of cl-autowrap you must of used cffi. See this gist of yours: https://gist.github.com/rpav/5710083.

Is the suggestion from the answer (quoted above) doable with sffi?

@ychakiris
Copy link
Author

Also have you looked at javacpp? it does a very nice job of not only wrapping the c-c++ code, it also writes out the wrapping file with all the documentation from the original include file. Very useful so that one does not have to hunt around for the significance of this or that in other places.

@ychakiris
Copy link
Author

Now that I am reading more closely your code in parse.lisp (very clever and clean macros!), it seems to me that one could imitate javacpp a bit more (at least in what it does) and also put in options to control the output: i) the same as you do now (basically a macro that gets evaluated and leaves no trace); ii) an output file that is compiled where one sees all the details of the wrapping; iii) also has all the documentation from the original include file. It seems to me that c2ffi probably could (or maybe it does already) also capture any comments from the include file.

@ychakiris
Copy link
Author

There might also be options to use cffi instead of sffi.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants