Как декодировать MJPEG с помощью FFmpeg - PullRequest
0 голосов
/ 13 марта 2019

Я пытаюсь декодировать поток MJPEG с помощью libav. Поток идет от драйвера V4L2. При работе с высокими разрешениями следующий код работает нормально. Однако при использовании низких разрешений (например, 320x190) он выдает странные артефакты

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);

}

Если я пытаюсь прочитать содержимое AVFrame на диск сразу после avcodec_receive_frame(), я получаю упомянутые «странные результаты» (я вижу это, загружая его на rawpixels.net), поэтому проблема не в этой строке. Если я сохраню данные pkt на диск в формате JPEG как раз перед freeBuffer(), изображение будет видно правильно. Я приложу несколько фотографий

V4L2 настроен на 1280x720 V4L2 configured at 1280x720

V4L2 настроен на 320x190 V4L2 configured at 320x190

Полный код можно найти по адресу:

https://github.com/oierlauzi/zuazo

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

Редактировать 1: Я забыл упомянуть, codecCtx-> pix_fmt имеет значение AL_PIX_FMT_YUVJ422P (предоставлено libav)

...