0

I am trying to decode a MJPEG stream with libav. The stream comes from V4L2 driver. When working with high resolutions the following code works fine. However, when using low resolutions (For instance 320x190) it produces strange artefacts

void V4L2::compressedCapturingThread(){
const AVCodec *codec=avcodec_find_decoder(m_currVidMode.codec.toAVCodecID());
if(!codec)
    return; //Something went horribly wrong

//Allocate the context
AVCodecContext* codecCtx=avcodec_alloc_context3(codec);
if(!codecCtx){
    return; //Error
}

codecCtx->width=m_currVidMode.res.width;
codecCtx->height=m_currVidMode.res.height;

//Open the context
if (avcodec_open2(codecCtx, codec, nullptr) < 0) {
    avcodec_free_context(&codecCtx);
    return;
}

//Allocate space for the packet
AVPacket *pkt=av_packet_alloc();
if(!pkt){
    avcodec_free_context(&codecCtx);
    return;
}

AVFrame* decodedFrame=av_frame_alloc();
if(!decodedFrame){
    avcodec_free_context(&codecCtx);
    avcodec_free_context(&codecCtx);
    return;
}

Graphics::Uploader uplo;
v4l2_buffer buf;
int ret;

Utils::ImageBuffer decodedImgBuf(
        Utils::ImageAttributes(
                m_currVidMode.res,
                m_currVidMode.pixFmt
        ), (u_int8_t*)nullptr
);

//Main loop
while(!m_threadExit){
    reqBuffer(&buf);

    if(!buf.bytesused){
        continue;
    }

    //Create the packet with the given data
    u_int8_t* bufData=(u_int8_t*)av_malloc(buf.bytesused);
    memcpy(bufData, m_buffers[buf.index].buffer, buf.bytesused); //copy the data
    av_packet_from_data (pkt, bufData, buf.bytesused);

    freeBuffer(&buf); //V4L2 buffer no longer needed

    //Try to decode the packet
    ret=avcodec_send_packet(codecCtx, pkt);
    av_packet_unref(pkt);
    if(ret<0){
        continue;
    }

    ret = avcodec_receive_frame(codecCtx, decodedFrame);
    if(ret<0){
        continue;
    }

    memcpy(decodedImgBuf.data, decodedFrame->data, sizeof(decodedImgBuf.data)); //Copy plane pointers

    if(decodedImgBuf.att.pixFmt == Utils::PixelFormats::NONE){
        decodedImgBuf.att.pixFmt=Utils::PixelFormat(codecCtx->pix_fmt);

        //Change deprecated formats
        if(decodedImgBuf.att.pixFmt == Utils::PixelFormats::YUVJ420P)
            decodedImgBuf.att.pixFmt = Utils::PixelFormats::YUV420P;
        else if(decodedImgBuf.att.pixFmt == Utils::PixelFormats::YUVJ422P)
            decodedImgBuf.att.pixFmt = Utils::PixelFormats::YUV422P;
        else if(decodedImgBuf.att.pixFmt == Utils::PixelFormats::YUVJ440P)
            decodedImgBuf.att.pixFmt = Utils::PixelFormats::YUV440P;
        else if(decodedImgBuf.att.pixFmt == Utils::PixelFormats::YUVJ444P)
            decodedImgBuf.att.pixFmt = Utils::PixelFormats::YUV444P;
    }

    std::unique_ptr<const Graphics::Frame> frame;

    {
        Graphics::UniqueContext ctx(Graphics::Context::getAvalibleCtx());
        frame=uplo.getFrame(decodedImgBuf);
    }

    Stream::AsyncSource<Graphics::Frame>::push(std::move(frame));
}

//Free everything
avcodec_free_context(&codecCtx);
av_packet_free(&pkt);
av_frame_free(&decodedFrame);

}

If I try to read the AVFrame's contents to disk just after avcodec_receive_frame() I get the mentioned "strange results" (I see it by uploading it to rawpixels.net ), so the problem is not after this line. If I save the pkt 's data to disk as JPEG just before freeBuffer() the image can be seen properly. I'll attach some pictures

V4L2 configured at 1280x720 V4L2 configured at 1280x720

V4L2 configured at 320x190 V4L2 configured at 320x190

The complete code can be found at:

https://github.com/oierlauzi/zuazo

https://github.com/oierlauzi/zuazo/blob/master/src/Zuazo/Sources/V4L2.cpp

Edit 1: I forgot to mention, codecCtx->pix_fmt has the value of AL_PIX_FMT_YUVJ422P (given by libav)

oierlauzi
  • 167
  • 2
  • 7

0 Answers0