如何使用FFmpeg解码MJPEG

时间:2019-03-12 21:39:53

标签: c++ mjpeg libav v4l2

我正在尝试使用libav解码MJPEG流。流来自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);

}

如果我尝试在avcodec_receive_frame()之后立即将AVFrame的内容读取到磁盘上,则会得到上述“奇怪的结果”(我将其上传到rawpixels.net看到了),因此问题不在此行之后。如果我在freeBuffer()之前将pkt的数据以JPEG格式保存到磁盘,则可以正确看到该图像。我要附上图片

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提供)

0 个答案:

没有答案