root/src/blocks/flv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. readAudioHdr
  2. readVideoHdr
  3. FLVStream_skipTagData
  4. destroyFLVStream
  5. FLVStream_fromInput
  6. FLVStream_rewind
  7. FLVStream_nextTag
  8. FLVStream_nextTagType
  9. FLVStream_getDuration
  10. FLVStream_getNumFrames
  11. FLVTag_getPayloadInput
  12. FLVStream_setStreamOffset

/*
   Ming, an SWF output library
   Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
   
   2.2.2007 Klaus Rechert
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
#include <stdio.h>
#include <stdlib.h>

#include "input.h"
#include "libming.h"
#include "error.h"
#include "flv.h"

static inline int readAudioHdr(FLVStream *flv, FLVTag *tag)
{
        int ichar;
        ichar = SWFInput_getChar(flv->input);
        if(ichar == EOF)
                return -1;
        tag->hdr.audio.format = (0xf0 & ichar);
        tag->hdr.audio.samplingRate = (0xc & ichar);
        tag->hdr.audio.sampleSize = (0x2 & ichar);
        tag->hdr.audio.channel = 0x1 & ichar;
        return 0;
}

static inline int readVideoHdr(FLVStream *flv, FLVTag *tag)
{
        int ichar;
        ichar = SWFInput_getChar(flv->input);
        if(ichar == EOF)
                return -1;
        
        tag->hdr.video.frameType = (0xf0 & ichar);
        tag->hdr.video.codec = 0x0f & ichar;
        return 0;
}

long FLVStream_skipTagData(FLVStream *flv, FLVTag *tag)
{
        if(flv == NULL || tag == NULL)
                return -1;
                
        SWFInput_seek(flv->input, tag->data + tag->dataSize + 4, SEEK_SET);
        
        return SWFInput_tell(flv->input);
}

void destroyFLVStream(FLVStream *flv)
{
        free(flv);
}

FLVStream *FLVStream_fromInput(SWFInput input)
{
        int ichar;
        unsigned long ulchar;
        FLVStream *flv;
        
        if(!input)
                return NULL;

        ichar = SWFInput_getChar(input);
        if(ichar == EOF || ichar != 'F')
                return NULL;
        
        ichar = SWFInput_getChar(input);
        if(ichar == EOF || ichar != 'L')
                return NULL;
        
        ichar = SWFInput_getChar(input);
        if(ichar == EOF || ichar != 'V')
                return NULL;

        flv = (FLVStream *)malloc(sizeof(FLVStream));
        if(flv == NULL)
                return  NULL;
        flv->input = input;
        
        ichar = SWFInput_getChar(input);
        if(ichar != EOF)
                flv->version = ichar;
        
        flv->has_video = 0;
        flv->has_audio = 0;
        ichar = SWFInput_getChar(input);
        if(ichar != EOF)
        {
                if(ichar & FLV_AUDIO_PRESENT)
                        flv->has_audio = 1;

                if(ichar & FLV_VIDEO_PRESENT)
                        flv->has_video = 1;
        }

        ulchar = SWFInput_getUInt32_BE(input);
        flv->offset = ulchar + 4;
        flv->stream_start = flv->offset;
        return flv;
}

void FLVStream_rewind(FLVStream *flv)
{
        SWFInput_seek(flv->input, flv->offset, SEEK_SET);
}

int FLVStream_nextTag(FLVStream *flv, FLVTag *tag, FLVTag *prev) 
{
        int ichar;
        unsigned long ulchar;
        
        if(prev == NULL)
                SWFInput_seek(flv->input, flv->offset, SEEK_SET);
        else
        {
                unsigned long off;
                if(prev->data < 0)
                        return -1;
                
                off = prev->data + prev->dataSize + 4;
                SWFInput_seek(flv->input, off, SEEK_SET);
        }
        
        tag->offset = SWFInput_tell(flv->input);

        tag->stream = flv;

        ichar = SWFInput_getChar(flv->input);
        if(ichar == EOF)
                return -1;
        if(ichar != FLV_VIDEOTAG && ichar != FLV_AUDIOTAG && ichar != FLV_SCRIPTTAG)
        {
                SWF_warn("FLV: stream out of sync!\n");
                return -1;
        }
        tag->tagType = ichar;
        
        ulchar = SWFInput_getUInt24_BE(flv->input);
        tag->dataSize = ulchar;

        ulchar = SWFInput_getUInt24_BE(flv->input);
        tag->timeStamp = ulchar;

        ulchar = SWFInput_getUInt32_BE(flv->input);
        if(ulchar)
        {
                SWF_warn("FLV: stream out of sync!\n");
                return -1;
        }

        tag->data = SWFInput_tell(flv->input);
        if(tag->tagType == FLV_VIDEOTAG)
                readVideoHdr(flv, tag);
        else if(tag->tagType == FLV_AUDIOTAG)
                readAudioHdr(flv, tag);
        
        return 0;
}

int FLVStream_nextTagType(FLVStream *flv, FLVTag *tag, FLVTag *prev, int type)
{
        
        while(FLVStream_nextTag(flv, tag, prev) == 0)
        {
                if(tag->tagType == type) 
                        return 0;
                prev = tag;
        }
        return -1;

}

unsigned int FLVStream_getDuration(FLVStream *flv, int type)
{
        unsigned int duration = 0;
        FLVTag tag, *p_tag = NULL;
        
        while(FLVStream_nextTag(flv, &tag, p_tag) == 0) 
        {
                if(tag.tagType == type)
                {
                        /* optimistic approach */
                        duration = tag.timeStamp; 
                }
                p_tag = &tag;
        }
        return duration;
}

int FLVStream_getNumFrames(FLVStream *flv, int type)
{
        int numFrames = 0;
        FLVTag tag, *p_tag = NULL;
        
        while(FLVStream_nextTag(flv, &tag, p_tag) == 0) 
        {
                if(tag.tagType == type)
                        numFrames++;
                p_tag = &tag;
        }
        return numFrames;
}

SWFInput FLVTag_getPayloadInput(FLVTag *tag)
{
        int length;
        SWFInput input;
        if(tag == NULL || tag->stream == NULL)
                return NULL;
        
        input = tag->stream->input;

        /* screen video needs this extra byte undocumented! */
        if(tag->tagType == FLV_VIDEOTAG 
                && tag->hdr.video.codec == VIDEO_CODEC_SCREEN)
        {
                length = tag->dataSize;
                SWFInput_seek(input, tag->data, SEEK_SET);
        }
        else if(tag->tagType == FLV_VIDEOTAG
                && tag->hdr.video.codec == VIDEO_CODEC_VP6)
        {
                length = tag->dataSize - 2;
                SWFInput_seek(input, tag->data + 2, SEEK_SET);
        }
        else /* skip flv-audio/video-data byte */
        {
                length = tag->dataSize - 1;
                SWFInput_seek(input, tag->data + 1, SEEK_SET);
        }

        return newSWFInput_input(input, length);
}

int FLVStream_setStreamOffset(FLVStream *flv, unsigned int msecs)
{
        FLVTag tag, *p_tag = NULL;
        
        /* reset stream offset */
        flv->offset = flv->stream_start;        
        
        while(FLVStream_nextTag(flv, &tag, p_tag) == 0) 
        {
                if(tag.timeStamp >= msecs)
                {
                        flv->offset = tag.offset;
                        return 0;
                }
                p_tag = &tag;
        }
        return -1;
}


/* [<][>][^][v][top][bottom][index][help] */