Hello,

Please see attached files to see how you can use FFmpeg's libavformat
library to write a framer for H264 video. The code has been tested
with .mp4 files, and with a tweak or two, will work for any other
container format. It assumes that the nal_length_size is 4, but one
can change this so that the nal_length_size is dynamically derived
from what libavformat knows it is. One nice thing about libavformat is
that it will parse the video stream for you and always give you a full
video frame on av_read_frame. (An access unit).

Regards,
-- 
B. Gitonga Marete
Tel: +254-722-151-590
#include "LavfH264StreamFramer.hh"

extern "C" 
{
#include <libavcodec/avcodec.h>
}
extern "C"
{
#include <libavutil/rational.h>
}


const int microInSec = 1000000;

LavfH264StreamFramer::LavfH264StreamFramer(UsageEnvironment& env,
					   AVFormatContext* pfmtContext,
					   char* sourceFile,
					   int streamIndex)
    : H264VideoStreamFramer(env, NULL)
{
    fpFmtContext = pfmtContext;
    fSourceFile = sourceFile;
    fStreamIndex = streamIndex;
}

LavfH264StreamFramer::~LavfH264StreamFramer()
{
    av_close_input_file(fpFmtContext);
}

LavfH264StreamFramer* LavfH264StreamFramer::createNew(UsageEnvironment& env, 
				char *sourceFile)
{
    AVFormatContext* pFmtContext;

    // Important: Register all codecs and formats
    av_register_all();

    if(av_open_input_file(&pFmtContext, sourceFile, NULL, 0, NULL) != 0){
	fprintf(stderr, "LavfH264StreamFramer:createNew(): Could not open "
		"source file %s\n", sourceFile);
	return NULL;
    }

    if(av_find_stream_info(pFmtContext) < 0){
	fprintf(stderr, "LavfH264StreamFramer:createNew(): Could not find "
		"stream information for source file %s\n", sourceFile);
	return NULL;
    }

    // In the following, we choose the first H.264 stream in the file
    int streamIndex = -1;
    for(unsigned int i = 0; i < pFmtContext->nb_streams; i++){
	if(pFmtContext->streams[i]->codec->codec_id == CODEC_ID_H264){
	    streamIndex = i;
	    break;
	}
    }

    if(streamIndex == -1){
	fprintf(stderr, "LavfH264StreamFramer:createNew(): Could not find "
		"a H.264 video stream in source file %s\n", sourceFile);
	return NULL;
    }

    return new LavfH264StreamFramer(env, pFmtContext, sourceFile, streamIndex);

}

Boolean LavfH264StreamFramer::currentNALUnitEndsAccessUnit()
{
    return True;
}

void LavfH264StreamFramer::doGetNextFrame()
{
    AVPacket packet;

    //fprintf(stderr, "doGetNextFrame() called\n");

    while(1){
	if(av_read_frame(fpFmtContext, &packet) != 0){
	    handleClosure(this);
	    /* XXX Should we call av_free_packet() here? */
	    return;
	}

	if(packet.stream_index == fStreamIndex){
	    break;
	}
	else{
	    av_free_packet(&packet);
	}
    }

    // Successfully read a packet of required type
    // Set the frame size
    fFrameSize = packet.size - 4;

    //fprintf(stderr, "Frame Size: %u", fFrameSize);

    if(fFrameSize <= fMaxSize){
	memcpy(fTo, packet.data + 4, fFrameSize);
	fNumTruncatedBytes = 0;
    }
    else{
	memcpy(fTo, packet.data + 4 , fMaxSize);
	fNumTruncatedBytes = fFrameSize - fMaxSize;
    }


    /* Calculate fPresentationTime and fDurationInMicroseconds */
    int timeBaseNum = fpFmtContext->streams[fStreamIndex]->r_frame_rate.den;
    int timeBaseDen = fpFmtContext->streams[fStreamIndex]->r_frame_rate.num;
    double timeUnitSecs = timeBaseNum / (double) timeBaseDen;

    fDurationInMicroseconds = ((packet.duration) * timeUnitSecs) * microInSec;

    //fprintf(stderr, " timeBaseNum %d", timeBaseNum);
    //fprintf(stderr, " timeBaseDen %d", timeBaseDen);

    //fprintf(stderr, " Duration %u\n", fDurationInMicroseconds);

    /* When not decompressing frames, libavformat recommends to use dts*
     * since some sources contain B frames
     */
    int ptsMicroseconds = ((packet.pts) * timeUnitSecs) * microInSec;
    fPresentationTime.tv_sec = ptsMicroseconds / microInSec;
    fPresentationTime.tv_usec = ptsMicroseconds % microInSec;

    //We are done with the packet for now
    av_free_packet(&packet);

    afterGetting(this);

}

#ifndef _LAVF_H264_STREAM_FRAMER_HH
#define _LAVF_H264_STREAM_FRAMER_HH

#include "H264VideoStreamFramer.hh"

#ifdef __cplusplus 
extern "C" {
#endif
#include <libavformat/avformat.h>
#ifdef __cplusplus
}
#endif

class LavfH264StreamFramer : public H264VideoStreamFramer
{

public:
    static LavfH264StreamFramer* createNew(UsageEnvironment& env,
					   char* sourceFile);
    virtual Boolean currentNALUnitEndsAccessUnit();
    virtual void doGetNextFrame();

protected:
    LavfH264StreamFramer(UsageEnvironment& env, AVFormatContext *pfmtContext, 
			 char* sourceFile, int streamIndex);
    virtual ~LavfH264StreamFramer();

private:
    AVFormatContext* fpFmtContext;
    char* fSourceFile;
    int fStreamIndex;
};


#endif // _LAVF_H264_STREAM_FRAMER_HH
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to