コード例 #1
0
ファイル: reader.c プロジェクト: Jerrific/HandBrake
static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
                        hb_buffer_t ** buf_out)
{
    hb_work_private_t  * r = w->private_data;
    hb_fifo_t         ** fifos;
    hb_buffer_t        * buf;
    hb_buffer_list_t     list;
    int                  ii, chapter = -1;

    hb_buffer_list_clear(&list);

    if (r->bd)
        chapter = hb_bd_chapter( r->bd );
    else if (r->dvd)
        chapter = hb_dvd_chapter( r->dvd );
    else if (r->stream)
        chapter = hb_stream_chapter( r->stream );

    if( chapter < 0 )
    {
        hb_log( "reader: end of the title reached" );
        reader_send_eof(r);
        return HB_WORK_DONE;
    }
    if( chapter > r->chapter_end )
    {
        hb_log("reader: end of chapter %d (media %d) reached at media chapter %d",
                r->job->chapter_end, r->chapter_end, chapter);
        reader_send_eof(r);
        return HB_WORK_DONE;
    }

    if (r->bd)
    {
        if( (buf = hb_bd_read( r->bd )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }
    else if (r->dvd)
    {
        if( (buf = hb_dvd_read( r->dvd )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }
    else if (r->stream)
    {
        if ( (buf = hb_stream_read( r->stream )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }

    (hb_demux[r->title->demuxer])(buf, &list, &r->demux);

    while ((buf = hb_buffer_list_rem_head(&list)) != NULL)
    {
        fifos = GetFifoForId( r, buf->s.id );

        if (fifos && r->stream && r->start_found == 2 )
        {
            // We will inspect the timestamps of each frame in sync
            // to skip from this seek point to the timestamp we
            // want to start at.
            if (buf->s.start != AV_NOPTS_VALUE &&
                buf->s.start < r->job->pts_to_start)
            {
                r->job->pts_to_start -= buf->s.start;
            }
            else if ( buf->s.start >= r->job->pts_to_start )
            {
                r->job->pts_to_start = 0;
            }
            r->start_found = 1;
        }

        if ( fifos && ! r->saw_video && !r->job->indepth_scan )
        {
            // The first data packet with a PTS from an audio or video stream
            // that we're decoding defines 'time zero'. Discard packets until
            // we get one.
            if (buf->s.start != AV_NOPTS_VALUE &&
                buf->s.renderOffset != AV_NOPTS_VALUE &&
                 (buf->s.id == r->title->video_id ||
                  is_audio( r, buf->s.id)))
            {
                // force a new scr offset computation
                r->scr_changes = r->demux.scr_changes - 1;
                // create a stream state if we don't have one so the
                // offset will get computed correctly.
                id_to_st( r, buf, 1 );
                r->saw_video = 1;
                hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64,
                        r->demux.last_scr, buf->s.id, buf->s.renderOffset );
            }
            else
            {
                fifos = NULL;
            }
        }

        if ( r->job->indepth_scan || fifos )
        {
            if ( buf->s.renderOffset != AV_NOPTS_VALUE )
            {
                if ( r->scr_changes != r->demux.scr_changes )
                {
                    // This is the first audio or video packet after an SCR
                    // change. Compute a new scr offset that would make this
                    // packet follow the last of this stream with the
                    // correct average spacing.
                    stream_timing_t *st = id_to_st( r, buf, 0 );

                    // if this is the video stream and we don't have
                    // audio yet or this is an audio stream
                    // generate a new scr
                    if ( st->is_audio ||
                         ( st == r->stream_timing && !r->saw_audio ) )
                    {
                        new_scr_offset( r, buf );
                        r->sub_scr_set = 0;
                    }
                    else
                    {
                        // defer the scr change until we get some
                        // audio since audio has a timestamp per
                        // frame but video & subtitles don't. Clear
                        // the timestamps so the decoder will generate
                        // them from the frame durations.
                        if (is_subtitle(r, buf->s.id) &&
                            buf->s.start != AV_NOPTS_VALUE)
                        {
                            if (!r->sub_scr_set)
                            {
                                // We can't generate timestamps in the
                                // subtitle decoder as we can for
                                // audio & video.  So we need to make
                                // the closest guess that we can
                                // for the subtitles start time here.
                                int64_t last = r->stream_timing[0].last;
                                r->scr_offset = buf->s.start - last;
                                r->sub_scr_set = 1;
                            }
                        }
                        else
                        {
                            buf->s.start = AV_NOPTS_VALUE;
                            buf->s.renderOffset = AV_NOPTS_VALUE;
                        }
                    }
                }
            }
            if ( buf->s.start != AV_NOPTS_VALUE )
            {
                int64_t start = buf->s.start - r->scr_offset;

                if (!r->start_found || r->job->indepth_scan)
                {
                    UpdateState( r, start );
                }

                if (r->job->indepth_scan && r->job->pts_to_stop &&
                    start >= r->pts_to_start + r->job->pts_to_stop)
                {
                    // sync normally would terminate p-to-p
                    // but sync doesn't run during indepth scan
                    hb_log("reader: reached pts %"PRId64", exiting early", start);
                    reader_send_eof(r);
                    hb_buffer_list_close(&list);
                    return HB_WORK_DONE;
                }

                if (!r->start_found && start >= r->pts_to_start)
                {
                    // pts_to_start point found
                    // Note that this code path only gets executed for
                    // medai where we have not performed an initial seek
                    // to get close to the start time. So the 'start' time
                    // is the time since the first frame.

                    if (r->stream)
                    {
                        // libav multi-threaded decoders can get into
                        // a bad state if the initial data is not
                        // decodable.  So try to improve the chances of
                        // a good start by waiting for an initial iframe
                        hb_stream_set_need_keyframe(r->stream, 1);
                        hb_buffer_close( &buf );
                        continue;
                    }
                    r->start_found = 1;
                    // sync.c also pays attention to job->pts_to_start
                    // It eats up the 10 second slack that we build in
                    // to the start time here in reader (so that video
                    // decode is clean at the start time).
                    // sync.c expects pts_to_start to be relative to the
                    // first timestamp it sees.
                    if (r->job->pts_to_start > start)
                    {
                        r->job->pts_to_start -= start;
                    }
                    else
                    {
                        r->job->pts_to_start = 0;
                    }
                }
                // This log is handy when you need to debug timing problems
                //hb_log("id %x scr_offset %"PRId64
                //       " start %"PRId64" --> %"PRId64"",
                //        buf->s.id, r->scr_offset, buf->s.start,
                //        buf->s.start - r->scr_offset);
                buf->s.start -= r->scr_offset;
                if ( buf->s.stop != AV_NOPTS_VALUE )
                {
                    buf->s.stop -= r->scr_offset;
                }
            }
            if ( buf->s.renderOffset != AV_NOPTS_VALUE )
            {
                // This packet is referenced to the same SCR as the last.
                // Adjust timestamp to remove the System Clock Reference
                // offset then update the average inter-packet time
                // for this stream.
                buf->s.renderOffset -= r->scr_offset;
                update_ipt( r, buf );
            }
#if 0
            // JAS: This was added to fix a rare "audio time went backward"
            // sync error I found in one sample.  But it has a bad side
            // effect on DVDs, causing frequent "adding silence" sync
            // errors. So I am disabling it.
            else
            {
                update_ipt( r, buf );
            }
#endif
        }
        buf = splice_discontinuity(r, buf);
        if( fifos && buf != NULL )
        {
            if ( !r->start_found )
            {
                hb_buffer_close( &buf );
                continue;
            }

            buf->sequence = r->sequence++;
            /* if there are mutiple output fifos, send a copy of the
             * buffer down all but the first (we have to not ship the
             * original buffer or we'll race with the thread that's
             * consuming the buffer & inject garbage into the data stream). */
            for (ii = 1; fifos[ii] != NULL; ii++)
            {
                hb_buffer_t *buf_copy = hb_buffer_init(buf->size);
                buf_copy->s = buf->s;
                memcpy(buf_copy->data, buf->data, buf->size);
                push_buf(r, fifos[ii], buf_copy);
            }
            push_buf(r, fifos[0], buf);
            buf = NULL;
        }
        else
        {
            hb_buffer_close(&buf);
        }
    }

    hb_buffer_list_close(&list);
    return HB_WORK_OK;
}
コード例 #2
0
ファイル: reader.c プロジェクト: bradleysepos/HandBrake
static int reader_work( hb_work_object_t * w, hb_buffer_t ** buf_in,
                        hb_buffer_t ** buf_out)
{
    hb_work_private_t  * r = w->private_data;
    hb_fifo_t         ** fifos;
    hb_buffer_t        * buf;
    hb_buffer_list_t     list;
    int                  ii, chapter = -1;

    hb_buffer_list_clear(&list);

    if (r->bd)
        chapter = hb_bd_chapter( r->bd );
    else if (r->dvd)
        chapter = hb_dvd_chapter( r->dvd );
    else if (r->stream)
        chapter = hb_stream_chapter( r->stream );

    if( chapter < 0 )
    {
        hb_log( "reader: end of the title reached" );
        reader_send_eof(r);
        return HB_WORK_DONE;
    }
    if( chapter > r->chapter_end )
    {
        hb_log("reader: end of chapter %d (media %d) reached at media chapter %d",
                r->job->chapter_end, r->chapter_end, chapter);
        reader_send_eof(r);
        return HB_WORK_DONE;
    }

    if (r->bd)
    {
        if( (buf = hb_bd_read( r->bd )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }
    else if (r->dvd)
    {
        if( (buf = hb_dvd_read( r->dvd )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }
    else if (r->stream)
    {
        if ( (buf = hb_stream_read( r->stream )) == NULL )
        {
            reader_send_eof(r);
            return HB_WORK_DONE;
        }
    }
    else
    {
        // This should never happen
        hb_error("Stream not initialized");
        reader_send_eof(r);
        return HB_WORK_DONE;
    }

    (hb_demux[r->title->demuxer])(buf, &list, &r->demux);

    while ((buf = hb_buffer_list_rem_head(&list)) != NULL)
    {
        fifos = GetFifoForId( r, buf->s.id );
        if (fifos && r->stream && !r->start_found)
        {
            // libav is allowing SSA subtitles to leak through that are
            // prior to the seek point.  So only make the adjustment to
            // pts_to_start after we see the next video buffer.
            if (buf->s.id != r->job->title->video_id)
            {
                hb_buffer_close(&buf);
                continue;
            }
            // We will inspect the timestamps of each frame in sync
            // to skip from this seek point to the timestamp we
            // want to start at.
            if (buf->s.start != AV_NOPTS_VALUE &&
                buf->s.start < r->job->pts_to_start)
            {
                r->job->pts_to_start -= buf->s.start;
            }
            else if ( buf->s.start >= r->job->pts_to_start )
            {
                r->job->pts_to_start = 0;
            }
            r->start_found = 1;
        }

        if (buf->s.start   != AV_NOPTS_VALUE &&
            r->scr_changes != r->demux.scr_changes)
        {
            // First valid timestamp after an SCR change.  Update
            // the per-stream scr sequence number
            r->scr_changes = r->demux.scr_changes;

            // libav tries to be too smart with timestamps and
            // enforces unnecessary conditions.  One such condition
            // is that subtitle timestamps must be monotonically
            // increasing.  To ensure this is the case, we calculate
            // an offset upon each SCR change that will guarantee this.
            // This is just a very rough SCR offset.  A fine grained
            // offset that maintains proper sync is calculated in sync.c
            if (r->last_pts != AV_NOPTS_VALUE)
            {
                r->scr_offset  = r->last_pts + 90000 - buf->s.start;
            }
            else
            {
                r->scr_offset  = -buf->s.start;
            }
        }
        // Set the scr sequence that this buffer's timestamps are
        // referenced to.
        buf->s.scr_sequence = r->scr_changes;
        if (buf->s.start != AV_NOPTS_VALUE)
        {
            buf->s.start += r->scr_offset;
        }
        if (buf->s.renderOffset != AV_NOPTS_VALUE)
        {
            buf->s.renderOffset += r->scr_offset;
        }
        if (buf->s.start > r->last_pts)
        {
            r->last_pts = buf->s.start;
            UpdateState(r);
        }

        buf = splice_discontinuity(r, buf);
        if (fifos && buf != NULL)
        {
            /* if there are mutiple output fifos, send a copy of the
             * buffer down all but the first (we have to not ship the
             * original buffer or we'll race with the thread that's
             * consuming the buffer & inject garbage into the data stream). */
            for (ii = 1; fifos[ii] != NULL; ii++)
            {
                hb_buffer_t *buf_copy = hb_buffer_init(buf->size);
                buf_copy->s = buf->s;
                memcpy(buf_copy->data, buf->data, buf->size);
                push_buf(r, fifos[ii], buf_copy);
            }
            push_buf(r, fifos[0], buf);
            buf = NULL;
        }
        else
        {
            hb_buffer_close(&buf);
        }
    }

    hb_buffer_list_close(&list);
    return HB_WORK_OK;
}