Esempio n. 1
0
/* This function imports the avi represented by the AVFormatContext to the movie media represented
 * in the map function. The aviheader_offset is used to calculate the packet offset from the
 * beginning of the file. It returns whether it was successful or not (i.e. whether the file had an index) */
int import_using_index(ff_global_ptr storage, int *hadIndex, TimeValue *addedDuration) {
    int j, k, l;
    NCStream *map;
    NCStream *ncstr;
    AVFormatContext *ic;
    AVStream *stream;
    AVCodecContext *codec;
    SampleReference64Ptr sampleRec;
    int64_t header_offset, offset, duration;
    short flags;
    int sampleNum;
    ComponentResult result = noErr;

    map = storage->stream_map;
    ic = storage->format_context;
    header_offset = storage->header_offset;

    if(*hadIndex == 0)
        goto bail;

    //FLVs have unusable indexes, so don't even bother.
    if(storage->componentType == 'FLV ')
        goto bail;

    /* process each stream in ic */
    for(j = 0; j < ic->nb_streams; j++) {
        ncstr = &map[j];
        stream = ncstr->str;
        codec = stream->codec;

        /* no stream we can read */
        if(!ncstr->valid)
            continue;

        /* no index, we might as well skip */
        if(stream->nb_index_entries == 0)
            continue;

        sampleNum = 0;
        ncstr->sampleTable = calloc(stream->nb_index_entries, sizeof(SampleReference64Record));

        /* now parse the index entries */
        for(k = 0; k < stream->nb_index_entries; k++) {

            /* file offset */
            offset = header_offset + stream->index_entries[k].pos;

            /* flags */
            flags = 0;
            if((stream->index_entries[k].flags & AVINDEX_KEYFRAME) == 0)
                flags |= mediaSampleNotSync;

            sampleRec = &ncstr->sampleTable[sampleNum++];

            /* set as many fields in sampleRec as possible */
            sampleRec->dataOffset.hi = offset >> 32;
            sampleRec->dataOffset.lo = (uint32_t)offset;
            sampleRec->dataSize = stream->index_entries[k].size;
            sampleRec->sampleFlags = flags;

            /* some samples have a data_size of zero. if that's the case, ignore them
            	* they seem to be used to stretch the frame duration & are already handled
            	* by the previous pkt */
            if(sampleRec->dataSize <= 0) {
                sampleNum--;
                continue;
            }

            /* switch for the remaining fields */
            if(codec->codec_type == AVMEDIA_TYPE_VIDEO) {

                /* Calculate the frame duration */
                duration = 1;
                for(l = k+1; l < stream->nb_index_entries; l++) {
                    if(stream->index_entries[l].size > 0)
                        break;
                    duration++;
                }

                sampleRec->durationPerSample = map->base.num * duration;
                sampleRec->numberOfSamples = 1;
            }
            else if(codec->codec_type == AVMEDIA_TYPE_AUDIO) {

                /* FIXME: check if that's really the right thing to do here */
                if(ncstr->vbr) {
                    sampleRec->numberOfSamples = 1;

                    if (k + 1 < stream->nb_index_entries)
                        sampleRec->durationPerSample = (stream->index_entries[k+1].timestamp - stream->index_entries[k].timestamp) * ncstr->base.num;
                    else if (sampleNum - 2 >= 0)
                        // if we're at the last index entry, use the duration of the previous sample
                        // FIXME: this probably could be better
                        sampleRec->durationPerSample = ncstr->sampleTable[sampleNum-2].durationPerSample;

                } else {
                    sampleRec->durationPerSample = 1;
                    sampleRec->numberOfSamples = (stream->index_entries[k].size * ncstr->asbd.mFramesPerPacket) / ncstr->asbd.mBytesPerPacket;
                }
            }
        }
        if(sampleNum != 0)
        {
            /* Add all of the samples to the media */
            AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, sampleNum, ncstr->sampleTable, NULL);

            /* The index is both present and not empty */
            *hadIndex = 1;
        }
        free(ncstr->sampleTable);
    }

    if(*hadIndex == 0)
        //No index, the remainder of this function will fail.
        goto bail;

    // insert media and set addedDuration;
    for(j = 0; j < storage->map_count && result == noErr; j++) {
        ncstr = &map[j];
        if(ncstr->valid) {
            Media media = ncstr->media;
            Track track;
            TimeRecord time;
            TimeValue mediaDuration;
            TimeScale mediaTimeScale;
            TimeScale movieTimeScale;
            int startTime = map[j].str->index_entries[0].timestamp;

            mediaDuration = GetMediaDuration(media);
            mediaTimeScale = GetMediaTimeScale(media);
            movieTimeScale = GetMovieTimeScale(storage->movie);

            /* we could handle this stream.
            * convert the atTime parameter to track scale.
            * FIXME: check if that's correct */
            time.value.hi = 0;
            time.value.lo = storage->atTime;
            time.scale = movieTimeScale;
            time.base = NULL;
            ConvertTimeScale(&time, mediaTimeScale);

            track = GetMediaTrack(media);
            result = InsertMediaIntoTrack(track, time.value.lo, 0, mediaDuration, fixed1);

            // set audio/video start delay
            // note str.start_time exists but is always 0 for AVI
            if (startTime) {
                TimeRecord startTimeRec;
                startTimeRec.value.hi = 0;
                startTimeRec.value.lo = startTime * map[j].str->time_base.num;
                startTimeRec.scale = map[j].str->time_base.den;
                startTimeRec.base = NULL;
                ConvertTimeScale(&startTimeRec, movieTimeScale);
                SetTrackOffset(track, startTimeRec.value.lo);
            }

            if(result != noErr)
                goto bail;

            time.value.hi = 0;
            time.value.lo = mediaDuration;
            time.scale = mediaTimeScale;
            time.base = NULL;
            ConvertTimeScale(&time, movieTimeScale);

            if(time.value.lo > *addedDuration)
                *addedDuration = time.value.lo;
        }
    }

    storage->loadedTime = *addedDuration;

bail:
    return result;
} /* import_using_index() */
Esempio n. 2
0
/* Import function for movies that lack an index.
 * Supports progressive importing, but will not idle if maxFrames == 0.
 */
ComponentResult import_with_idle(ff_global_ptr storage, long inFlags, long *outFlags, int minFrames, int maxFrames, bool addSamples) {
    SampleReference64Record sampleRec;
    AVFormatContext *formatContext;
    AVCodecContext *codecContext;
    AVStream *stream;
    AVPacket packet;
    NCStream *ncstream;
    ComponentResult dataResult; //used for data handler operations that can fail.
    ComponentResult result;
    TimeValue minLoadedTime;
    TimeValue movieTimeScale = GetMovieTimeScale(storage->movie);
    int64_t availableSize, margin;
    long idling;
    int readResult, framesProcessed, i;
    int firstPts[storage->map_count];
    short flags;

    formatContext = storage->format_context;
    result = noErr;
    minLoadedTime = -1;
    availableSize = 0;
    idling = (inFlags & movieImportWithIdle);
    framesProcessed = 0;

    if(idling) {
        //get the size of immediately available data
        if(storage->dataHandlerSupportsWideOffsets) {
            wide wideSize;

            dataResult = DataHGetAvailableFileSize64(storage->dataHandler, &wideSize);
            if(dataResult == noErr) availableSize = ((int64_t)wideSize.hi << 32) + wideSize.lo;
        } else {
            long longSize;

            dataResult = DataHGetAvailableFileSize(storage->dataHandler, &longSize);
            if(dataResult == noErr) availableSize = longSize;
        }
    }

    for(i = 0; i < storage->map_count; i++) {
        ncstream = &storage->stream_map[i];
        Media media = ncstream->media;

        firstPts[i] = -1;
        if(media && ncstream->duration == -1)
            ncstream->duration = GetMediaDuration(media);
    }

    while((readResult = av_read_frame(formatContext, &packet)) == 0) {
        bool trustPacketDuration = true;
        int64_t dts = packet.dts;
        ncstream = &storage->stream_map[packet.stream_index];
        stream = ncstream->str;
        codecContext = stream->codec;
        flags = 0;

        if (!ncstream->valid)
            continue;

        if((packet.flags & AV_PKT_FLAG_KEY) == 0)
            flags |= mediaSampleNotSync;

        if(IS_NUV(storage->componentType) && codecContext->codec_id == CODEC_ID_MP3) trustPacketDuration = false;
        if(IS_FLV(storage->componentType)) trustPacketDuration = false;

        memset(&sampleRec, 0, sizeof(sampleRec));
        sampleRec.dataOffset.hi = packet.pos >> 32;
        sampleRec.dataOffset.lo = (uint32_t)packet.pos;
        sampleRec.dataSize = packet.size;
        sampleRec.sampleFlags = flags;

        if (packet.pos <= 0)
            continue;

        if(firstPts[packet.stream_index] < 0)
            firstPts[packet.stream_index] = packet.pts;

        if(packet.size > storage->largestPacketSize)
            storage->largestPacketSize = packet.size;

        if(sampleRec.dataSize <= 0)
            continue;

        if(codecContext->codec_type == AVMEDIA_TYPE_AUDIO && !ncstream->vbr)
            sampleRec.numberOfSamples = (packet.size * ncstream->asbd.mFramesPerPacket) / ncstream->asbd.mBytesPerPacket;
        else
            sampleRec.numberOfSamples = 1; //packet.duration;

        //add any samples waiting to be added
        if(ncstream->lastSample.numberOfSamples > 0) {
            //calculate the duration of the sample before adding it
            ncstream->lastSample.durationPerSample = (dts - ncstream->lastdts) * ncstream->base.num;

            AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &ncstream->lastSample, NULL);
        }

#if 0
        if (0) {
            Codecprintf(NULL, "Stream:%d Pts:%lld Dts:%lld DtsUsed:%lld Pos:%lld Size:%d\n", packet.stream_index, packet.pts, packet.dts, dts, packet.pos, packet.size);
            Codecprintf(NULL, "Stream:%d Nsamples:%ld RealDuration:%d CalcDuration:%ld TimeDts:%lld TimeDurations:%lld FrameDts:%d FrameGuess:%lld\n",
                        packet.stream_index, sampleRec.numberOfSamples, packet.duration, ncstream->lastSample.durationPerSample,
                        packet.dts, ncstream->timeByDurations, (int)((packet.dts * stream->time_base.num * ncstream->asbd.mSampleRate) / stream->time_base.den),
                        ncstream->timeByFrames);

            ncstream->timeByDurations += packet.duration;
            ncstream->timeByFrames += ncstream->asbd.mFramesPerPacket;
        }
#endif

        ncstream->lastSample = sampleRec;
        ncstream->lastdts = packet.dts;

        // If this is a nuv file, then we want to set the duration to zero.
        // This is because the nuv container doesn't have the framesize info
        // for audio.
        if(packet.duration == 0 || !trustPacketDuration) {
            //no duration, we'll have to wait for the next packet to calculate it
            // keep the duration of the last sample, so we can use it if it's the last frame
            sampleRec.durationPerSample = ncstream->lastSample.durationPerSample;
        } else {
            ncstream->lastSample.numberOfSamples = 0;

            if(codecContext->codec_type == AVMEDIA_TYPE_AUDIO && !ncstream->vbr)
                sampleRec.durationPerSample = 1;
            else
                sampleRec.durationPerSample = ncstream->base.num * packet.duration;

            AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &sampleRec, NULL);
        }

        framesProcessed++;

        //if we're idling, try really not to read past the end of available data
        //otherwise we will cause blocking i/o.
        if(idling && framesProcessed >= minFrames && availableSize > 0 && availableSize < storage->dataSize) {
            margin = availableSize - (packet.pos + packet.size);
            if(margin < (storage->largestPacketSize * 8)) { // 8x fudge factor for comfortable margin, could be tweaked.
                av_free_packet(&packet);
                break;
            }
        }

        av_free_packet(&packet);

        //stop processing if we've hit the max frame limit
        if(maxFrames > 0 && framesProcessed >= maxFrames)
            break;
    }

    if(readResult != 0) {
        //if readResult != 0, we've hit the end of the stream.
        //add any pending last frames.
        for(i = 0; i < formatContext->nb_streams; i++) {
            ncstream = &storage->stream_map[i];
            if(ncstream->lastSample.numberOfSamples > 0)
                AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &ncstream->lastSample, NULL);
        }
    }

    for(i = 0; i < storage->map_count && result == noErr; i++) {
        ncstream = &storage->stream_map[i];
        Media media = ncstream->media;

        if(ncstream->valid && (addSamples || readResult != 0)) {
            Track track = GetMediaTrack(media);
            TimeScale mediaTimeScale = GetMediaTimeScale(media);
            TimeValue prevDuration = ncstream->duration;
            TimeValue mediaDuration = GetMediaDuration(media);
            TimeValue addedDuration = mediaDuration - prevDuration;
            TimeValue mediaLoadedTime = movieTimeScale * mediaDuration / mediaTimeScale;

            if(minLoadedTime == -1 || mediaLoadedTime < minLoadedTime)
                minLoadedTime = mediaLoadedTime;

            if(addedDuration > 0) {
                result = InsertMediaIntoTrack(track, -1, prevDuration, addedDuration, fixed1);
            }

            if (!prevDuration && firstPts[i] > 0) {
                TimeRecord startTimeRec;
                startTimeRec.value.hi = 0;
                startTimeRec.value.lo = firstPts[i] * formatContext->streams[i]->time_base.num;
                startTimeRec.scale = formatContext->streams[i]->time_base.den;
                startTimeRec.base = NULL;
                ConvertTimeScale(&startTimeRec, movieTimeScale);
                SetTrackOffset(track, startTimeRec.value.lo);
            }
            ncstream->duration = -1;
        }
    }

    //set the loaded time to the length of the shortest track.
    if(minLoadedTime > 0)
        storage->loadedTime = minLoadedTime;

    if(readResult != 0) {
        //remove the placeholder track
        if(storage->placeholderTrack != NULL) {
            DisposeMovieTrack(storage->placeholderTrack);
            storage->placeholderTrack = NULL;
        }

        //set the movie load state to complete, as well as mark the import output flag.
        storage->movieLoadState = kMovieLoadStateComplete;
        *outFlags |= movieImportResultComplete;
    } else {
        //if we're not yet done with the import, calculate the movie load state.
        int64_t timeToCompleteFile; //time until the file should be completely available, in terms of AV_TIME_BASE
        long dataRate = 0;

        dataResult = DataHGetDataRate(storage->dataHandler, 0, &dataRate);
        if(dataResult == noErr && dataRate > 0) {
            timeToCompleteFile = (AV_TIME_BASE * (storage->dataSize - availableSize)) / dataRate;

            if(storage->loadedTime > (10 * GetMovieTimeScale(storage->movie)) && timeToCompleteFile < (storage->format_context->duration * .85))
                storage->movieLoadState = kMovieLoadStatePlaythroughOK;
            else
                storage->movieLoadState = kMovieLoadStatePlayable;

        } else {
            storage->movieLoadState = kMovieLoadStatePlayable;
        }

        *outFlags |= movieImportResultNeedIdles;
    }

    send_movie_changed_notification(storage->movie);

    //tell the idle manager to idle us again in 500ms.
    if(idling && storage->idleManager && storage->isStreamed)
        QTIdleManagerSetNextIdleTimeDelta(storage->idleManager, 1, 2);

    return(result);
} /* import_with_idle() */
Esempio n. 3
0
ComponentResult FFAvi_MovieImportDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
										 Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
{
	ComponentResult result = noErr;
	ByteIOContext *byteContext;
	AVFormatContext *ic = NULL;
	AVFormatParameters params;
	OSType mediaType;
	Media media;
	int count, hadIndex, j;
		
	/* make sure that in case of error, the flag movieImportResultComplete is not set */
	*outFlags = 0;
	
	/* probe the format first */
	UInt8 valid = 0;
	FFAvi_MovieImportValidateDataRef(storage, dataRef, dataRefType, &valid);
	if(valid != 255)
		goto bail;
			
	/* Prepare the iocontext structure */
	result = url_open_dataref(&byteContext, dataRef, dataRefType, &storage->dataHandler, &storage->dataHandlerSupportsWideOffsets, &storage->dataSize);
	storage->isStreamed = dataRefType == URLDataHandlerSubType;
	require_noerr(result, bail);
	
	/* Open the Format Context */
	memset(&params, 0, sizeof(params));
	result = av_open_input_stream(&ic, byteContext, "", storage->format, &params);
	require_noerr(result,bail);
	storage->format_context = ic;
	
	// AVIs without an index currently add a few entries to the index so it can
	// determine codec parameters.  Check for index existence here before it
	// reads any packets.
	hadIndex = 1;
	for (j = 0; j < ic->nb_streams; j++) {
		if (ic->streams[j]->nb_index_entries <= 1)
		{
			hadIndex = 0;
			break;
		}
	}
	
	/* Get the Stream Infos if not already read */
	result = av_find_stream_info(ic);
	
	// -1 means it couldn't understand at least one stream
	// which might just mean we don't have its video decoder enabled
	if(result < 0 && result != -1)
		goto bail;
	
	// we couldn't find any streams, bail with an error.
	if(ic->nb_streams == 0) {
		result = -1; //is there a more appropriate error code?
		goto bail;
	}
	
	//determine a header offset (needed by index-based import).
	result = determine_header_offset(storage);
	if(result < 0)
		goto bail;
	
	/* Initialize the Movie */
	storage->movie = theMovie;
	if(inFlags & movieImportMustUseTrack) {
		storage->map_count = 1;
		prepare_track(storage, targetTrack, dataRef, dataRefType);
	} else {
		storage->map_count = ic->nb_streams;
		result = prepare_movie(storage, theMovie, dataRef, dataRefType);
		if (result != 0)
			goto bail;
	}
	
	/* replace the SampleDescription if user called MovieImportSetSampleDescription() */
	if(storage->imgHdl) {
		for(j = 0; j < storage->map_count; j++) {
			NCStream ncstream = storage->stream_map[j];
			GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
			if(mediaType == VideoMediaType && ncstream.sampleHdl) {
				DisposeHandle((Handle)ncstream.sampleHdl);
				ncstream.sampleHdl = (SampleDescriptionHandle)storage->imgHdl;
			}
		}
	}
	if(storage->sndHdl) {
		for(j = 0; j < storage->map_count; j++) {
			NCStream ncstream = storage->stream_map[j];
			GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
			if(mediaType == SoundMediaType && ncstream.sampleHdl) {
				DisposeHandle((Handle)ncstream.sampleHdl);
				ncstream.sampleHdl = (SampleDescriptionHandle)storage->sndHdl;
			}
		}
	}
	
	count = 0; media = NULL;
	for(j = 0; j < storage->map_count; j++) {
		media = storage->stream_map[j].media;
		if(media)
			count++;
	}
	
	if(count > 1)
		*outFlags |= movieImportResultUsedMultipleTracks;
	
	/* The usedTrack parameter. Count the number of Tracks and set usedTrack if we operated
		* on a single track. Note that this requires the media to be set by track counting above*/
	if(usedTrack && count == 1 && media)
		*usedTrack = GetMediaTrack(media);
	
	result = noErr;

	*addedDuration = 0;
	
	//attempt to import using indexes.
	result = import_using_index(storage, &hadIndex, addedDuration);
	require_noerr(result, bail);
	
	if(hadIndex) {
		//file had an index and was imported; we are done.
		*outFlags |= movieImportResultComplete;
		
	} else if(inFlags & movieImportWithIdle) {
		if(addedDuration && ic->duration > 0) {
			TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
			*addedDuration = movieTimeScale * ic->duration / AV_TIME_BASE;
			
			//create a placeholder track so that progress displays correctly.
			create_placeholder_track(storage->movie, &storage->placeholderTrack, *addedDuration, dataRef, dataRefType);
			
			//give the data handler a hint as to how fast we need the data.
			//suggest a speed that's faster than the bare minimum.
			//if there's an error, the data handler probably doesn't support
			//this, so we can just ignore.
			DataHPlaybackHints(storage->dataHandler, 0, 0, -1, (storage->dataSize * 1.15) / ((double)ic->duration / AV_TIME_BASE));
		}
			
		//import with idle. Decode a little bit of data now.
		import_with_idle(storage, inFlags, outFlags, 10, 300, true);
	} else {
		//QuickTime didn't request import with idle, so do it all now.
		import_with_idle(storage, inFlags, outFlags, 0, 0, true);			
	}
	
	LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);

bail:
	if(result == noErr)
		storage->movieLoadState = kMovieLoadStateLoaded;
	else
		storage->movieLoadState = kMovieLoadStateError;
		
	if (result == -1)
		result = invalidMovie; // a bit better error message
	
	return result;
} /* FFAvi_MovieImportDataRef */