

class FFMpegSourceModule : public SimpleModule {
public:
    FFMpegSourceModule() : avFormatContext( 0 )
    {
    }

    bool supportsOutputType( Format type )
    {
        return type == "FRAME_ID_MPEG1_VIDEO_PACKET" || type == "FRAME_ID_MPEG_AUDIO_PACKET" || type == "FRAME_ID_MPEG2_VIDEO_PACKET" || type == "FRAME_ID_MPEG4_VIDEO_PACKET";
    }
    
    const char* name() { return "FFMpeg Demuxer Source"; }
    Format inputFormat() { return "FRAME_ID_URL_SOURCE"; }
    Format outputFormat() { return "FRAME_ID_MULTIPLE_PACKET"; }
    bool isBlocking() { return true; }
    list<Module*> threadAffinity() { }

    void init()
    {
	av_register_all();
    }
    
    void process( const Frame &frame )
    {
        printf("file: %s\n", (char*)frame.data());
        
        // Open file
        if ( av_open_input_file(&avFormatContext, (char*)frame.data(), 0, 0, 0) < 0 || !avFormatContext ) {
            printf("error opening file");
            return;
        }
        
        frame.deref();
        
        // Gather stream information
        if ( av_find_stream_info(avFormatContext) < 0 ) {
            printf("error getting stream info\n");
            return;
        }
   
        while( avFormatContext ) {
            AVPacket *pkt = new AVPacket;
//            if ( av_read_packet(avFormatContext, pkt) < 0 ) {
            if ( av_read_frame(avFormatContext, pkt) < 0 ) {
                printf("error reading packet\n");
                av_free_packet( pkt );
                delete pkt;
                exit( 0 ); // EOF ?
            } else {
                AVCodecContext *context = &avFormatContext->streams[pkt->stream_index]->codec;
                Frame *f = getAvailableFrame( context->codec_type );                
                if ( !f )
                    continue;
                FFMpegStreamPacket *packet = (FFMpegStreamPacket*)f->data();
                packet->packet = pkt;
                //av_dup_packet( pkt );

		ProcessMessages();
                
                dispatch( routes[pkt->stream_index], Process, f );
            }
        }
        exit( 0 );
    }

    Frame *getAvailableFrame( int type )
    {       
        Frame *frame;
        list<Frame*>::iterator it;
        for ( it = used[type].begin(); it != used[type].end(); ++it ) {
            frame = *it;
            if ( frame->refcount() == 0 ) {
                reuseFrame( frame );
                frame->ref();
                return frame;
            }
        }
        
        // Create new frame
        frame = createNewFrame( type );
        if ( frame ) {
            frame->ref();
            used[type].push_back( frame );
        }
        return frame;
    }
    
    Frame* createNewFrame( int type )
    { 
        FFMpegStreamPacket *packet = new FFMpegStreamPacket;
        switch( type ) {
            case CODEC_TYPE_AUDIO:
                return new Frame( "FRAME_ID_MPEG_AUDIO_PACKET", packet );
            case CODEC_TYPE_VIDEO:
                return new Frame( "FRAME_ID_MPEG1_VIDEO_PACKET", packet );
        }
        return 0;
    }
    
    void reuseFrame( Frame *frame )
    {
        FFMpegStreamPacket *packet = (FFMpegStreamPacket*)frame->data();
        av_free_packet( packet->packet );
        delete packet->packet;
    }

    void connectTo( Module *next, const Frame &f )
    {
        routes[((FFMpegStreamPacket*)f.data())->packet->stream_index] = next;
    }

private:    
    AVFormatContext *avFormatContext;
    map<int,list<Frame*> > used;    
    map<int, Module*> routes;
};

