0

I'm trying to take a YUV 420 stream, convert it to MPEG2 and send it via UDP as a transmport stream.

The conversion appears to work correctly, by saving the output I can create a playable MPEG. When viewing the transmitted packets in wireshark I am able to see the Program Association, and Program Map tables (they appear correct) and the b-frames and p-frames. I'm unable to see any I-frames but can see MPEG sequence header packets.

Using VLC I am unable to view the stream (UDP://239.192.1.114:6677)

Below is a code snippet showing the conversion and the transmission of video packets.

Any ideas on why I can't see the I-frames would be greatly appreciated.

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
    }
dougie
  • 67
  • 6
  • From looking at a wireshark output from VLC when streaming over UDP it appears that no iframes are shown. It also looks like there are seven transport stream packets(video data or PSI) per UDP packet. I will update my code and post more comments on whether this works. – dougie Oct 30 '12 at 10:36

1 Answers1

1

I had been thinking the LIBav encode call would return a Packetised Elementary Stream packet, turns out its a standard ES packet. By wrapping this in the PES header then placing that into transport stream packets (7 per UDP) I'm able to stream what appears to be a correct mpeg TS. Still having a few issues getting VLC to show the frames although I think thats implementation specific.

dougie
  • 67
  • 6
  • All issues fixed. Now streaming to VLC without a problem. I had forgotten to include DTS timestamp in my PES packet. – dougie Nov 02 '12 at 10:46