[FFmpeg-devel] [PATCH 2/2 v2] avcodec/mjpegdec: postpone calling ff_get_buffer() until the SOS marker
James Almer
jamrial at gmail.com
Sat May 1 18:18:02 EEST 2021
On 5/1/2021 12:06 PM, James Almer wrote:
> On 5/1/2021 11:30 AM, James Almer wrote:
>> On 5/1/2021 11:17 AM, Michael Niedermayer wrote:
>>> On Sat, May 01, 2021 at 09:41:55AM -0300, James Almer wrote:
>>>> On 5/1/2021 4:01 AM, Michael Niedermayer wrote:
>>>>> On Thu, Apr 22, 2021 at 02:02:33PM -0300, James Almer wrote:
>>>>>> With JPEG-LS PAL8 samples, the JPEG-LS extension parameters
>>>>>> signaled with
>>>>>> the LSE marker show up after SOF but before SOS. For those, the
>>>>>> pixel format
>>>>>> chosen by get_format() in SOF is GRAY8, and then replaced by PAL8
>>>>>> in LSE.
>>>>>> This has not been an issue given both pixel formats allocate the
>>>>>> second data
>>>>>> plane for the palette, but after the upcoming soname bump, GRAY8
>>>>>> will no longer
>>>>>> do that. This will result in segfauls when ff_jpegls_decode_lse()
>>>>>> attempts to
>>>>>> write the palette on a buffer originally allocated as a GRAY8 one.
>>>>>>
>>>>>> Work around this by calling ff_get_buffer() after the actual pixel
>>>>>> format is
>>>>>> known.
>>>>>
>>>>> What if the LSE occurs after the SOS ?
>>>>> What if there are 2 LSE ?
>>>>> It seems allowed by the specification
>>>>>
>>>>> "The LSE marker segment may be present where tables or
>>>>> miscellaneous marker segments may appear. If tables specified
>>>>> in this marker segment for a given table ID appear more than
>>>>> once, each specification shall replace the previous
>>>>> specification."
>>>>>
>>>>> Maybe iam missing something but a implemenattion of this would seem to
>>>>> require scanning through the image, finding all LSE and checking if
>>>>> they all
>>>>> are compatible with the PAL8 format before allocating the image in SOF
>>>>>
>>>>> The implemenattion here which delays allocation slightly seems to make
>>>>> the code much more unstable allowing the frame configuration or
>>>>> allocation
>>>>> to be partly done, double done, redone, without the matching partner.
>>>>> Also it does not seem to implement the related specification
>>>>> completely.
>>>>
>>>> Well, it was a hack to replace a previous hack (allocate a gray
>>>> buffer then
>>>> silently treat it as PAL8 once an LSE showed up). It's no wonder it
>>>> may not
>>>> perfectly follow the spec and consider all potential cases.
>>>
>>> yes and i wouldnt mind but the new hack is leading to crashes ...
>>> and the fixes to the crashes all in all start looking a bit ugly and
>>> iam not
>>> sure if any more remain. So id like to take a bit a step back here
>>> and see
>>> if there isnt a easier/cleaner solution.
>>>
>>>
>>>>
>>>>>
>>>>> A complete implemenattion may reqire RGB24 or 48 to be selected in
>>>>> some cases
>>>>> like when each scan uses its own palette, which unless iam missing
>>>>> something
>>>>> seems allowed.
>>>>> But then maybe iam missing something, in which case please correct me!
>>>>
>>>> You're probably not wrong, since unlike me you actually know this
>>>> code and
>>>> format. I tried to workaround an issue and it seemed to work and not
>>>> break
>>>> any file anyone here could test.
>>>>
>>>> If you think it's not good enough then just revert it and maybe
>>>> allocate the
>>>> data[1] pointer for the palette with av_buffer_alloc(),
>>>> get_buffer2() custom
>>>> implementations be damned. But i don't think the previous code even
>>>> did half
>>>> the things you listed above.
>>>
>>> how can i reproduce the issue you where trying to fix with this patch ?
>>
>> It's the crash you reported in
>> https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2021-April/279350.html
>>
>> The explanation of why it started crashing is in c8197f73e6.
>> Basically, since GRAY8 no longer unnecessarily allocates a fixed
>> palette, the old hack of changing pix_fmt from GRAY8 to PAL8 after
>> having called ff_get_buffer() with the former, editing the palette
>> plane buffer, and have things magically work is not possible anymore,
>> because no palette buffer was ever allocated. ff_get_buffer() is meant
>> to be called once the actual pix_fmt is known to correctly allocate
>> all plane buffers.
>
> Another possible solution instead of my suggestion above (revert and
> then manually allocate a palette plane buffer, which can potentially
> piss off custom get_buffer2() implementations), is to revert and then
> initially set the pix_fmt in ff_mjpeg_decode_sof() to PAL8 instead of
> GRAY8 for jpegls, and replace it with the latter if no LSE is ever
> parsed (or if one is parsed and it doesn't contain a palette).
> It's also hacky, basically the inverse of what it used to be, but at
> least it ensures the palette plane is allocated by get_buffer2(), which
> will ultimately be ignored and freed.
Which is to say, revert fb5e2d7112 c8197f73e6 and try the something like
the following:
> $ git diff
> diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
> index 7c66ff8637..70e674758d 100644
> --- a/libavcodec/mjpegdec.c
> +++ b/libavcodec/mjpegdec.c
> @@ -681,10 +681,8 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
> } else if (s->nb_components != 1) {
> av_log(s->avctx, AV_LOG_ERROR, "Unsupported number of components %d\n", s->nb_components);
> return AVERROR_PATCHWELCOME;
> - } else if (s->palette_index && s->bits <= 8)
> + } else if (s->bits <= 8)
> s->avctx->pix_fmt = AV_PIX_FMT_PAL8;
> - else if (s->bits <= 8)
> - s->avctx->pix_fmt = AV_PIX_FMT_GRAY8;
> else
> s->avctx->pix_fmt = AV_PIX_FMT_GRAY16;
> }
> @@ -733,6 +731,13 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
> for (i = 0; i < 4; i++)
> s->linesize[i] = s->picture_ptr->linesize[i] << s->interlaced;
>
> + if (s->ls && !s->palette_index && s->bits <= 8) {
> + // The LSE marker will change this back to PAL8 if there's a palette.
> + // By calling ff_get_buffer() with PAL8 pix_fmt we ensured a plane for
> + // it was allocated.
> + s->avctx->pix_fmt = s->picture_ptr->format = AV_PIX_FMT_GRAY8;
> + avpriv_set_systematic_pal2((uint32_t *)s->picture_ptr->data[1], s->picture_ptr->format);
> + }
> ff_dlog(s->avctx, "%d %d %d %d %d %d\n",
> s->width, s->height, s->linesize[0], s->linesize[1],
> s->interlaced, s->avctx->height);
fate-jpegls passes, so even if also hacky, it may be better than
postponing the call to ff_get_buffer().
More information about the ffmpeg-devel
mailing list