Exemplo n.º 1
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() */
Exemplo n.º 2
0
Arquivo: track.c Projeto: one-k/rmov
/*
  call-seq: offset=(seconds)
  
  Sets the offset of the track from the start of the movie (in seconds).
*/
static VALUE track_set_offset(VALUE obj, VALUE seconds)
{
  SetTrackOffset(TRACK(obj), TRACK_TIME(obj, seconds));
  return Qnil;
}
Exemplo n.º 3
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() */