On 27/02/2019 10:22, Gyan wrote:
> On 27-02-2019 01:57 PM, Guo, Yejun wrote:
>>
>>> -----Original Message-----
>>> From: ffmpeg-devel [mailto:ffmpeg-devel-boun...@ffmpeg.org] On Behalf
>>> Of Guo, Yejun
>>> Sent: Thursday, February 28, 2019 12:13 AM
>>> To: ffmpeg-devel@ffmpeg.org
>>> Subject: [FFmpeg-devel] [PATCH] avcodec/libvpxenc: add VP8 support for
>>> ROI-based encoding
>>>
>>> Signed-off-by: Guo, Yejun <yejun....@intel.com>
>>> ---
>>>   libavcodec/libvpxenc.c | 132
>>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>>   1 file changed, 132 insertions(+)
>>>
>>> diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c
>>> index c823b8a..e3de547 100644
>>> --- a/libavcodec/libvpxenc.c
>>> +++ b/libavcodec/libvpxenc.c
>>> @@ -1057,6 +1057,135 @@ 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
>>> +
>>> +    AVRegionOfInterest *roi = NULL;
>>> +    vpx_roi_map_t roi_map;
>>> +    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 segment_id;
>>> +    int zero_delta_q = 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};
>>> +
>>> +    active_map.rows = (frame->height + 15) / 16;
>>> +    active_map.cols = (frame->width  + 15) / 16;
>>> +
>>> +    if (!sd) {
>>> +        if (vpx_codec_control(&ctx->encoder, VP8E_SET_ACTIVEMAP,
>>> &active_map)) {
>>> +            log_encoder_error(avctx, "Failed to set VP8E_SET_ACTIVEMAP 
>>> codec
>>> control.\n");
>>> +            return AVERROR_INVALIDDATA;
>>> +        }
>>> +        return 0;
>>> +    }
>>> +
>>> +    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 (%d bytes) failed.\n",
>>> +               active_map.rows * active_map.cols);
>>> +        return AVERROR(ENOMEM);
>>> +    }
>>> +    memset(active_map.active_map, 1, active_map.rows * active_map.cols);
>>> +
>>> +    /* 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_id = 0;
>>> +    segment_mapping[zero_delta_q + MAX_DELTA_Q] = segment_id + 1;
>>> +    segment_id++;
>>> +    memset(&roi_map, 0, sizeof(roi_map));
>>> +    roi_map.delta_q[segment_id] = zero_delta_q;
>>> +
>>> +    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_free(active_map.active_map);
>>> +        av_log(avctx, AV_LOG_ERROR,
>>> +               "roi_map alloc (%d bytes) failed.\n",
>>> +               roi_map.rows * roi_map.cols);
>>> +        return AVERROR(ENOMEM);
>>> +    }
>>> +
>>> +    roi = (AVRegionOfInterest*)sd->data;
>>> +    while ((uint8_t*)roi < sd->data + sd->size) {
>>> +        int qoffset;
>>> +        int mapping_index;
>>> +        int mapping_value;
>>> +        int starty = FFMIN(roi_map.rows, roi->top / 16);
>>> +        int endy   = FFMIN(roi_map.rows, (roi->bottom + 15) / 16);
>>> +        int startx = FFMIN(roi_map.cols, roi->left / 16);
>>> +        int endx   = FFMIN(roi_map.cols, (roi->right + 15) / 16);
>>> +
>>> +        if (roi->self_size == 0) {
>>> +            av_free(active_map.active_map);
>>> +            av_free(roi_map.roi_map);
>>> +            av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.self_size must be
>>> set to sizeof(AVRegionOfInterest).\n");
>>> +            return AVERROR(EINVAL);
>>> +        }
>>> +
>>> +        if (roi->qoffset.den == 0) {
>>> +            av_free(active_map.active_map);
>>> +            av_free(roi_map.roi_map);
>>> +            av_log(ctx, AV_LOG_ERROR, "AVRegionOfInterest.qoffset.den must
>>> not be zero.\n");
>>> +            return AVERROR(EINVAL);
>>> +        }
>>> +
>>> +        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 > 3) {
>>> +                av_log(ctx, AV_LOG_WARNING,
>>> +                       "ROI only support 4 segments (and segment 0 is 
>>> reserved for
>>> non-ROIs), skipping this one.\n");
>>> +                roi = (AVRegionOfInterest*)((char*)roi + roi->self_size);
>>> +                continue;
>>> +            }
>>> +
>>> +            segment_mapping[mapping_index] = segment_id + 1;
>>> +            roi_map.delta_q[segment_id] = qoffset;
>>> +            segment_id++;
>>> +        }
>>> +
>>> +        mapping_value = segment_mapping[mapping_index];
>>> +
>>> +        for (int y = starty; y < endy; y++)
>>> +            for (int x = startx; x < endx; x++)
>>> +                roi_map.roi_map[x + y * roi_map.cols] = mapping_value - 1;
>>> +
>>> +        roi = (AVRegionOfInterest*)((char*)roi + roi->self_size);
>>> +    }
>>> +
>>> +    if (vpx_codec_control(&ctx->encoder, VP8E_SET_ROI_MAP, &roi_map))
>>> {
>>> +        av_free(active_map.active_map);
>>> +        av_free(roi_map.roi_map);
>>> +        log_encoder_error(avctx, "Failed to set VP8E_SET_ROI_MAP codec
>>> control.\n");
>>> +        return AVERROR_INVALIDDATA;
>>> +    }
>>> +
>>> +    if (vpx_codec_control(&ctx->encoder, VP8E_SET_ACTIVEMAP,
>>> &active_map)) {
>>> +        av_free(active_map.active_map);
>>> +        av_free(roi_map.roi_map);
>>> +        log_encoder_error(avctx, "Failed to set VP8E_SET_ACTIVEMAP codec
>>> control.\n");
>>> +        return AVERROR_INVALIDDATA;
>>> +    }
>>> +
>>> +    av_free(active_map.active_map);
>>> +    av_free(roi_map.roi_map);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt,
>>>                         const AVFrame *frame, int *got_packet)
>>>   {
>>> @@ -1113,6 +1242,9 @@ static int vpx_encode(AVCodecContext *avctx,
>>> AVPacket *pkt,
>>>                   flags |= strtoul(en->value, NULL, 10);
>>>               }
>>>           }
>>> +
>>> +        if (avctx->codec_id == AV_CODEC_ID_VP8)
>>> +            vp8_encode_set_roi(avctx, frame);
>>>       }
>>>
>>>       res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp,
>> to verify this patch, please cherry-pick 
>> https://github.com/guoyejun/ffmpeg/commit/7ba191ad7da5cfd1e6241d0a549c3c6b88c6b2f8,
>> and run command like:
>> ./ffmpeg -i .../path_to_1920x1080_video_file -vf scale=1920:1080 -c:v libvpx 
>> -b:v 5M -y tmp.webm
>>
> 
> Looks like, at present, the only way to effect ROI is via side data, and no 
> filter or other in-tree mechanism at present can convey or generate it.
> 
> Are there any near-term plans to add some? If not, may I suggest that you add 
> a private option for supporting encoders, for users to input ROIs?

I had a filter doing exactly this for testing - it adds the ROI side-data to 
frames, and can be applied multiple times to add multiple regions.  I've 
included it the set just sent, though I'm not sure how useful it actually is 
for non-test cases.  See 
<https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2019-February/240533.html>.

Thanks,

- Mark
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to