Example #1
0
static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
                           int64_t dts, int64_t next)
{
    int ret = AVERROR_INVALIDDATA, i;
    AVIOContext *pb = s->pb;
    AVStream *st = NULL;
    AMFDataType type;
    char buf[20];
    int length;

    type = avio_r8(pb);
    if (type == AMF_DATA_TYPE_MIXEDARRAY)
        avio_seek(pb, 4, SEEK_CUR);
    else if (type != AMF_DATA_TYPE_OBJECT)
        goto out;

    amf_get_string(pb, buf, sizeof(buf));
    if (strcmp(buf, "type") || avio_r8(pb) != AMF_DATA_TYPE_STRING)
        goto out;

    amf_get_string(pb, buf, sizeof(buf));
    //FIXME parse it as codec_id
    amf_get_string(pb, buf, sizeof(buf));
    if (strcmp(buf, "text") || avio_r8(pb) != AMF_DATA_TYPE_STRING)
        goto out;

    length = avio_rb16(pb);
    ret = av_get_packet(s->pb, pkt, length);
    if (ret < 0) {
        ret = AVERROR(EIO);
        goto out;
    }

    for (i = 0; i < s->nb_streams; i++) {
        st = s->streams[i];
        if (st->id == 2)
            break;
    }

    if (i == s->nb_streams) {
        st = create_stream(s, 2, AVMEDIA_TYPE_DATA);
        if (!st)
            goto out;
        st->codec->codec_id = CODEC_ID_TEXT;
    }

    pkt->dts  = dts;
    pkt->pts  = dts;
    pkt->size = ret;

    pkt->stream_index = st->index;
    pkt->flags |= AV_PKT_FLAG_KEY;

    avio_seek(s->pb, next + 4, SEEK_SET);
out:
    return ret;
}
Example #2
0
static int flv_read_metabody(flv* p, format_reader* Reader, int64_t next_pos) 
{
    AMFDataType type;
    char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want.

    //first object needs to be "onMetaData" string
	type = Reader->Read8(Reader);

    if(type != AMF_DATA_TYPE_STRING || amf_get_string(p,Reader, buffer, sizeof(buffer)) < 0 || tcscmp(buffer, "onMetaData"))
        return -1;

    //parse the second object (we want a mixed array)
    if(amf_parse_object(p,Reader, buffer, next_pos, 0) < 0)
        return -1;

    return 0;
}
Example #3
0
static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) {
    AMFDataType type;
    AVStream *stream, *astream, *vstream, *dstream;
    AVIOContext *ioc;
    int i;
    char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want.

    vstream = astream = dstream = NULL;
    ioc = s->pb;

    //first object needs to be "onMetaData" string
    type = avio_r8(ioc);
    if (type != AMF_DATA_TYPE_STRING ||
            amf_get_string(ioc, buffer, sizeof(buffer)) < 0)
        return -1;

    if (!strcmp(buffer, "onTextData"))
        return 1;

    if (strcmp(buffer, "onMetaData"))
        return -1;

    //find the streams now so that amf_parse_object doesn't need to do the lookup every time it is called.
    for(i = 0; i < s->nb_streams; i++) {
        stream = s->streams[i];
        if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) vstream = stream;
        else if(stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) astream = stream;
        else if(stream->codec->codec_type == AVMEDIA_TYPE_DATA) dstream = stream;
    }

    //parse the second object (we want a mixed array)
    if(amf_parse_object(s, astream, vstream, buffer, next_pos, 0) < 0)
        return -1;

    return 0;
}
Example #4
0
static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vstream, const char *key, int64_t max_pos, int depth) {
    AVCodecContext *acodec, *vcodec;
    AVIOContext *ioc;
    AMFDataType amf_type;
    char str_val[256];
    double num_val;

    num_val = 0;
    ioc = s->pb;

    amf_type = avio_r8(ioc);

    switch(amf_type) {
    case AMF_DATA_TYPE_NUMBER:
        num_val = av_int2double(avio_rb64(ioc));
        break;
    case AMF_DATA_TYPE_BOOL:
        num_val = avio_r8(ioc);
        break;
    case AMF_DATA_TYPE_STRING:
        if(amf_get_string(ioc, str_val, sizeof(str_val)) < 0)
            return -1;
        break;
    case AMF_DATA_TYPE_OBJECT:
        if ((vstream || astream) && ioc->seekable && key && !strcmp(KEYFRAMES_TAG, key) && depth == 1)
            if (parse_keyframes_index(s, ioc, vstream ? vstream : astream,
                                      max_pos) < 0)
                av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");

        while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
            if (amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0)
                return -1; //if we couldn't skip, bomb out.
        }
        if(avio_r8(ioc) != AMF_END_OF_OBJECT)
            return -1;
        break;
    case AMF_DATA_TYPE_NULL:
    case AMF_DATA_TYPE_UNDEFINED:
    case AMF_DATA_TYPE_UNSUPPORTED:
        break; //these take up no additional space
    case AMF_DATA_TYPE_MIXEDARRAY:
        avio_skip(ioc, 4); //skip 32-bit max array index
        while(avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
            //this is the only case in which we would want a nested parse to not skip over the object
            if(amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0)
                return -1;
        }
        if(avio_r8(ioc) != AMF_END_OF_OBJECT)
            return -1;
        break;
    case AMF_DATA_TYPE_ARRAY: {
        unsigned int arraylen, i;

        arraylen = avio_rb32(ioc);
        for(i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
            if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0)
                return -1; //if we couldn't skip, bomb out.
        }
    }
    break;
    case AMF_DATA_TYPE_DATE:
        avio_skip(ioc, 8 + 2); //timestamp (double) and UTC offset (int16)
        break;
    default: //unsupported type, we couldn't skip
        return -1;
    }

    if(depth == 1 && key) { //only look for metadata values when we are not nested and key != NULL
        acodec = astream ? astream->codec : NULL;
        vcodec = vstream ? vstream->codec : NULL;

        if (amf_type == AMF_DATA_TYPE_NUMBER) {
            if (!strcmp(key, "duration"))
                s->duration = num_val * AV_TIME_BASE;
            else if (!strcmp(key, "videodatarate") && vcodec && 0 <= (int)(num_val * 1024.0))
                vcodec->bit_rate = num_val * 1024.0;
            else if (!strcmp(key, "audiodatarate") && acodec && 0 <= (int)(num_val * 1024.0))
                acodec->bit_rate = num_val * 1024.0;
            else if (!strcmp(key, "datastream")) {
                AVStream *st = create_stream(s, 2, AVMEDIA_TYPE_DATA);
                if (!st)
                    return AVERROR(ENOMEM);
                st->codec->codec_id = CODEC_ID_TEXT;
            }
        }

        if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 &&
                ((!acodec && !strcmp(key, "audiocodecid")) ||
                 (!vcodec && !strcmp(key, "videocodecid"))))
            s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object

        if (!strcmp(key, "duration")        ||
                !strcmp(key, "filesize")        ||
                !strcmp(key, "width")           ||
                !strcmp(key, "height")          ||
                !strcmp(key, "videodatarate")   ||
                !strcmp(key, "framerate")       ||
                !strcmp(key, "videocodecid")    ||
                !strcmp(key, "audiodatarate")   ||
                !strcmp(key, "audiosamplerate") ||
                !strcmp(key, "audiosamplesize") ||
                !strcmp(key, "stereo")          ||
                !strcmp(key, "audiocodecid"))
            return 0;

        if(amf_type == AMF_DATA_TYPE_BOOL) {
            av_strlcpy(str_val, num_val > 0 ? "true" : "false", sizeof(str_val));
            av_dict_set(&s->metadata, key, str_val, 0);
        } else if(amf_type == AMF_DATA_TYPE_NUMBER) {
            snprintf(str_val, sizeof(str_val), "%.f", num_val);
            av_dict_set(&s->metadata, key, str_val, 0);
        } else if (amf_type == AMF_DATA_TYPE_STRING)
            av_dict_set(&s->metadata, key, str_val, 0);
    }

    return 0;
}
Example #5
0
static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) {
    FLVContext *flv = s->priv_data;
    unsigned int timeslen = 0, fileposlen = 0, i;
    char str_val[256];
    int64_t *times = NULL;
    int64_t *filepositions = NULL;
    int ret = AVERROR(ENOSYS);
    int64_t initial_pos = avio_tell(ioc);

    if(vstream->nb_index_entries>0) {
        av_log(s, AV_LOG_WARNING, "Skiping duplicate index\n");
        return 0;
    }

    if (s->flags & AVFMT_FLAG_IGNIDX)
        return 0;

    while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
        int64_t** current_array;
        unsigned int arraylen;

        // Expect array object in context
        if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY)
            break;

        arraylen = avio_rb32(ioc);
        if(arraylen>>28)
            break;

        if       (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times) {
            current_array= &times;
            timeslen= arraylen;
        } else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions) {
            current_array= &filepositions;
            fileposlen= arraylen;
        } else // unexpected metatag inside keyframes, will not use such metadata for indexing
            break;

        if (!(*current_array = av_mallocz(sizeof(**current_array) * arraylen))) {
            ret = AVERROR(ENOMEM);
            goto finish;
        }

        for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
            if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER)
                goto invalid;
            current_array[0][i] = av_int2double(avio_rb64(ioc));
        }
        if (times && filepositions) {
            // All done, exiting at a position allowing amf_parse_object
            // to finish parsing the object
            ret = 0;
            break;
        }
    }

    if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) {
        for (i = 0; i < fileposlen; i++) {
            av_add_index_entry(vstream, filepositions[i], times[i]*1000,
                               0, 0, AVINDEX_KEYFRAME);
            if (i < 2) {
                flv->validate_index[i].pos = filepositions[i];
                flv->validate_index[i].dts = times[i] * 1000;
                flv->validate_count = i + 1;
            }
        }
    } else {
invalid:
        av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n");
    }

finish:
    av_freep(&times);
    av_freep(&filepositions);
    avio_seek(ioc, initial_pos, SEEK_SET);
    return ret;
}
Example #6
0
static int amf_parse_object(flv* p, format_reader* Reader, const char *key, int64_t max_pos, int depth) 
{
    AMFDataType amf_type;

    char str_val[256];

    conv c;

	double num_val;

    num_val = 0;

	amf_type = Reader->Read8(Reader);

    switch(amf_type) 
	{
        case AMF_DATA_TYPE_NUMBER:
			{
				c.src = Reader->ReadBE64(Reader);
				num_val =c.dst;
			}
			break;
        case AMF_DATA_TYPE_BOOL:
			num_val = Reader->Read8(Reader);
			break;
        case AMF_DATA_TYPE_STRING:
            if(amf_get_string(p,Reader, str_val, sizeof(str_val)) < 0)
                return -1;
            break;
        case AMF_DATA_TYPE_OBJECT: 
			{
				while(Reader->FilePos < max_pos - 2 && amf_get_string(p,Reader, str_val, sizeof(str_val)) > 0)
				{
					if(amf_parse_object(p,Reader, str_val, max_pos, depth + 1) < 0)
					{
						return -1; //if we couldn't skip, bomb out.
					}

				}
				if(Reader->Read8(Reader) != AMF_END_OF_OBJECT)
					return -1;
			}
            break;
        case AMF_DATA_TYPE_NULL:
        case AMF_DATA_TYPE_UNDEFINED:
        case AMF_DATA_TYPE_UNSUPPORTED:
            break; //these take up no additional space
        case AMF_DATA_TYPE_MIXEDARRAY:
			Reader->Skip(Reader,4); //skip 32-bit max array index
			while(Reader->FilePos < max_pos - 2 && amf_get_string(p,Reader, str_val, sizeof(str_val)) > 0) 
			{
                //this is the only case in which we would want a nested parse to not skip over the object
                if(amf_parse_object(p,Reader, str_val, max_pos, depth + 1) < 0)
                    return -1;
            }
			if(Reader->Read8(Reader) != AMF_END_OF_OBJECT)
				return -1;
            break;
        case AMF_DATA_TYPE_ARRAY: 
			{
				unsigned int arraylen, i;

				arraylen = Reader->ReadBE32(Reader);
				if(strcmp(key,"times")==0||strcmp(key,"filepositions")==0)
				{
					if(p->IndexNum == 0)
					{
						int32_t size;
						p->IndexNum  = arraylen;
						size = sizeof(flvindex)*p->IndexNum;
						size = ((size+SAFETAIL-1)/SAFETAIL)*SAFETAIL;
						if (!AllocBlock(size,&p->IndexBuffer,0,HEAP_ANY))
							return ERR_OUT_OF_MEMORY;
					}
				}

				p->IndexCur  = 0;

				for(i = 0; i < arraylen && Reader->FilePos < max_pos - 1; i++) 
				{
					if(amf_parse_object(p,Reader,  key, max_pos, depth + 1) < 0)
						return -1; //if we couldn't skip, bomb out.
				}
        }
            break;
        case AMF_DATA_TYPE_DATE:
			Reader->Skip(Reader, 8 + 2); //timestamp (double) and UTC offset (int16)
            break;
        default: //unsupported type, we couldn't skip
            return -1;
    }

    if(depth == 1 &&key) 
	{	//only look for metadata values when we are not nested and key != NULL
        if(amf_type == AMF_DATA_TYPE_BOOL) 
		{
        } 
		else if(amf_type == AMF_DATA_TYPE_NUMBER) 
		{
            if(!tcscmp(key, "duration")) 
				p->Format.Duration = Scale(num_val, TICKSPERSEC, 1);
        } 
		else if (amf_type == AMF_DATA_TYPE_STRING)
		{
			
		}
		else if(amf_type == AMF_DATA_TYPE_DATE)
		{

		}
    }
	else if(key)
	{
		if(tcscmp(key,"times")==0)
		{
			((flvindex*)IndexBuffer(p,p->IndexCur))->times = num_val;
			p->IndexCur++;
		}
		else if(tcscmp(key,"filepositions")==0)
		{
			((flvindex*)IndexBuffer(p,p->IndexCur))->pos = num_val;
			p->IndexCur++;
		}
	}

    return 0;
}