Re: [FFmpeg-devel] [PATCH V3] avcodec/libvpxenc: add VP8 support for ROI-based encoding
> -Original Message- > From: ffmpeg-devel [mailto:ffmpeg-devel-boun...@ffmpeg.org] On Behalf > Of James Zern > Sent: Friday, March 08, 2019 9:53 AM > To: FFmpeg development discussions and patches de...@ffmpeg.org> > Subject: Re: [FFmpeg-devel] [PATCH V3] avcodec/libvpxenc: add VP8 > support for ROI-based encoding > > On Thu, Feb 28, 2019 at 11:23 PM Guo, Yejun wrote: > > > > Signed-off-by: Guo, Yejun > > --- > > libavcodec/libvpxenc.c | 150 > + > > 1 file changed, 150 insertions(+) > > > > diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c > > index c823b8a..fc9b1fd 100644 > > --- a/libavcodec/libvpxenc.c > > +++ b/libavcodec/libvpxenc.c > > @@ -1057,6 +1057,153 @@ static int queue_frames(AVCodecContext > *avctx, AVPacket *pkt_out) > > return size; > > } > > > > +static int vp8_encode_set_roi(AVCodecContext *avctx, const AVFrame > *frame) > > +{ > > +/* range of vpx_roi_map_t.delta_q[i] is [-63, 63] */ > > +#define MAX_DELTA_Q 63 > > + > > +const AVRegionOfInterest *roi = NULL; > > +vpx_roi_map_t roi_map; > > +int ret = 0; > > +int nb_rois; > > +AVFrameSideData *sd = av_frame_get_side_data(frame, > AV_FRAME_DATA_REGIONS_OF_INTEREST); > > +VPxContext *ctx = avctx->priv_data; > > +vpx_active_map_t active_map = { 0, 0, 0 }; > > { 0 } will work. thanks, will fix. > > > +int max_segment_cnt = 4;/* VP8 ROI only support 4 segments. */ > > +int segment_id = 0; > > + > > +/* record the mapping from delta_q to "segment id + 1". > > + * delta_q is shift with MAX_DELTA_Q, and so the range is [0, > 2*MAX_DELTA_Q]. > > + * add 1 to segment id, so no mapping if the value of array element is > zero. > > + */ > > +int segment_mapping[2 * MAX_DELTA_Q + 1] = {0}; > > + > > { 0 } would be more consistent with this file. will fix > > > +/* segment id 0 in roi_map is reserved for the areas not covered by > AVRegionOfInterest. > > + * segment id 0 in roi_map is also for the areas with > AVRegionOfInterest.qoffset near 0. > > + */ > > +segment_mapping[MAX_DELTA_Q] = segment_id + 1; > > +segment_id++; > > + > > +active_map.rows = (frame->height + 15) / 16; > > +active_map.cols = (frame->width + 15) / 16; > > + > > +if (!sd) { > > +/* For the case that some middle frames do not have ROI while other > frames have ROIs. > > + * Due to libvpx behaivor, we have to reset VP8E_SET_ACTIVEMAP, > otherwise the previous > > behavior will fix > > > + * ROIs continue working for frames without ROIs. > > + */ > > +if (vpx_codec_control(>encoder, VP8E_SET_ACTIVEMAP, > _map)) { > > +log_encoder_error(avctx, "Failed to set VP8E_SET_ACTIVEMAP > codec control.\n"); > > +return AVERROR_INVALIDDATA; > > +} > > +return 0; > > +} > > + > > +memset(_map, 0, sizeof(roi_map)); > > +roi_map.rows = active_map.rows; > > +roi_map.cols = active_map.cols; > > +roi_map.roi_map = av_mallocz(roi_map.rows * roi_map.cols); > > +if (!roi_map.roi_map) { > > +av_log(avctx, AV_LOG_ERROR, "roi_map alloc failed.\n"); > > +ret = AVERROR(ENOMEM); > > +goto fail; > > +} > > + > > +active_map.active_map = av_malloc(active_map.rows * > active_map.cols); > > +if (!active_map.active_map) { > > +av_log(avctx, AV_LOG_ERROR, "active_map alloc failed.\n"); > > +ret = AVERROR(ENOMEM); > > +goto fail; > > +} > > +/* set 1 to enable the corresponding element of > vpx_roi_map_t.roi_map. */ > > +memset(active_map.active_map, 1, active_map.rows * > active_map.cols); > > + > > +roi = (const AVRegionOfInterest*)sd->data; > > +if (!roi->self_size || sd->size % roi->self_size != 0) { > > +av_log(ctx, AV_LOG_ERROR, "Invalid > AVRegionOfInterest.self_size.\n"); > > +ret = AVERROR(EINVAL); > > +goto fail; > > +} > > +nb_rois = sd->size / roi->self_size; > > + > > +/* This list must be iterated from zero because regions are > > + * defined in order of decreasing importance. So discard less > > + * important areas if exceeding the supported segment count. > > ...if they exceed the se
Re: [FFmpeg-devel] [PATCH V3] avcodec/libvpxenc: add VP8 support for ROI-based encoding
On Thu, Feb 28, 2019 at 11:23 PM Guo, Yejun wrote: > > Signed-off-by: Guo, Yejun > --- > libavcodec/libvpxenc.c | 150 > + > 1 file changed, 150 insertions(+) > > diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c > index c823b8a..fc9b1fd 100644 > --- a/libavcodec/libvpxenc.c > +++ b/libavcodec/libvpxenc.c > @@ -1057,6 +1057,153 @@ static int queue_frames(AVCodecContext *avctx, > AVPacket *pkt_out) > return size; > } > > +static int vp8_encode_set_roi(AVCodecContext *avctx, const AVFrame *frame) > +{ > +/* range of vpx_roi_map_t.delta_q[i] is [-63, 63] */ > +#define MAX_DELTA_Q 63 > + > +const AVRegionOfInterest *roi = NULL; > +vpx_roi_map_t roi_map; > +int ret = 0; > +int nb_rois; > +AVFrameSideData *sd = av_frame_get_side_data(frame, > AV_FRAME_DATA_REGIONS_OF_INTEREST); > +VPxContext *ctx = avctx->priv_data; > +vpx_active_map_t active_map = { 0, 0, 0 }; { 0 } will work. > +int max_segment_cnt = 4;/* VP8 ROI only support 4 segments. */ > +int segment_id = 0; > + > +/* record the mapping from delta_q to "segment id + 1". > + * delta_q is shift with MAX_DELTA_Q, and so the range is [0, > 2*MAX_DELTA_Q]. > + * add 1 to segment id, so no mapping if the value of array element is > zero. > + */ > +int segment_mapping[2 * MAX_DELTA_Q + 1] = {0}; > + { 0 } would be more consistent with this file. > +/* segment id 0 in roi_map is reserved for the areas not covered by > AVRegionOfInterest. > + * segment id 0 in roi_map is also for the areas with > AVRegionOfInterest.qoffset near 0. > + */ > +segment_mapping[MAX_DELTA_Q] = segment_id + 1; > +segment_id++; > + > +active_map.rows = (frame->height + 15) / 16; > +active_map.cols = (frame->width + 15) / 16; > + > +if (!sd) { > +/* For the case that some middle frames do not have ROI while other > frames have ROIs. > + * Due to libvpx behaivor, we have to reset VP8E_SET_ACTIVEMAP, > otherwise the previous behavior > + * ROIs continue working for frames without ROIs. > + */ > +if (vpx_codec_control(>encoder, VP8E_SET_ACTIVEMAP, > _map)) { > +log_encoder_error(avctx, "Failed to set VP8E_SET_ACTIVEMAP codec > control.\n"); > +return AVERROR_INVALIDDATA; > +} > +return 0; > +} > + > +memset(_map, 0, sizeof(roi_map)); > +roi_map.rows = active_map.rows; > +roi_map.cols = active_map.cols; > +roi_map.roi_map = av_mallocz(roi_map.rows * roi_map.cols); > +if (!roi_map.roi_map) { > +av_log(avctx, AV_LOG_ERROR, "roi_map alloc failed.\n"); > +ret = AVERROR(ENOMEM); > +goto fail; > +} > + > +active_map.active_map = av_malloc(active_map.rows * active_map.cols); > +if (!active_map.active_map) { > +av_log(avctx, AV_LOG_ERROR, "active_map alloc failed.\n"); > +ret = AVERROR(ENOMEM); > +goto fail; > +} > +/* set 1 to enable the corresponding element of vpx_roi_map_t.roi_map. */ > +memset(active_map.active_map, 1, active_map.rows * active_map.cols); > + > +roi = (const AVRegionOfInterest*)sd->data; > +if (!roi->self_size || sd->size % roi->self_size != 0) { > +av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n"); > +ret = AVERROR(EINVAL); > +goto fail; > +} > +nb_rois = sd->size / roi->self_size; > + > +/* This list must be iterated from zero because regions are > + * defined in order of decreasing importance. So discard less > + * important areas if exceeding the supported segment count. ...if they exceed the segment count > + */ > +for (int i = 0; i < nb_rois; i++) { > +int qoffset; > +int mapping_index; > + > +roi = (const AVRegionOfInterest*)(sd->data + roi->self_size * i); > +if (roi->qoffset.den == 0) { > +av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.qoffset.den must > not be zero.\n"); > +ret = AVERROR(EINVAL); > +goto fail; > +} > + > +qoffset = (int)(roi->qoffset.num * 1.0f / roi->qoffset.den * > MAX_DELTA_Q); > +qoffset = av_clip(qoffset, -MAX_DELTA_Q, MAX_DELTA_Q); > + > +mapping_index = qoffset + MAX_DELTA_Q; > +if (!segment_mapping[mapping_index]) { > +if (segment_id > max_segment_cnt - 1) { > +av_log(ctx, AV_LOG_WARNING, > + "ROI only support %d segments (and segment 0 is > reserved for non-ROIs), skipping this one.\n", supports > + max_segment_cnt); > +roi = (AVRegionOfInterest*)((char*)roi + roi->self_size); > +continue; > +} > + > +segment_mapping[mapping_index] = segment_id + 1; > +roi_map.delta_q[segment_id] = qoffset; > +segment_id++; > +} > +} > + > +/* This list
[FFmpeg-devel] [PATCH V3] avcodec/libvpxenc: add VP8 support for ROI-based encoding
Signed-off-by: Guo, Yejun --- libavcodec/libvpxenc.c | 150 + 1 file changed, 150 insertions(+) diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index c823b8a..fc9b1fd 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -1057,6 +1057,153 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out) return size; } +static int vp8_encode_set_roi(AVCodecContext *avctx, const AVFrame *frame) +{ +/* range of vpx_roi_map_t.delta_q[i] is [-63, 63] */ +#define MAX_DELTA_Q 63 + +const AVRegionOfInterest *roi = NULL; +vpx_roi_map_t roi_map; +int ret = 0; +int nb_rois; +AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); +VPxContext *ctx = avctx->priv_data; +vpx_active_map_t active_map = { 0, 0, 0 }; +int max_segment_cnt = 4;/* VP8 ROI only support 4 segments. */ +int segment_id = 0; + +/* record the mapping from delta_q to "segment id + 1". + * delta_q is shift with MAX_DELTA_Q, and so the range is [0, 2*MAX_DELTA_Q]. + * add 1 to segment id, so no mapping if the value of array element is zero. + */ +int segment_mapping[2 * MAX_DELTA_Q + 1] = {0}; + +/* segment id 0 in roi_map is reserved for the areas not covered by AVRegionOfInterest. + * segment id 0 in roi_map is also for the areas with AVRegionOfInterest.qoffset near 0. + */ +segment_mapping[MAX_DELTA_Q] = segment_id + 1; +segment_id++; + +active_map.rows = (frame->height + 15) / 16; +active_map.cols = (frame->width + 15) / 16; + +if (!sd) { +/* For the case that some middle frames do not have ROI while other frames have ROIs. + * Due to libvpx behaivor, we have to reset VP8E_SET_ACTIVEMAP, otherwise the previous + * ROIs continue working for frames without ROIs. + */ +if (vpx_codec_control(>encoder, VP8E_SET_ACTIVEMAP, _map)) { +log_encoder_error(avctx, "Failed to set VP8E_SET_ACTIVEMAP codec control.\n"); +return AVERROR_INVALIDDATA; +} +return 0; +} + +memset(_map, 0, sizeof(roi_map)); +roi_map.rows = active_map.rows; +roi_map.cols = active_map.cols; +roi_map.roi_map = av_mallocz(roi_map.rows * roi_map.cols); +if (!roi_map.roi_map) { +av_log(avctx, AV_LOG_ERROR, "roi_map alloc failed.\n"); +ret = AVERROR(ENOMEM); +goto fail; +} + +active_map.active_map = av_malloc(active_map.rows * active_map.cols); +if (!active_map.active_map) { +av_log(avctx, AV_LOG_ERROR, "active_map alloc failed.\n"); +ret = AVERROR(ENOMEM); +goto fail; +} +/* set 1 to enable the corresponding element of vpx_roi_map_t.roi_map. */ +memset(active_map.active_map, 1, active_map.rows * active_map.cols); + +roi = (const AVRegionOfInterest*)sd->data; +if (!roi->self_size || sd->size % roi->self_size != 0) { +av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n"); +ret = AVERROR(EINVAL); +goto fail; +} +nb_rois = sd->size / roi->self_size; + +/* This list must be iterated from zero because regions are + * defined in order of decreasing importance. So discard less + * important areas if exceeding the supported segment count. + */ +for (int i = 0; i < nb_rois; i++) { +int qoffset; +int mapping_index; + +roi = (const AVRegionOfInterest*)(sd->data + roi->self_size * i); +if (roi->qoffset.den == 0) { +av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.qoffset.den must not be zero.\n"); +ret = AVERROR(EINVAL); +goto fail; +} + +qoffset = (int)(roi->qoffset.num * 1.0f / roi->qoffset.den * MAX_DELTA_Q); +qoffset = av_clip(qoffset, -MAX_DELTA_Q, MAX_DELTA_Q); + +mapping_index = qoffset + MAX_DELTA_Q; +if (!segment_mapping[mapping_index]) { +if (segment_id > max_segment_cnt - 1) { +av_log(ctx, AV_LOG_WARNING, + "ROI only support %d segments (and segment 0 is reserved for non-ROIs), skipping this one.\n", + max_segment_cnt); +roi = (AVRegionOfInterest*)((char*)roi + roi->self_size); +continue; +} + +segment_mapping[mapping_index] = segment_id + 1; +roi_map.delta_q[segment_id] = qoffset; +segment_id++; +} +} + +/* This list must be iterated in reverse, so for the case that + * two regions overlapping, the more important area takes effect. + */ +for (int i = nb_rois - 1; i >= 0; i--) { +int qoffset; +int mapping_value; +int starty, endy, startx, endx; + +roi = (const AVRegionOfInterest*)(sd->data + roi->self_size * i); + +starty = FFMIN(roi_map.rows, roi->top / 16); +endy = FFMIN(roi_map.rows,