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

Add avfilter module/command #1038

Merged
merged 10 commits into from Jul 19, 2020
Merged

Add avfilter module/command #1038

merged 10 commits into from Jul 19, 2020

Conversation

mkurkov
Copy link
Contributor

@mkurkov mkurkov commented Jul 15, 2020

Hi,

Here is my attempt to add FFmpeg filter graphs integration to baresip.

Idea is to allow dynamically set video filter graph for current video stream. And then remove it when needed.
It's implemented as baresip filter module and command /avfilter. For example to enable vflip simple filter graph just call /avfilter vflip at any time and see how video is flipped. To disable filter graph just send /avfilter command without any filter string.
As more complex example you can apply some png overlay on top of current video stream with
/avfilter movie=/path/to/some/image.png[pic];[in][pic]overlay=(W-w)/2:H-h-20 command.
Full syntax and list of filters are available on FFmpeg Filtegraph's page: https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-description

I run into several issues during development:

1. Bottom-up FFmpeg frames.

It seems that FFmpeg frame structure could keep both top-down and bottom-up plane lines orders. In the last case
linesizes are negative and data pointers point to the last rows in the buffer. Baresip uses unsigned linesizes, so as I understand
it doesn't support bottom-up order. As workaround I had to rearrange plane lines in top-down order for such frames and
update linesizes/data pointers. So my question is how Baresip works with bottom-up frames and is my approach valid.

2. Moving data between AVFrame/vidframe structures

I wonder how properly work with FFmpeg AVFrame structure. Currently as I get new AVFrame from filter graph pipeline, I just
update incoming filter frame, setting data pointers and linesizes to AVframe data. It seems to work well and without any additional copying. Main question is when I supposed to unref AVFrame structure. Normally it should be done in the end of encoding pipeline, but filter module sits in the middle of pipeline and doesn't control it. Now I unref previous AVFrame before processing new one, assuming that previous encoding cycle is finished and it's safe to clean memory. Am I right or there is a more proper way to do it?

I don't have much experience with baresip modules and video processing, so any suggestions are greatly appreciated.

Thanks,
Mikhail

*
*/

static struct lock *lock = (void*)0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to initialise lock, static/global vars are inited to null.

int err;
err = lock_alloc(&lock);
if (err)
return ENOMEM;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should return err here, instead of ENOMEM


static int module_close(void)
{
mem_deref(lock);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also setting lock to NULL makes it more robust:

lock = mem_deref(lock)

#include <baresip.h>
#include "avfilter.h"
#include "util.h"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 empty lines between functions

snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:"
"time_base=%d/%d:pixel_aspect=1/1",
frame->size.w, frame->size.h, src_format, 1, 25);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time_base = 1/25 is hardcoded.

if you want to use the timestamps from the API you can use 1/1000000 as time_base.
(or VIDEO_TIMEBASE constant)

return err;
}

int filter_reset(struct avfilter_st* st)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 empty lines between functions

st->enabled = false;
info("avfilter: filter graph reset\n");
return 0;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since filter_reset always returns 0, you can change the return type to void

return 0;
}

bool filter_valid(struct avfilter_st* st, struct vidframe *frame)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the two input arguments can be made const

#include "util.h"

static int swap_lines(uint8_t *a, uint8_t *b, size_t size) {
uint8_t tmp[size];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is dangerous to declare a variable size array on the stack.

probably better to use a heap buffer, or a fixed size stack buffer

return 0;
}

static int reverse_lines(uint8_t *data, int linesize, int count) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curly brace at the end { should be moved to next line


/*
* Sometimes AVFrame contains planes with lines in bottom-up order.
* Then linesize is negative and data points to the last row in buffer.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sounds strange, I have never seen a negative linesize. Which plane was it (0 to 3) ?

the linesize is the number of pixels in a horizontal line, for each plane.
example: [320, 160, 160, 0]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For bottom-up frames all planes have negative linesizes, like this: linesize = {-1920, -960, -960, 0, 0, 0, 0, 0} (this is from gdb inspecting AVFrame structure).

return 0;
}

enum AVPixelFormat vidfmt_to_avpixfmt(const enum vidfmt fmt)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const is not needed

}
}

enum vidfmt avpixfmt_to_vidfmt(const enum AVPixelFormat pix_fmt)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const is not needed

src/config.c Outdated
@@ -806,6 +806,7 @@ int config_write_template(const char *file, const struct config *cfg)
(void)re_fprintf(f, "#module\t\t\t" "snapshot" MOD_EXT "\n");
(void)re_fprintf(f, "#module\t\t\t" "swscale" MOD_EXT "\n");
(void)re_fprintf(f, "#module\t\t\t" "vidinfo" MOD_EXT "\n");
(void)re_fprintf(f, "#module\t\t\t" "avfilter" MOD_EXT "\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use TAB for indentation

@alfredh
Copy link
Collaborator

alfredh commented Jul 17, 2020

hi @mkurkov many thanks for this good and useful piece of work. we will merge it to master.

regarding bottom-up frames, I have never seen negative linesize in the wild.
baresip linesize is only unsigned.

regarding copying of buffers, it is fine to update data pointers in struct vidframe to point
to the real buffers. the buffer should be valid until the vidfilt encode function has returned,
because the application will use the data. The next time the vidfilt encode function is called,
they can be overwritten with new pointers.

I tested this with ffmpeg 4.1.4 and it works just fine:

$ ./baresip -m avfilter.so -e/vidloop -e"/avfilter boxblur"

I added some review comments inline, please have a look

@alfredh
Copy link
Collaborator

alfredh commented Jul 18, 2020

please also add your name and the new module to this list:

https://github.com/baresip/baresip/wiki/Modules

@mkurkov
Copy link
Contributor Author

mkurkov commented Jul 19, 2020

@alfredh thanks for reviewing, will fix mentioned issues.

Regarding bottom-up frames, I run into them trying simple filters like hflip - it worked properly, but with vflip I constantly got segfault. Looking into AVFrame from vflip in gdb I saw this:

{data = {0x7fffe7675cc0 '\353' <repeats 199 times>, <incomplete sequence \353>..., 
    0x7fffe76f85a0 '\200' <repeats 200 times>..., 0x7fffe7777dc0 '\200' <repeats 200 times>..., 0x0, 0x0, 0x0, 0x0, 0x0}, 
  linesize = {-1920, -960, -960, 0, 0, 0, 0, 0}, extended_data = 0x7fffec0e7600, width = 1920, height = 1080,

I guess vflip switches frame to bottom-up as optimization. Looking at vflip implementation, it's visible that they also change data to point on last row in buffer:

static int filter_frame(AVFilterLink *link, AVFrame *frame)
{
     FlipContext *flip = link->dst->priv;
     int i;
 
     for (i = 0; i < 4; i ++) {
         int vsub = i == 1 || i == 2 ? flip->vsub : 0;
         int height = FF_CEIL_RSHIFT(link->h, vsub);
 
         if (frame->data[i]) {
             frame->data[i] += (height - 1) * frame->linesize[i];
             frame->linesize[i] = -frame->linesize[i];
         }
     }
 
     return ff_filter_frame(link->dst->outputs[0], frame);
}

Not sure if this could be a problem in other parts of Baresip, but avfilter needs to support bottom-up frames too.

@mkurkov
Copy link
Contributor Author

mkurkov commented Jul 19, 2020

@alfredh issues should be resolved in last commits.

@alfredh
Copy link
Collaborator

alfredh commented Jul 19, 2020

thanks

I get an error when compiling now:

  CC [M]  build-x86_64/modules/avfilter/filter.o
modules/avfilter/filter.c:119:3: error: void function 'filter_reset' should not return a value
      [-Wreturn-type]
                return 0;
                ^      ~
1 error generated.
make: *** [build-x86_64/modules/avfilter/filter.o] Error 1

@alfredh
Copy link
Collaborator

alfredh commented Jul 19, 2020

https://patchwork.ffmpeg.org/project/ffmpeg/patch/20170806190921.10897-1-cus@passwd.hu/

librem/baresip supports only positive linesize:

/** Video frame */
struct vidframe {
	uint8_t *data[4];      /**< Video planes        */
	uint16_t linesize[4];  /**< Array of line-sizes */
	struct vidsz size;     /**< Frame resolution    */
	enum vidfmt fmt;       /**< Video pixel format  */
};

this has worked fine for many years.

adding support for negative linesize, would mean updating and testing
all modules and code that is involved with video processing.

I think for now it is best that you work around it in avfilter.so

@alfredh alfredh merged commit 66daaf2 into baresip:master Jul 19, 2020
@alfredh
Copy link
Collaborator

alfredh commented Jul 19, 2020

thanks a lot for your contribution :)

wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request Nov 28, 2020
(Presumably the changelog [Unreleased] section is for 1.0.0)

= Baresip Changelog

== [Unreleased]

=== Added

- aac: add AAC_STREAMTYPE_AUDIO enum value
- aac: add AAC_ prefix
- Video mode param to call_answer(), ua_answer() and ua_hold_answer [#966]
- video_stop_display() API function [#977]
- module: add path to module_load() function
- conf: add conf_configure_buf
- test: add usage of g711.so module [#978]
- JSON initial codec state command and response [#973]
- account_set_video_codecs() API function [#981]
- net: add fallback dns nameserver [#996]
- gtk: show call_peername in notify title [#1006]
- call: Added call_state() API function that returns enum state of the call [#1013]
- account_set_stun_user() and account_set_stun_pass() API functions [#1015]
- API functions account_stun_uri and account_set_stun_uri. [#1018]
- ausine: Audio sine wave input module [#1021]
- gtk/menu: replace spaces from uri [#1007]
- jack: allowing jack client name to be specified in the config file [#1025] [#1020]
- snapshot: Add snapshot_send and snapshot_recv commands [#1029]
- webrtc_aec: 'extended_filter' config option [#1030]
- avfilter: FFmpeg filter graphs integration [#1038]
- reg: view proxy expiry value in reg_status [#1068]
- account: add parameter rwait for re-register interval [#1069]
- call, stream, menu: add cmd to set the direction of video stream [#1073]

=== Changed

- **Using [baresip/re](https://github.com/baresip/re) fork now**
- audio: move calculation to audio_jb_current_value
- avformat: clean up docs
- gzrtp: update docs
- account: increased size of audio codec list to 16
- video: make video_sdp_attr_decode public
- config: Derive default audio driver from default audio device [#1009]
- jack: modifying info message on jack client creation [#1019]
- call: when video stream is disabled, stop also video display [#1023]
- dtls_srtp: use tls_set_selfsigned_rsa with keysize 2048 [#1062] [#1056]
- rst: use a min ptime of 20ms
- aac: change ptime to 4ms

=== Fixed

- avcodec: fix H.264 interop with Firefox
- winwave: waveInGetPosition is no longer supported for use as of Windows Vista [#960]
- avcodec: call av_hwdevice_ctx_create before if-statement
- account: use single quote instead of backtick
- ice: fix segfault in connh [#980]
- call: Update call->got_offer when re-INVITE or answer to re-INVITE
  is received [#986]
- mk: Test also for /usr/lib64/libspeexdsp.so to cover Fedora/RHEL/CentOS [#992]
- config: Allow distribution specific CA trust bundle locations (fixes [#993]
- config: Allow distribution specific default audio device (fixes [#994]
- mqtt: fix err is never read (found by clang static analyzer)
- avcodec: fix err is never read (found by clang static analyzer)
- gtk: notification buttons do not work on Systems [#1012]
- gtk: fix dtmf_tone and add tones as feedback [#1010]
- pulse: drain pulse buffers before freeing [#1016]
- jack: jack_play connect all physical ports [#1028]
- Makefile: do not try to install modules if build is static [#1031]
- gzrtp: media_alloc function is missing [#1034] [#1022]
- call: when updating video, check if video stream has been disabled [#1037]
- amr: fix length check, fixes [#1011]
- modules: fix search path for avdevice.h [#1043]
- gtk: declare variables C89 style
- config: init newly added member
- menu: fix segfault in ua_event_handler [#1059] [#1061]
- debug_cmd: fix OpenSSL no-deprecated [#1065]
- aac: handle missing bitrate parameter in SDP format
- av1: properly configure encoder

=== Removed

- ice: remove support for ICE-lite
- ice: remove ice_debug, use log level DEBUG instead
- ice: make stun server optional
- config: remove ice_debug option (unused)

=== Contributors (many thanks)

- Alfred E. Heggestad
- Alexander Gramner
- Andrew Webster
- Christian Spielberger
- Christoph Huber
- Davide Alberani
- Ethan Funk
- Juha Heinanen
- mbattista
- Michael Malone
- Mikl Kurkov
- ndilieto
- Robert Scheck
- Roger Sandholm
- Sebastian Reimers

[#966]: baresip/baresip#966
[#977]: baresip/baresip#977
[#978]: baresip/baresip#978
[#973]: baresip/baresip#973
[#981]: baresip/baresip#981
[#996]: baresip/baresip#996
[#1006]: baresip/baresip#1006
[#1013]: baresip/baresip#1013
[#1015]: baresip/baresip#1015
[#1018]: baresip/baresip#1018
[#1021]: baresip/baresip#1021
[#1007]: baresip/baresip#1007
[#1025]: baresip/baresip#1025
[#1020]: baresip/baresip#1020
[#1029]: baresip/baresip#1029
[#1030]: baresip/baresip#1030
[#1038]: baresip/baresip#1038
[#1009]: baresip/baresip#1009
[#1019]: baresip/baresip#1019
[#1023]: baresip/baresip#1023
[#1062]: baresip/baresip#1062
[#1056]: baresip/baresip#1056
[#960]: baresip/baresip#960
[#980]: baresip/baresip#980
[#986]: baresip/baresip#986
[#992]: baresip/baresip#992
[#993]: baresip/baresip#993
[#994]: baresip/baresip#994
[#1012]: baresip/baresip#1012
[#1010]: baresip/baresip#1010
[#1016]: baresip/baresip#1016
[#1028]: baresip/baresip#1028
[#1031]: baresip/baresip#1031
[#1034]: baresip/baresip#1034
[#1022]: baresip/baresip#1022
[#1037]: baresip/baresip#1037
[#1011]: baresip/baresip#1011
[#1043]: baresip/baresip#1043
[#1059]: baresip/baresip#1059
[#1061]: baresip/baresip#1061
[#1065]: baresip/baresip#1065
[#1068]: baresip/baresip#1068
[#1069]: baresip/baresip#1069
[#1073]: baresip/baresip#1073

[Unreleased]: baresip/baresip@v0.6.6...HEAD
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

Successfully merging this pull request may close these issues.

None yet

2 participants