0

我正在尝试获取 YUV 420 流,将其转换为 MPEG2 并通过 UDP 作为传输流发送。

转换似乎工作正常,通过保存输出我可以创建一个可播放的 MPEG。在wireshark 中查看传输的数据包时,我可以看到程序关联和程序映射表(它们看起来是正确的)以及b 帧和p 帧。我看不到任何 I 帧,但可以看到 MPEG 序列标头数据包。

使用 VLC 我无法查看流(UDP://239.192.1.114:6677)

下面是一段代码,展示了视频包的转换和传输。

任何关于为什么我看不到 I 帧的想法将不胜感激。

bool MPEGTransmitter::InitMPEG(int width, int height)
{

    /* find the mpeg2 video encoder */
    codec = avcodec_find_encoder(CODEC_ID_MPEG2VIDEO);
    if (!codec) 
    {
        //debugCb("Codec Not Found"); //fprintf(stderr, "codec not found\n");
        return false;
    }

    codecCtxt = avcodec_alloc_context3(codec);
    picture= avcodec_alloc_frame();

    /* put sample parameters */
    codecCtxt->bit_rate = 0;
    /* resolution */
    codecCtxt->width = width;
    codecCtxt->height = height;
    /* frames per second */
    AVRational avr;
    avr.den = 25;
    avr.num = 1;
    codecCtxt->time_base= avr;
    codecCtxt->gop_size = 5; /* emit one intra frame every 5 frames */
    codecCtxt->max_b_frames=2;
    codecCtxt->pix_fmt = PIX_FMT_YUV420P;   // 4:2:0 used for MPEG

    /* open it */
    if (avcodec_open2(codecCtxt, codec,NULL) < 0) 
    {
        //debugCb("Could not open video codec"); //fprintf(stderr, "could not open codec\n");
        return false;
    }



    /* alloc image and output buffer */
    outbuf_size = 100000;
    outbuf = (uint8_t*)malloc(outbuf_size);
    size = codecCtxt->width * codecCtxt->height;
    picture_buf = (uint8_t*)malloc((size * 3)/2); /* size for YUV 420 */

    picture->data[0] = picture_buf;
    picture->data[1] = picture->data[0] + size;
    picture->data[2] = picture->data[1] + size/4;
    picture->linesize[0] = codecCtxt->width;
    picture->linesize[1] = codecCtxt->width /2 ;
    picture->linesize[2] = codecCtxt->width /2;

    SaveToFile(outfileName);
    //MpegInitialised = true;

    std::stringstream ssLog;
    ssLog << "MPEG initialised with Height "<< height << " Width " << width << endl;
    WriteLogMsg(ssLog.str());

    return true;
}


void MPEGTransmitter::WriteFrame(unsigned char* Yarray, unsigned char* cbArray, unsigned char* crArray)
{
    /* prepare the image */
    /* Y */
    int idx=0;
    for(int y=0;y<codecCtxt->height;y++) 
    {
        for(int x=0;x<codecCtxt->width;x++) 
        {
            picture->data[0][y * picture->linesize[0] + x] = Yarray[idx++];
        }
    }

    /* Cb and Cr */
    for(int y=0;y < codecCtxt->height/2 ;y++) 
    {
        for(int x=0;x< codecCtxt->width/2 ;x++) 
        {
            picture->data[1][y * codecCtxt->width/2 + x] = (cbArray[(y*codecCtxt->width)+x] + cbArray[(y*codecCtxt->width)+ x+1])/2; 
            picture->data[2][y * codecCtxt->width/2 + x] = (crArray[(y*codecCtxt->width)+x] + crArray[(y*codecCtxt->width)+ x+1])/2;
        }
    }

    /* encode the image */
    out_size = avcodec_encode_video(codecCtxt, outbuf, outbuf_size, picture);

    /* send the image */
    WriteMpegTS(outbuf, out_size,0x1F);

    /* save the image */
    if( fVideoOut )
    {
        fwrite(outbuf, 1, out_size, fVideoOut);
    }
 }

void MPEGTransmitter::WriteMpegTS(uint8_t* pPayloadPkt, int iPktSize, unsigned int iPidLower)
{
    int dataCounter = 0;
    bool firstPacket=true;

    while(dataCounter < iPktSize)
    {   
        // TS Packets are 188 bytes long, 4 byte header followed by payload
        char* pTSPkt = new char[188];
        pTSPkt[0] = 0x47; // Sync byte

        if(firstPacket) 
        {
            pTSPkt[1] = 0x40; // start of payload flag set + high bytes of payload id
            firstPacket=false;
        }
        else
        {
            pTSPkt[1] = 0; // start of payload flag not set + high bytes of payload id
        }

        pTSPkt[2] = iPidLower; // low bytes of payload id

        pTSPkt[3] = 0x10 | getContinuityCounter(iPidLower); //returns the cont. counter for the specified pkt type

        // Fill the rest of the packet with data, or padding if no more data to add
        for(int i= 4; i< 188; i++)
        {
            if(dataCounter <iPktSize)
            {
                pTSPkt[i] = pPayloadPkt[dataCounter++];
            }
            else
            {
                pTSPkt[i] = 0xFF;
            }
        }

        // Send the packet over multicast/rtp
        unsigned int pktSize = 188;
        sktMpeg->SendMsg(pTSPkt, pktSize); // calls the sendMsg function of the socket class
    }
4

1 回答 1

1

我一直认为 LIBav 编码调用会返回一个打包的基本流数据包,结果它是一个标准的 ES 数据包。通过将其包装在 PES 标头中,然后将其放入传输流数据包中(每个 UDP 7 个),我可以流式传输看似正确的 mpeg TS。尽管我认为这是特定于实现的,但让 VLC 显示框架仍然存在一些问题。

于 2012-11-01T11:45:42.983 回答