1

我有一个 jpeg 集合,它必须由 lib jpeg 解码,然后由 x264 编码(在它编码的数据包通过 rtmp 流式传输之后)。我用于解码的代码:

struct my_error_mgr 
{   
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef my_error_mgr *my_error_ptr;

METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
    my_error_ptr myerr = (my_error_ptr) cinfo->err;
    (*cinfo->err->output_message) (cinfo);
    longjmp(myerr->setjmp_buffer, 1);   
}

void init_source(j_decompress_ptr ptr)
{
    Q_UNUSED(ptr)
}

boolean fill_input_buffer(j_decompress_ptr ptr) 
{
     Q_UNUSED(ptr)
    return TRUE;
}

void term_source(j_decompress_ptr ptr)
{
    Q_UNUSED(ptr)
}

void skip_input_data(j_decompress_ptr ptr, long num_bytes)
{
    if(num_bytes>0) 
    {
        ptr->src->next_input_byte+=(size_t)num_bytes;
        ptr->src->bytes_in_buffer-=(size_t)num_bytes;   
    }
}

EtherDecoder::EtherDecoder(QObject *parent):
QObject(parent)
{
}

void EtherDecoder::dataBlockReady(QByteArray data)
{
    jpeg_decompress_struct decompressInfo;
    jpeg_create_decompress(&decompressInfo);
    my_error_mgr err;
    decompressInfo.do_fancy_upsampling = FALSE;
    decompressInfo.src = (jpeg_source_mgr *) (*decompressInfo.mem->alloc_small)       ((j_common_ptr) &decompressInfo, JPOOL_PERMANENT, sizeof(jpeg_source_mgr));
    decompressInfo.err = jpeg_std_error(&err.pub);
    err.pub.error_exit = my_error_exit;
    if (setjmp(err.setjmp_buffer))  
    {
        jpeg_destroy_decompress(&decompressInfo);
        return; 
    }
    decompressInfo.src->init_source = init_source;
    decompressInfo.src->resync_to_restart = jpeg_resync_to_restart;
    decompressInfo.src->fill_input_buffer = fill_input_buffer;
    decompressInfo.src->skip_input_data = skip_input_data;
    decompressInfo.src->term_source = term_source;
    decompressInfo.src->next_input_byte = reinterpret_cast<const JOCTET*>(data.data());
    decompressInfo.src->bytes_in_buffer = data.size();
    jpeg_read_header(&decompressInfo, TRUE);
    jpeg_start_decompress(&decompressInfo);
    int size = 0;
    int n_samples = 0;
    char *samples = new char[5242880];
    char *reserv = samples;
    while (decompressInfo.output_scanline < decompressInfo.output_height)   
    {
        n_samples = jpeg_read_scanlines(&decompressInfo, (JSAMPARRAY) &samples, 1);
        samples += n_samples * decompressInfo.image_width * decompressInfo.num_components;
        size += n_samples * decompressInfo.image_width * decompressInfo.num_components; 
    }
    jpeg_finish_decompress(&decompressInfo);
    QByteArray output(reserv, size);
    emit frameReady(output, decompressInfo.output_width, decompressInfo.output_height);
    jpeg_destroy_decompress(&decompressInfo);
    delete[] reserv;
}

当我发出 frameReady 信号时,我将数据发送到 Encoder,方法,我在其中初始化 Encedor,如下所示:

bool EtherEncoder::initEncoder(unsigned int width, unsigned int height)
{
    x264_param_t param;
    x264_param_default_preset(&param, "veryfast", "zerolatency");
    param.i_width=width;
    param.i_height=height;
    param.i_frame_total=0;
    param.i_csp=X264_CSP_I420;
    param.i_timebase_num=1;
    param.i_timebase_den=96000;
    param.b_annexb=true;
    param.b_repeat_headers=false;
    x264_param_apply_fastfirstpass(&param);
    x264_param_apply_profile(&param, "baseline");
    _context=x264_encoder_open(&param);
    if(!_context)
        return false;
    int nal_count;
    x264_nal_t *nals;
    if(x264_encoder_headers(_context, &nals, &nal_count)<0) 
    {
        x264_encoder_close(_context);
        _context=0;
        return false;   
    }
    _extradata=QByteArray();
    _width=width;
    _height=height;
    if(nal_count>0) 
    {
        _extradata=QByteArray(
            (const char *)nals[0].p_payload,
            nals[nal_count-1].p_payload+nals[nal_count-1].i_payload-nals[0].p_payload); 
    }
    return true;
}

及编码方式:

void EtherEncoder::onFrameReady(QByteArray data, int width, int height)
{
    while(data.size()>0)    
    {
        if(!_context && initEncoder(width, height))
        {
            _timestampDelta=realTimestamp();
        }
        if(_context)
        {
            x264_picture_t pic;
            x264_picture_init(&pic);
            pic.i_type=X264_TYPE_AUTO;
            pic.i_pts=_timestampDelta*96000;
            pic.img.i_csp=X264_CSP_I420;
            pic.img.i_plane=3;
            int planeSize = width*height;
            uint8_t *p = (uint8_t*)data.data();
            pic.img.plane[0]=p;
            p+=planeSize;
            pic.img.plane[1]=p;
            p+=planeSize/4;
            pic.img.plane[2]=p;
            pic.img.i_stride[0]=width;
            pic.img.i_stride[1]=width/2;
            pic.img.i_stride[2]=width/2;
            if(_forceKeyFrame)
            {
                pic.i_type=X264_TYPE_I;
                _forceKeyFrame=false;
            }
            int nal_count;
            x264_nal_t *nals;
            int rc=x264_encoder_encode(_context, &nals, &nal_count, &pic, &pic);
            if(rc>0)
            {
                _mutex.lock();
                _packets.push_back(
                    Packet(
                        QByteArray(
                            (const char *)nals[0].p_payload,         nals[nal_count-    1].p_payload+nals[nal_count-1].i_payload-nals[0].p_payload),
                        _timestampDelta/96.0,
                        _timestampDelta/96.0,
                        pic.b_keyframe));
                _timestampDelta+=40;
                data.clear();
                _mutex.unlock();
                emit onPacketReady();
            }
        }   
    }
}

解码和编码没有错误地继续进行,最后我得到了有效的视频流,但是,似乎在其中一个步骤中,我为解码器/编码器设置了无效数据。我只得到图像的 1/4 部分(据我了解,左上角)并且它具有无效的颜色并出现彩色条纹。也许我在编码帧时设置了无效的步幅和平面,或者我为 libjpeg 解码器设置的数据不正确。请就我的代码提出问题,我会尽力为你做一些解释。我爆炸了我的大脑..谢谢。

4

0 回答 0