Exemple #1
0
static void mux_loop( void * _w )
{
    hb_work_object_t  * w = _w;
    hb_work_private_t * pv = w->private_data;
    hb_job_t          * job = pv->job;
    hb_buffer_t       * buf_in;

    while ( !*job->die && w->status != HB_WORK_DONE )
    {
        buf_in = hb_fifo_get_wait( w->fifo_in );
        if ( pv->mux->done )
            break;
        if ( buf_in == NULL )
            continue;
        if ( *job->die )
        {
            if( buf_in )
            {
                hb_buffer_close( &buf_in );
            }
            break;
        }

        w->status = w->work( w, &buf_in, NULL );
        if( buf_in )
        {
            hb_buffer_close( &buf_in );
        }
    }
}
Exemple #2
0
/***********************************************************************
 * Encode
 ***********************************************************************
 *
 **********************************************************************/
static hb_buffer_t * Encode( hb_work_object_t * w )
{
    hb_work_private_t * pv = w->private_data;
    hb_audio_t * audio = w->audio;
    hb_buffer_t * buf;
    float samples[2][1152];
    uint64_t pts, pos;
    int      i, j;

    if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) )
    {
        return NULL;
    }

    hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ),
                      &pts, &pos);

    for( i = 0; i < 1152; i++ )
    {
        for( j = 0; j < pv->out_discrete_channels; j++ )
        {
            samples[j][i] = ((float *) pv->buf)[(pv->out_discrete_channels * i + j)];
        }
    }

    buf        = hb_buffer_init( pv->output_bytes );
    buf->s.start = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate;
    buf->s.stop  = buf->s.start + 90000 * 1152 / audio->config.out.samplerate;
    pv->pts = buf->s.stop;
    buf->size  = lame_encode_buffer_float( 
            pv->lame, samples[0], samples[1],
            1152, buf->data, LAME_MAXMP3BUFFER );

    buf->s.type = AUDIO_BUF;
    buf->s.frametype = HB_FRAME_AUDIO;

    if( !buf->size )
    {
        /* Encoding was successful but we got no data. Try to encode
           more */
        hb_buffer_close( &buf );
        return Encode( w );
    }
    else if( buf->size < 0 )
    {
        hb_log( "enclame: lame_encode_buffer failed" );
        hb_buffer_close( &buf );
        return NULL;
    }
    return buf;
}
Exemple #3
0
int encsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                hb_buffer_t ** buf_out )
{
    hb_buffer_t * in = *buf_in;

    if (w->subtitle->source != VOBSUB)
    {
        // Invalid source, send EOF, this shouldn't ever happen
        hb_log("encvobsub: invalid subtitle source");
        hb_buffer_close( buf_in );
        *buf_out = hb_buffer_init(0);
    }
    if ( in->size <= 0 )
    {
        /* EOF on input stream - send it downstream & say that we're done */
        *buf_out = in;
        *buf_in = NULL;
        return HB_WORK_DONE;
    }

    /*
     * Not much to do, just pass the buffer on.
     * Some day, we may re-encode bd subtitles here ;)
     */
    if (buf_out)
    {
        *buf_out = in;
        *buf_in = NULL;
    }

    return HB_WORK_OK; 
}
// Some encoders (e.g. flac) require a final NULL encode in order to
// finalize things.
static void Finalize(hb_work_object_t *w)
{
    hb_work_private_t *pv = w->private_data;

    // Finalize with NULL input needed by FLAC to generate md5sum
    // in context extradata

    // Prepare output packet
    AVPacket pkt;
    int got_packet;
    hb_buffer_t *buf = hb_buffer_init(pv->max_output_bytes);
    av_init_packet(&pkt);
    pkt.data = buf->data;
    pkt.size = buf->alloc;

    avcodec_encode_audio2(pv->context, &pkt, NULL, &got_packet);
    hb_buffer_close(&buf);

    // Then we need to recopy the header since it was modified
    if (pv->context->extradata != NULL)
    {
        memcpy(w->config->extradata.bytes, pv->context->extradata,
               pv->context->extradata_size);
        w->config->extradata.length = pv->context->extradata_size;
    }
}
// Some encoders (e.g. flac) require a final NULL encode in order to
// finalize things.
static void Finalize( hb_work_object_t * w )
{
    hb_work_private_t * pv = w->private_data;
    hb_buffer_t * buf = hb_buffer_init( pv->output_bytes );

    // Finalize with NULL input needed by FLAC to generate md5sum
    // in context extradata
    avcodec_encode_audio( pv->context, buf->data, buf->alloc, NULL );
    hb_buffer_close( &buf );
}
Exemple #6
0
// Removes a list of packets from the specified FIFO that were stored as a single element.
hb_buffer_t *hb_fifo_get_list_element( hb_fifo_t *fifo )
{
    hb_buffer_t *container = hb_fifo_get( fifo );
    // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next')
    //      to carry the list inside a single "container" buffer
    hb_buffer_t *buffer_list = container->sub;
    hb_buffer_close( &container );
    
    return buffer_list;
}
Exemple #7
0
/***********************************************************************
 * Work
 ***********************************************************************
 *
 **********************************************************************/
int enclameWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 hb_buffer_t ** buf_out )
{
    hb_work_private_t * pv = w->private_data;
    hb_audio_t * audio = w->audio;
    hb_buffer_t * in = *buf_in;
    hb_buffer_t * buf;

    if ( (*buf_in)->size <= 0 )
    {
        /* EOF on input - send it downstream & say we're done */

        buf = hb_buffer_init( pv->output_bytes );
        buf->size = lame_encode_flush( pv->lame, buf->data, LAME_MAXMP3BUFFER );
        buf->s.start = pv->pts;
        buf->s.stop  = buf->s.start + 90000 * 1152 / audio->config.out.samplerate;

        buf->s.type = AUDIO_BUF;
        buf->s.frametype = HB_FRAME_AUDIO;

        if( buf->size <= 0 )
        {
            hb_buffer_close( &buf );
        }

        // Add the flushed data
        *buf_out = buf;

        // Add the eof
        if ( buf )
        {
            buf->next = in;
        }
        else
        {
            *buf_out = in;
        }

        *buf_in = NULL;
        return HB_WORK_DONE;
    }

    hb_list_add( pv->list, *buf_in );
    *buf_in = NULL;

    *buf_out = buf = Encode( w );

    while( buf )
    {
        buf->next = Encode( w );
        buf       = buf->next;
    }

    return HB_WORK_OK;
}
static int dectx3gWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                        hb_buffer_t ** buf_out )
{
    hb_buffer_t * in = *buf_in;
    hb_buffer_t * out = NULL;
    
    // Warn if the subtitle's duration has not been passed through by the demuxer,
    // which will prevent the subtitle from displaying at all
    if ( in->s.stop == 0 ) {
        hb_log( "dectx3gsub: subtitle packet lacks duration" );
    }
    
    if ( in->size > 0 ) {
        out = tx3g_decode_to_utf8(in);
    } else {
        out = hb_buffer_init( 0 );
    }
    
    if ( out != NULL ) {
        // We shouldn't be storing the extra NULL character,
        // but the MP4 muxer expects this, unfortunately.
        if (out->size > 0 && out->data[out->size - 1] != '\0')
        {
            hb_buffer_realloc(out, ++out->size);
            out->data[out->size - 1] = '\0';
        }
        
        // If the input packet was non-empty, do not pass through
        // an empty output packet (even if the subtitle was empty),
        // as this would be interpreted as an end-of-stream
        if ( in->size > 0 && out->size == 0 ) {
            hb_buffer_close(&out);
        }
    }
    
    // Dispose the input packet, as it is no longer needed
    hb_buffer_close(&in);
    
    *buf_in = NULL;
    *buf_out = out;
    return HB_WORK_OK;
}
Exemple #9
0
void hb_buffer_reduce( hb_buffer_t * b, int size )
{
    if ( size < b->alloc / 8 || b->data == NULL )
    {
        hb_buffer_t * tmp = hb_buffer_init( size );

        hb_buffer_swap_copy( b, tmp );
        memcpy( b->data, tmp->data, size );
        hb_buffer_close( &tmp );
    }
}
Exemple #10
0
void hb_rotate_close( hb_filter_private_t * pv )
{
    if( !pv )
    {
        return;
    }

    /* Cleanup frame buffers */
    if( pv->buf_out )
    {
        hb_buffer_close( &pv->buf_out );
    }
    if (pv->buf_settings )
    {
        hb_buffer_close( &pv->buf_settings );
    }

    int i;
    for( i = 0; i < pv->cpu_count; i++)
    {
        /*
         * Tell each rotate thread to stop, and then cleanup.
         */
        pv->rotate_arguments[i].stop = 1;
        hb_unlock(  pv->rotate_begin_lock[i] );
    
        hb_thread_close( &pv->rotate_threads[i] );
        hb_lock_close( &pv->rotate_begin_lock[i] );
        hb_lock_close( &pv->rotate_complete_lock[i] );
    }
    
    /*
     * free memory for rotate structs
     */
    free( pv->rotate_threads );
    free( pv->rotate_begin_lock );
    free( pv->rotate_complete_lock );
    free( pv->rotate_arguments );

    free( pv );
}
Exemple #11
0
/***********************************************************************
 * Close Video
 ***********************************************************************
 *
 **********************************************************************/
void syncVideoClose( hb_work_object_t * w )
{
    hb_work_private_t * pv = w->private_data;
    hb_job_t          * job   = pv->job;
    hb_sync_video_t   * sync = &pv->type.video;

    // Wake up audio sync if it's still waiting on condition.
    pv->common->pts_offset = 0;
    pv->common->start_found = 1;
    hb_cond_broadcast( pv->common->next_frame );

    if( sync->cur )
    {
        hb_buffer_close( &sync->cur );
    }

    hb_log( "sync: got %d frames, %d expected",
            pv->common->count_frames, sync->count_frames_max );

    /* save data for second pass */
    if( job->pass == 1 )
    {
        /* Preserve frame count for better accuracy in pass 2 */
        hb_interjob_t * interjob = hb_interjob_get( job->h );
        interjob->frame_count = pv->common->count_frames;
        interjob->last_job = job->sequence_id;
    }

    if (sync->drops || sync->dups )
    {
        hb_log( "sync: %d frames dropped, %d duplicated", 
                sync->drops, sync->dups );
    }

    hb_lock( pv->common->mutex );
    if ( --pv->common->ref == 0 )
    {
        hb_unlock( pv->common->mutex );
        hb_cond_close( &pv->common->next_frame );
        hb_lock_close( &pv->common->mutex );
        free( pv->common->first_pts );
        free( pv->common );
    }
    else
    {
        hb_unlock( pv->common->mutex );
    }

    free( pv );
    w->private_data = NULL;
}
Exemple #12
0
static int AVIEnd( hb_mux_object_t * m )
{
    hb_job_t * job = m->job;

    hb_log( "muxavi: writing index" );
    AddIndex( m );

    hb_log( "muxavi: closing %s", job->file );
    fclose( m->file );

    hb_buffer_close( &m->index );

    return 0;
}
Exemple #13
0
void hb_fifo_flush( hb_fifo_t * f )
{
    hb_buffer_t * b;

    while( ( b = hb_fifo_get( f ) ) )
    {
        hb_buffer_close( &b );
    }
    hb_lock( f->lock );
    hb_cond_signal( f->cond_empty );
    hb_cond_signal( f->cond_full );
    hb_unlock( f->lock );

}
Exemple #14
0
static void push_buf( const hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
{
    while ( !*r->die && !r->job->done )
    {
        if ( hb_fifo_full_wait( fifo ) )
        {
            hb_fifo_push( fifo, buf );
            buf = NULL;
            break;
        }
    }
    if ( buf )
    {
        hb_buffer_close( &buf );
    }
}
Exemple #15
0
// Some encoders (e.g. flac) require a final NULL encode in order to
// finalize things.
static void Finalize( hb_work_object_t * w )
{
    hb_work_private_t * pv = w->private_data;
    hb_buffer_t * buf = hb_buffer_init( pv->output_bytes );

    // Finalize with NULL input needed by FLAC to generate md5sum
    // in context extradata
    avcodec_encode_audio( pv->context, buf->data, buf->alloc, NULL );
    hb_buffer_close( &buf );

    // Then we need to recopy the header since it was modified
    if ( pv->context->extradata )
    {
        memcpy( w->config->extradata.bytes, pv->context->extradata, pv->context->extradata_size );
        w->config->extradata.length = pv->context->extradata_size;
    }
}
Exemple #16
0
void hb_fifo_close( hb_fifo_t ** _f )
{
    hb_fifo_t   * f = *_f;
    hb_buffer_t * b;

    hb_deep_log( 2, "fifo_close: trashing %d buffer(s)", hb_fifo_size( f ) );
    while( ( b = hb_fifo_get( f ) ) )
    {
        hb_buffer_close( &b );
    }

    hb_lock_close( &f->lock );
    hb_cond_close( &f->cond_empty );
    hb_cond_close( &f->cond_full );
    free( f );

    *_f = NULL;
}
Exemple #17
0
static hb_buffer_t * splice_discontinuity( hb_work_private_t *r, hb_buffer_t *buf )
{
    // Handle buffers that were split across a PCR discontinuity.
    // Rejoin them into a single buffer.
    hb_buffer_list_t * list = get_splice_list(r, buf->s.id);
    if (list != NULL)
    {
        hb_buffer_list_append(list, buf);
        if (buf->s.split)
        {
            return NULL;
        }

        int count = hb_buffer_list_count(list);
        if (count > 1)
        {
            int size = hb_buffer_list_size(list);
            hb_buffer_t * b = hb_buffer_init(size);
            buf = hb_buffer_list_head(list);
            b->s = buf->s;

            int pos = 0;
            while ((buf = hb_buffer_list_rem_head(list)) != NULL)
            {
                memcpy(b->data + pos, buf->data, buf->size);
                pos += buf->size;
                hb_buffer_close(&buf);
            }
            buf = b;
        }
        else
        {
            buf = hb_buffer_list_clear(list);
        }
    }
    return buf;
}
Exemple #18
0
/***********************************************************************
 * SyncAudio
 ***********************************************************************
 *
 **********************************************************************/
static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                       hb_buffer_t ** buf_out )
{
    hb_work_private_t * pv = w->private_data;
    hb_job_t        * job = pv->job;
    hb_sync_audio_t * sync = &pv->type.audio;
    hb_buffer_t     * buf;
    int64_t start;

    *buf_out = NULL;
    buf = *buf_in;
    *buf_in = NULL;
    /* if the next buffer is an eof send it downstream */
    if ( buf->size <= 0 )
    {
        hb_buffer_close( &buf );
        *buf_out = hb_buffer_init( 0 );
        pv->common->first_pts[sync->index+1] = INT64_MAX - 1;
        return HB_WORK_DONE;
    }

    /* Wait till we can determine the initial pts of all streams */
    if( pv->common->pts_offset == INT64_MIN )
    {
        pv->common->first_pts[sync->index+1] = buf->s.start;
        hb_lock( pv->common->mutex );
        while( pv->common->pts_offset == INT64_MIN )
        {
            // Full fifos will make us wait forever, so get the
            // pts offset from the available streams if full
            if (hb_fifo_is_full(w->fifo_in))
            {
                getPtsOffset( w );
                hb_cond_broadcast( pv->common->next_frame );
            }
            else if ( checkPtsOffset( w ) )
                hb_cond_broadcast( pv->common->next_frame );
            else
                hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
        }
        hb_unlock( pv->common->mutex );
    }

    /* Wait for start frame if doing point-to-point */
    hb_lock( pv->common->mutex );
    start = buf->s.start - pv->common->audio_pts_slip;
    while ( !pv->common->start_found )
    {
        if ( pv->common->audio_pts_thresh < 0 )
        {
            // I would initialize this in hb_sync_init, but 
            // job->pts_to_start can be modified by reader 
            // after hb_sync_init is called.
            pv->common->audio_pts_thresh = job->pts_to_start;
        }
        if ( buf->s.start < pv->common->audio_pts_thresh )
        {
            hb_buffer_close( &buf );
            hb_unlock( pv->common->mutex );
            return HB_WORK_OK;
        }
        while ( !pv->common->start_found && 
                buf->s.start >= pv->common->audio_pts_thresh )
        {
            hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 10 );
            // There is an unfortunate unavoidable deadlock that can occur.
            // Since we need to wait for a specific frame in syncVideoWork,
            // syncAudioWork can be stalled indefinitely.  The video decoder
            // often drops multiple of the initial frames after starting
            // because they require references that have not been decoded yet.
            // This allows a lot of audio to be queued in the fifo and the
            // audio fifo fills before we get a single video frame.  So we
            // must drop some audio to unplug the pipeline and allow the first
            // video frame to be decoded.
            if ( hb_fifo_is_full(w->fifo_in) )
            {
                hb_buffer_t *tmp;
                tmp = buf = hb_fifo_get( w->fifo_in );
                while ( tmp )
                {
                    tmp = hb_fifo_get( w->fifo_in );
                    if ( tmp )
                    {
                        hb_buffer_close( &buf );
                        buf = tmp;
                    }
                }
            }
        }
        start = buf->s.start - pv->common->audio_pts_slip;
    }
    if ( start < 0 )
    {
        hb_buffer_close( &buf );
        hb_unlock( pv->common->mutex );
        return HB_WORK_OK;
    }
    hb_unlock( pv->common->mutex );

    if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop )
    {
        hb_buffer_close( &buf );
        *buf_out = hb_buffer_init( 0 );
        return HB_WORK_DONE;
    }

    if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
    {
        hb_buffer_close( &buf );
        *buf_out = hb_buffer_init( 0 );
        return HB_WORK_DONE;
    }

    // audio time went backwards.
    // If our output clock is more than a half frame ahead of the
    // input clock drop this frame to move closer to sync.
    // Otherwise drop frames until the input clock matches the output clock.
    if ( sync->next_start - start > 90*15 )
    {
        // Discard data that's in the past.
        if ( sync->first_drop == 0 )
        {
            sync->first_drop = start;
        }
        ++sync->drop_count;
        hb_buffer_close( &buf );
        return HB_WORK_OK;
    }
    if ( sync->first_drop )
    {
        // we were dropping old data but input buf time is now current
        hb_log( "sync: audio 0x%x time went backwards %d ms, dropped %d frames "
                "(start %"PRId64", next %"PRId64")", w->audio->id,
                (int)( sync->next_start - sync->first_drop ) / 90,
                sync->drop_count, sync->first_drop, (int64_t)sync->next_start );
        sync->first_drop = 0;
        sync->drop_count = 0;
    }
    if ( start - sync->next_start >= (90 * 70) )
    {
        if ( start - sync->next_start > (90000LL * 60) )
        {
            // there's a gap of more than a minute between the last
            // frame and this. assume we got a corrupted timestamp
            // and just drop the next buf.
            hb_log( "sync: %d minute time gap in audio 0x%x - dropping buf"
                    "  start %"PRId64", next %"PRId64,
                    (int)((start - sync->next_start) / (90000*60)),
                    w->audio->id, start, (int64_t)sync->next_start );
            hb_buffer_close( &buf );
            return HB_WORK_OK;
        }
        /*
         * there's a gap of at least 70ms between the last
         * frame we processed & the next. Fill it with silence.
         * Or in the case of DCA, skip some frames from the
         * other streams.
         */
        if ( sync->drop_video_to_sync )
        {
            hb_log( "sync: audio gap %d ms. Skipping frames. Audio 0x%x"
                    "  start %"PRId64", next %"PRId64,
                    (int)((start - sync->next_start) / 90),
                    w->audio->id, start, (int64_t)sync->next_start );
            hb_lock( pv->common->mutex );
            pv->common->audio_pts_slip += (start - sync->next_start);
            pv->common->video_pts_slip += (start - sync->next_start);
            hb_unlock( pv->common->mutex );
            *buf_out = OutputAudioFrame( w->audio, buf, sync );
            return HB_WORK_OK;
        }
        hb_log( "sync: adding %d ms of silence to audio 0x%x"
                "  start %"PRId64", next %"PRId64,
                (int)((start - sync->next_start) / 90),
                w->audio->id, start, (int64_t)sync->next_start );
        InsertSilence( w, start - sync->next_start );
    }

    /*
     * When we get here we've taken care of all the dups and gaps in the
     * audio stream and are ready to inject the next input frame into
     * the output stream.
     */
    *buf_out = OutputAudioFrame( w->audio, buf, sync );
    return HB_WORK_OK;
}
Exemple #19
0
/***********************************************************************
 * syncVideoWork
 ***********************************************************************
 *
 **********************************************************************/
int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
              hb_buffer_t ** buf_out )
{
    hb_buffer_t * cur, * next, * sub = NULL;
    hb_work_private_t * pv = w->private_data;
    hb_job_t          * job = pv->job;
    hb_subtitle_t     * subtitle;
    hb_sync_video_t   * sync = &pv->type.video;
    int i;
    int64_t next_start;

    *buf_out = NULL;
    next = *buf_in;
    *buf_in = NULL;

    /* Wait till we can determine the initial pts of all streams */
    if( next->size != 0 && pv->common->pts_offset == INT64_MIN )
    {
        pv->common->first_pts[0] = next->s.start;
        hb_lock( pv->common->mutex );
        while( pv->common->pts_offset == INT64_MIN )
        {
            // Full fifos will make us wait forever, so get the
            // pts offset from the available streams if full
            if ( hb_fifo_is_full( job->fifo_raw ) )
            {
                getPtsOffset( w );
                hb_cond_broadcast( pv->common->next_frame );
            }
            else if ( checkPtsOffset( w ) )
                hb_cond_broadcast( pv->common->next_frame );
            else
                hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
        }
        hb_unlock( pv->common->mutex );
    }

    hb_lock( pv->common->mutex );
    next_start = next->s.start - pv->common->video_pts_slip;
    hb_unlock( pv->common->mutex );

    /* Wait for start of point-to-point encoding */
    if( !pv->common->start_found )
    {
        hb_sync_video_t   * sync = &pv->type.video;

        if( next->size == 0 )
        {
            *buf_out = next;
            pv->common->start_found = 1;
            pv->common->first_pts[0] = INT64_MAX - 1;
            hb_cond_broadcast( pv->common->next_frame );

            /*
             * Push through any subtitle EOFs in case they 
             * were not synced through.
             */
            for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
            {
                subtitle = hb_list_item( job->list_subtitle, i );
                if( subtitle->config.dest == PASSTHRUSUB )
                {
                    hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
                }
            }
            return HB_WORK_DONE;
        }
        if ( pv->common->count_frames < job->frame_to_start ||
             next->s.start < job->pts_to_start )
        {
            // Flush any subtitles that have pts prior to the
            // current frame
            for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
            {
                subtitle = hb_list_item( job->list_subtitle, i );
                while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
                {
                    if ( sub->s.start > next->s.start )
                        break;
                    sub = hb_fifo_get( subtitle->fifo_raw );
                    hb_buffer_close( &sub );
                }
            }
            hb_lock( pv->common->mutex );
            // Tell the audio threads what must be dropped
            pv->common->audio_pts_thresh = next_start + pv->common->video_pts_slip;
            hb_cond_broadcast( pv->common->next_frame );
            hb_unlock( pv->common->mutex );

            UpdateSearchState( w, next_start );
            hb_buffer_close( &next );

            return HB_WORK_OK;
        }
        hb_lock( pv->common->mutex );
        pv->common->audio_pts_thresh = 0;
        pv->common->audio_pts_slip += next_start;
        pv->common->video_pts_slip += next_start;
        next_start = 0;
        pv->common->start_found = 1;
        pv->common->count_frames = 0;
        hb_cond_broadcast( pv->common->next_frame );
        hb_unlock( pv->common->mutex );
        sync->st_first = 0;
    }

    if( !sync->cur )
    {
        sync->cur = next;
        if (next->size == 0)
        {
            /* we got an end-of-stream as our first video packet? 
             * Feed it downstream & signal that we're done. 
             */
            *buf_out = next;

            pv->common->start_found = 1;
            pv->common->first_pts[0] = INT64_MAX - 1;
            hb_cond_broadcast( pv->common->next_frame );

            /*
             * Push through any subtitle EOFs in case they 
             * were not synced through.
             */
            for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
            {
                subtitle = hb_list_item( job->list_subtitle, i );
                if( subtitle->config.dest == PASSTHRUSUB )
                {
                    hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
                }
            }
            return HB_WORK_DONE;
        }
        return HB_WORK_OK;
    }
    cur = sync->cur;
    /* At this point we have a frame to process. Let's check
        1) if we will be able to push into the fifo ahead
        2) if the next frame is there already, since we need it to
           compute the duration of the current frame*/
    if( next->size == 0 )
    {
        hb_buffer_close( &next );

        pv->common->first_pts[0] = INT64_MAX - 1;
        cur->s.start = sync->next_start;
        cur->s.stop = cur->s.start + 90000. / ((double)job->vrate / (double)job->vrate_base);
        sync->next_start += cur->s.stop - cur->s.start;;

        /* Make sure last frame is reflected in frame count */
        pv->common->count_frames++;

        /* Push the frame to the renderer */
        *buf_out = cur;
        sync->cur = NULL;

        /* we got an end-of-stream. Feed it downstream & signal that
         * we're done. Note that this means we drop the final frame of
         * video (we don't know its duration). On DVDs the final frame
         * is often strange and dropping it seems to be a good idea. */
        (*buf_out)->next = hb_buffer_init( 0 );

        /*
         * Push through any subtitle EOFs in case they were not synced through.
         */
        for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
        {
            subtitle = hb_list_item( job->list_subtitle, i );
            if( subtitle->config.dest == PASSTHRUSUB )
            {
                hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
            }
        }
        pv->common->start_found = 1;
        hb_cond_broadcast( pv->common->next_frame );
        return HB_WORK_DONE;
    }

    /* Check for end of point-to-point frame encoding */
    if( job->frame_to_stop && pv->common->count_frames > job->frame_to_stop )
    {
        // Drop an empty buffer into our output to ensure that things
        // get flushed all the way out.
        hb_buffer_close( &sync->cur );
        hb_buffer_close( &next );
        *buf_out = hb_buffer_init( 0 );
        hb_log( "sync: reached %d frames, exiting early",
                pv->common->count_frames );

        /*
         * Push through any subtitle EOFs in case they were not synced through.
         */
        for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
        {
            subtitle = hb_list_item( job->list_subtitle, i );
            if( subtitle->config.dest == PASSTHRUSUB )
            {
                hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
            }
        }
        return HB_WORK_DONE;
    }

    /* Check for end of point-to-point pts encoding */
    if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
    {
        // Drop an empty buffer into our output to ensure that things
        // get flushed all the way out.
        hb_log( "sync: reached pts %"PRId64", exiting early", cur->s.start );
        hb_buffer_close( &sync->cur );
        hb_buffer_close( &next );
        *buf_out = hb_buffer_init( 0 );

        /*
         * Push through any subtitle EOFs in case they were not synced through.
         */
        for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
        {
            subtitle = hb_list_item( job->list_subtitle, i );
            if( subtitle->config.dest == PASSTHRUSUB )
            {
                hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
            }
        }
        return HB_WORK_DONE;
    }

    if( sync->first_frame )
    {
        /* This is our first frame */
        if ( cur->s.start > 0 )
        {
            /*
             * The first pts from a dvd should always be zero but
             * can be non-zero with a transport or program stream since
             * we're not guaranteed to start on an IDR frame. If we get
             * a non-zero initial PTS extend its duration so it behaves
             * as if it started at zero so that our audio timing will
             * be in sync.
             */
            hb_log( "sync: first pts is %"PRId64, cur->s.start );
            cur->s.start = 0;
        }
        sync->first_frame = 0;
    }

    /*
     * since the first frame is always 0 and the upstream reader code
     * is taking care of adjusting for pts discontinuities, we just have
     * to deal with the next frame's start being in the past. This can
     * happen when the PTS is adjusted after data loss but video frame
     * reordering causes some frames with the old clock to appear after
     * the clock change. This creates frames that overlap in time which
     * looks to us like time going backward. The downstream muxing code
     * can deal with overlaps of up to a frame time but anything larger
     * we handle by dropping frames here.
     */
    if ( next_start - cur->s.start <= 0 )
    {
        if ( sync->first_drop == 0 )
        {
            sync->first_drop = next_start;
        }
        ++sync->drop_count;
        if ( next->s.new_chap )
        {
            // don't drop a chapter mark when we drop the buffer
            sync->chap_mark = next->s.new_chap;
        }
        hb_buffer_close( &next );
        return HB_WORK_OK;
    }
    if ( sync->first_drop )
    {
        hb_log( "sync: video time didn't advance - dropped %d frames "
                "(delta %d ms, current %"PRId64", next %"PRId64", dur %d)",
                sync->drop_count, (int)( cur->s.start - sync->first_drop ) / 90,
                cur->s.start, next_start, (int)( next_start - cur->s.start ) );
        sync->first_drop = 0;
        sync->drop_count = 0;
    }

    /*
     * Track the video sequence number locally so that we can sync the audio
     * to it using the sequence number as well as the PTS.
     */
    sync->video_sequence = cur->sequence;
    
    /* Process subtitles that apply to this video frame */
    // NOTE: There is no logic in either subtitle-sync algorithm that waits
    // for the subtitle-decoder if it is lagging behind the video-decoder.
    //       
    // Therefore there is the implicit assumption that the subtitle-decoder 
    // is always faster than the video-decoder. This assumption is definitely 
    // incorrect in some cases where the SSA subtitle decoder is used.

    for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
    {
        int64_t sub_start, sub_stop, duration;

        subtitle = hb_list_item( job->list_subtitle, i );
        
        // Sanitize subtitle start and stop times, then pass to 
        // muxer or renderer filter.
        while ( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) != NULL )
        {
            hb_lock( pv->common->mutex );
            sub_start = sub->s.start - pv->common->video_pts_slip;
            hb_unlock( pv->common->mutex );

            if (sub->s.stop == -1)
            {
                if (subtitle->config.dest != RENDERSUB &&
                    hb_fifo_size( subtitle->fifo_raw ) < 2)
                {
                    // For passthru subs, we want to wait for the
                    // next subtitle so that we can fill in the stop time.
                    // This way the muxer can compute the duration of
                    // the subtitle.
                    //
                    // For render subs, we need to ensure that they
                    // get to the renderer before the associated video
                    // that they are to be applied to.  It is the 
                    // responsibility of the renderer to handle
                    // stop == -1.
                    break;
                }
            }

            sub = hb_fifo_get( subtitle->fifo_raw );
            if ( sub->s.stop == -1 )
            {
                hb_buffer_t *next;
                next = hb_fifo_see( subtitle->fifo_raw );
                if (next != NULL)
                    sub->s.stop = next->s.start;
            }
            // Need to re-write subtitle timestamps to account
            // for any slippage.
            sub_stop = -1;
            if ( sub->s.stop != -1 )
            {
                duration = sub->s.stop - sub->s.start;
                sub_stop = sub_start + duration;
            }

            sub->s.start = sub_start;
            sub->s.stop = sub_stop;

            hb_fifo_push( subtitle->fifo_out, sub );
        }
    }

    /*
     * Adjust the pts of the current frame so that it's contiguous
     * with the previous frame. The start time of the current frame
     * has to be the end time of the previous frame and the stop
     * time has to be the start of the next frame.  We don't
     * make any adjustments to the source timestamps other than removing
     * the clock offsets (which also removes pts discontinuities).
     * This means we automatically encode at the source's frame rate.
     * MP2 uses an implicit duration (frames end when the next frame
     * starts) but more advanced containers like MP4 use an explicit
     * duration. Since we're looking ahead one frame we set the
     * explicit stop time from the start time of the next frame.
     */
    *buf_out = cur;
    int64_t duration = next_start - cur->s.start;
    sync->cur = cur = next;
    cur->sub = NULL;
    cur->s.start -= pv->common->video_pts_slip;
    cur->s.stop -= pv->common->video_pts_slip;
    sync->pts_skip = 0;
    if ( duration <= 0 )
    {
        hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
                duration, cur->s.start, next_start );
    }

    (*buf_out)->s.start = sync->next_start;
    sync->next_start += duration;
    (*buf_out)->s.stop = sync->next_start;

    if ( sync->chap_mark )
    {
        // we have a pending chapter mark from a recent drop - put it on this
        // buffer (this may make it one frame late but we can't do any better).
        (*buf_out)->s.new_chap = sync->chap_mark;
        sync->chap_mark = 0;
    }

    /* Update UI */
    UpdateState( w );

    return HB_WORK_OK;
}
Exemple #20
0
/*
 * SSA line format:
 *   Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
 *             1      2     3   4     5    6       7       8       9      10
 *
 * MKV-SSA packet format:
 *   ReadOrder,Marked,          Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
 *   1         2                3     4    5       6       7       8      9
 */
static hb_buffer_t *
ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, hb_buffer_t * in,
                            uint8_t *in_data, int in_size )
{
    hb_work_private_t * pv = w->private_data;
    hb_buffer_t * out;

    // Parse values for in->s.start and in->s.stop
    int64_t in_start, in_stop;
    if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) )
        goto fail;

    // Convert the SSA packet to MKV-SSA format, which is what libass expects
    char *mkvIn;
    int numPartsRead;
    char *styleToTextFields;
    char *layerField = malloc( in_size );

    // SSA subtitles have an empty layer field (bare ',').  The scanf
    // format specifier "%*128[^,]" will not match on a bare ','.  There
    // must be at least one non ',' character in the match.  So the format
    // specifier is placed directly next to the ':' so that the next
    // expected ' ' after the ':' will be the character it matches on
    // when there is no layer field.
    numPartsRead = sscanf( (char *)in_data, "Dialogue:%128[^,],", layerField );
    if ( numPartsRead != 1 )
        goto fail;

    styleToTextFields = (char *)find_field( in_data, in_data + in_size, 4 );
    if ( styleToTextFields == NULL ) {
        free( layerField );
        goto fail;
    }

    // The sscanf conversion above will result in an extra space
    // before the layerField.  Strip the space.
    char *stripLayerField = layerField;
    for(; *stripLayerField == ' '; stripLayerField++);

    out = hb_buffer_init( in_size + 1 );
    mkvIn = (char*)out->data;

    mkvIn[0] = '\0';
    sprintf(mkvIn, "%d", pv->readOrder++);    // ReadOrder: make this up
    strcat( mkvIn, "," );
    strcat( mkvIn, stripLayerField );
    strcat( mkvIn, "," );
    strcat( mkvIn, (char *)styleToTextFields );

    out->size           = strlen(mkvIn) + 1;
    out->s.frametype    = HB_FRAME_SUBTITLE;
    out->s.start        = in->s.start;
    out->s.duration     = in_stop - in_start;
    out->s.stop         = in->s.start + out->s.duration;
    out->s.scr_sequence = in->s.scr_sequence;

    if( out->size == 0 )
    {
        hb_buffer_close(&out);
    }

    free( layerField );

    return out;

fail:
    hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data );
    return NULL;
}
Exemple #21
0
static hb_buffer_t* nal_encode(hb_work_object_t *w,
                               x265_picture *pic_out,
                               x265_nal *nal, uint32_t nnal)
{
    hb_work_private_t *pv = w->private_data;
    hb_job_t *job         = pv->job;
    hb_buffer_t *buf      = NULL;
    int i;

    if (nnal <= 0)
    {
        return NULL;
    }

    buf = hb_video_buffer_init(job->width, job->height);
    if (buf == NULL)
    {
        return NULL;
    }

    buf->size = 0;
    // copy the bitstream data
    for (i = 0; i < nnal; i++)
    {
        memcpy(buf->data + buf->size, nal[i].payload, nal[i].sizeBytes);
        buf->size += nal[i].sizeBytes;
    }

    // use the pts to get the original frame's duration.
    buf->s.duration     = get_frame_duration(pv, pic_out->pts);
    buf->s.stop         = pic_out->pts + buf->s.duration;
    buf->s.start        = pic_out->pts;
    buf->s.renderOffset = pic_out->dts;
    if (w->config->h264.init_delay == 0 && pic_out->dts < 0)
    {
        w->config->h264.init_delay -= pic_out->dts;
    }

    switch (pic_out->sliceType)
    {
        case X265_TYPE_IDR:
            buf->s.frametype = HB_FRAME_IDR;
            break;
        case X265_TYPE_I:
            buf->s.frametype = HB_FRAME_I;
            break;
        case X265_TYPE_P:
            buf->s.frametype = HB_FRAME_P;
            break;
        case X265_TYPE_B:
            buf->s.frametype = HB_FRAME_B;
            break;
        case X265_TYPE_BREF:
            buf->s.frametype = HB_FRAME_BREF;
            break;
        default:
            buf->s.frametype = 0;
            break;
    }

    if (pv->next_chapter_pts != AV_NOPTS_VALUE &&
        pv->next_chapter_pts <= pic_out->pts   &&
        pic_out->sliceType   == X265_TYPE_IDR)
    {
        // we're no longer looking for this chapter
        pv->next_chapter_pts = AV_NOPTS_VALUE;

        // get the chapter index from the list
        struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0);
        if (item != NULL)
        {
            // we're done with this chapter
            hb_list_rem(pv->delayed_chapters, item);
            buf->s.new_chap = item->index;
            free(item);

            // we may still have another pending chapter
            item = hb_list_item(pv->delayed_chapters, 0);
            if (item != NULL)
            {
                // we're looking for this one now
                // we still need it, don't remove it
                pv->next_chapter_pts = item->start;
            }
        }
    }

    // discard empty buffers (no video)
    if (buf->size <= 0)
    {
        hb_buffer_close(&buf);
    }
    return buf;
}
Exemple #22
0
static hb_buffer_t* Encode(hb_work_object_t *w)
{
    hb_work_private_t *pv = w->private_data;
    hb_audio_t *audio = w->audio;
    uint64_t pts, pos;

    if (hb_list_bytes(pv->list) < pv->input_samples * sizeof(float))
    {
        return NULL;
    }

    hb_list_getbytes(pv->list, pv->input_buf, pv->input_samples * sizeof(float),
                     &pts, &pos);

    // Prepare input frame
    int out_linesize;
    int out_size = av_samples_get_buffer_size(&out_linesize,
                                              pv->context->channels,
                                              pv->samples_per_frame,
                                              pv->context->sample_fmt, 1);
    AVFrame frame = { .nb_samples = pv->samples_per_frame, };
    avcodec_fill_audio_frame(&frame,
                             pv->context->channels, pv->context->sample_fmt,
                             pv->output_buf, out_size, 1);
    if (pv->avresample != NULL)
    {
        int in_linesize;
        av_samples_get_buffer_size(&in_linesize, pv->context->channels,
                                   frame.nb_samples, AV_SAMPLE_FMT_FLT, 1);
        int out_samples = avresample_convert(pv->avresample,
                                             frame.extended_data, out_linesize,
                                             frame.nb_samples,
                                             &pv->input_buf,       in_linesize,
                                             frame.nb_samples);
        if (out_samples != pv->samples_per_frame)
        {
            // we're not doing sample rate conversion, so this shouldn't happen
            hb_log("encavcodecaWork: avresample_convert() failed");
            return NULL;
        }
    }

    // Libav requires that timebase of audio frames be in sample_rate units
    frame.pts = pts + (90000 * pos / (sizeof(float) *
                                      pv->out_discrete_channels *
                                      audio->config.out.samplerate));
    frame.pts = av_rescale(frame.pts, pv->context->sample_rate, 90000);

    // Prepare output packet
    AVPacket pkt;
    int got_packet;
    hb_buffer_t *out = hb_buffer_init(pv->max_output_bytes);
    av_init_packet(&pkt);
    pkt.data = out->data;
    pkt.size = out->alloc;

    // Encode
    int ret = avcodec_encode_audio2(pv->context, &pkt, &frame, &got_packet);
    if (ret < 0)
    {
        hb_log("encavcodeca: avcodec_encode_audio failed");
        hb_buffer_close(&out);
        return NULL;
    }

    if (got_packet && pkt.size)
    {
        out->size = pkt.size;

        // The output pts from libav is in context->time_base. Convert it back
        // to our timebase.
        //
        // Also account for the "delay" factor that libav seems to arbitrarily
        // subtract from the packet.  Not sure WTH they think they are doing by
        // offsetting the value in a negative direction.
        out->s.start = av_rescale_q(pv->context->delay + pkt.pts,
                                    pv->context->time_base,
                                    (AVRational){1, 90000});

        out->s.stop  = out->s.start + (90000 * pv->samples_per_frame /
                                       audio->config.out.samplerate);

        out->s.type = AUDIO_BUF;
        out->s.frametype = HB_FRAME_AUDIO;
    }
    else
    {
        hb_buffer_close(&out);
        return Encode(w);
    }

    return out;
}

static hb_buffer_t * Flush( hb_work_object_t * w )
{
    hb_buffer_t *first, *buf, *last;

    first = last = buf = Encode( w );
    while( buf )
    {
        last = buf;
        buf->next = Encode( w );
        buf = buf->next;
    }

    if( last )
    {
        last->next = hb_buffer_init( 0 );
    }
    else
    {
        first = hb_buffer_init( 0 );
    }

    return first;
}
Exemple #23
0
/***********************************************************************
 * DecodePreviews
 ***********************************************************************
 * Decode 10 pictures for the given title.
 * It assumes that data->reader and data->vts have successfully been
 * DVDOpen()ed and ifoOpen()ed.
 **********************************************************************/
static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
    int             i, npreviews = 0;
    hb_buffer_t   * buf_ps, * buf_es;
    hb_list_t     * list_es;
    int progressive_count = 0;
    int interlaced_preview_count = 0;
    info_list_t * info_list = calloc( data->preview_count+1, sizeof(*info_list) );
    crop_record_t *crops = calloc( 1, sizeof(*crops) );

    buf_ps   = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
    list_es  = hb_list_init();

    hb_log( "scan: decoding previews for title %d", title->index );

    if (data->bd)
    {
        hb_bd_start( data->bd, title );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->dvd)
    {
        hb_dvd_start( data->dvd, title, 1 );
        title->angle_count = hb_dvd_angle_count( data->dvd );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->batch)
    {
        data->stream = hb_stream_open( title->path, title );
    }

    for( i = 0; i < data->preview_count; i++ )
    {
        int j;
        FILE * file_preview;
        char   filename[1024];

        if ( *data->die )
        {
            return 0;
        }
        if (data->bd)
        {
            if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        if (data->dvd)
        {
            if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        else if (data->stream)
        {
          /* we start reading streams at zero rather than 1/11 because
           * short streams may have only one sequence header in the entire
           * file and we need it to decode any previews. */
          if (!hb_stream_seek(data->stream, (float) i / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }

        hb_deep_log( 2, "scan: preview %d", i + 1 );

        int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
#if defined(USE_FF_MPEG2)
        if (vcodec == WORK_DECMPEG2)
        {
            vcodec = WORK_DECAVCODECV;
            title->video_codec_param = CODEC_ID_MPEG2VIDEO;
        }
#endif
        hb_work_object_t *vid_decoder = hb_get_work( vcodec );
        vid_decoder->codec_param = title->video_codec_param;
        vid_decoder->title = title;
        vid_decoder->init( vid_decoder, NULL );
        hb_buffer_t * vid_buf = NULL;
        int vidskip = 0;

        if ( title->flags & HBTF_NO_IDR )
        {
            // title doesn't have IDR frames so we decode but drop one second's
            // worth of frames to allow the decoder to converge.
            if ( ! title->rate_base )
            {
                vidskip = 30;
            }
            else
            {
                vidskip = (double)title->rate / (double)title->rate_base + 0.5;
            }
            // If it's a BD, we can relax this a bit. Since seeks will
            // at least get us to a recovery point.
            if (data->bd)
                vidskip = 4;
        }

        for( j = 0; j < 10240 ; j++ )
        {
            if (data->bd)
            {
              if( !hb_bd_read( data->bd, buf_ps ) )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            if (data->dvd)
            {
              if( !hb_dvd_read( data->dvd, buf_ps ) )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            else if (data->stream)
            {
              if ( !hb_stream_read(data->stream,buf_ps) )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            (hb_demux[title->demuxer])(buf_ps, list_es, 0 );

            while( ( buf_es = hb_list_item( list_es, 0 ) ) )
            {
                hb_list_rem( list_es, buf_es );
                if( buf_es->id == title->video_id && vid_buf == NULL )
                {
                    vid_decoder->work( vid_decoder, &buf_es, &vid_buf );
                    if ( vid_buf && vidskip && --vidskip > 0 )
                    {
                        // we're dropping frames to get the video decoder in sync
                        // when the video stream doesn't contain IDR frames
                        while (vid_buf && --vidskip >= 0)
                        {
                            hb_buffer_t * next = vid_buf->next;
                            vid_buf->next = NULL;
                            hb_buffer_close( &vid_buf );
                            vid_buf = next;
                        }
                    }
                }
                else if( ! AllAudioOK( title ) ) 
                {
                    LookForAudio( title, buf_es );
                    buf_es = NULL;
                }
                if ( buf_es )
                    hb_buffer_close( &buf_es );
            }

            if( vid_buf && AllAudioOK( title ) )
                break;
        }

        if( ! vid_buf )
        {
            hb_log( "scan: could not get a decoded picture" );
            continue;
        }

        /* Get size and rate infos */

        hb_work_info_t vid_info;
        if( !vid_decoder->info( vid_decoder, &vid_info ) )
        {
            /*
             * Could not fill vid_info, don't continue and try to use vid_info
             * in this case.
             */
            if (vid_buf)
            {
                hb_buffer_close( &vid_buf );
            }
            vid_decoder->close( vid_decoder );
            free( vid_decoder );
            continue;
        }
        vid_decoder->close( vid_decoder );
        free( vid_decoder );

        remember_info( info_list, &vid_info );

        title->video_codec_name = strdup( vid_info.name );
        title->width = vid_info.width;
        title->height = vid_info.height;
        title->rate = vid_info.rate;
        title->rate_base = vid_info.rate_base;
        title->video_bitrate = vid_info.bitrate;

        if( title->rate_base == 1126125 )
        {
            /* Frame FPS is 23.976 (meaning it's progressive), so
               start keeping track of how many are reporting at
               that speed. When enough show up that way, we want
               to make that the overall title FPS.
            */
            progressive_count++;

            if( progressive_count < 6 )
            {
                /* Not enough frames are reporting as progressive,
                   which means we should be conservative and use
                   29.97 as the title's FPS for now.
                */
                title->rate_base = 900900;
            }
            else
            {
                /* A majority of the scan frames are progressive. Make that
                    the title's FPS, and announce it once to the log.
                */
                if( progressive_count == 6 )
                {
                    hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976");
                }
                title->rate_base = 1126125;
            }
        }
        else if( title->rate_base == 900900 && progressive_count >= 6 )
        {
            /*
             * We've already deduced that the frame rate is 23.976, so set it
             * back again.
             */
            title->rate_base = 1126125;
        }

        while( ( buf_es = hb_list_item( list_es, 0 ) ) )
        {
            hb_list_rem( list_es, buf_es );
            hb_buffer_close( &buf_es );
        }

        /* Check preview for interlacing artifacts */
        if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9, 10, 30, 9 ) )
        {
            hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1);
            interlaced_preview_count++;
        }
        
        if( data->store_previews )
        {
            hb_get_tempory_filename( data->h, filename, "%d_%d_%d",
                                     hb_get_instance_id(data->h), title->index, i );

            file_preview = fopen( filename, "wb" );
            if( file_preview )
            {
                fwrite( vid_buf->data, title->width * title->height * 3 / 2,
                        1, file_preview );
                fclose( file_preview );
            }
            else
            {
                hb_log( "scan: fopen failed (%s)", filename );
            }
        }

        /* Detect black borders */

#define Y    vid_buf->data
        int top, bottom, left, right;
        int h4 = title->height / 4, w4 = title->width / 4;

        // When widescreen content is matted to 16:9 or 4:3 there's sometimes
        // a thin border on the outer edge of the matte. On TV content it can be
        // "line 21" VBI data that's normally hidden in the overscan. For HD
        // content it can just be a diagnostic added in post production so that
        // the frame borders are visible. We try to ignore these borders so
        // we can crop the matte. The border width depends on the resolution
        // (12 pixels on 1080i looks visually the same as 4 pixels on 480i)
        // so we allow the border to be up to 1% of the frame height.
        const int border = title->height / 100;

        for ( top = border; top < h4; ++top )
        {
            if ( ! row_all_dark( title, Y, top ) )
                break;
        }
        if ( top <= border )
        {
            // we never made it past the border region - see if the rows we
            // didn't check are dark or if we shouldn't crop at all.
            for ( top = 0; top < border; ++top )
            {
                if ( ! row_all_dark( title, Y, top ) )
                    break;
            }
            if ( top >= border )
            {
                top = 0;
            }
        }
        for ( bottom = border; bottom < h4; ++bottom )
        {
            if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) )
                break;
        }
        if ( bottom <= border )
        {
            for ( bottom = 0; bottom < border; ++bottom )
            {
                if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) )
                    break;
            }
            if ( bottom >= border )
            {
                bottom = 0;
            }
        }
        for ( left = 0; left < w4; ++left )
        {
            if ( ! column_all_dark( title, Y, top, bottom, left ) )
                break;
        }
        for ( right = 0; right < w4; ++right )
        {
            if ( ! column_all_dark( title, Y, top, bottom, title->width - 1 - right ) )
                break;
        }

        // only record the result if all the crops are less than a quarter of
        // the frame otherwise we can get fooled by frames with a lot of black
        // like titles, credits & fade-thru-black transitions.
        if ( top < h4 && bottom < h4 && left < w4 && right < w4 )
        {
            record_crop( crops, top, bottom, left, right );
        }
        ++npreviews;

skip_preview:
        /* Make sure we found audio rates and bitrates */
        for( j = 0; j < hb_list_count( title->list_audio ); j++ )
        {
            hb_audio_t * audio = hb_list_item( title->list_audio, j );
            if ( audio->priv.scan_cache )
            {
                hb_fifo_flush( audio->priv.scan_cache );
            }
        }
        if (vid_buf)
        {
            hb_buffer_close( &vid_buf );
        }
    }

    if ( data->batch && data->stream )
    {
        hb_stream_close( &data->stream );
    }

    if ( npreviews )
    {
        // use the most common frame info for our final title dimensions
        hb_work_info_t vid_info;
        most_common_info( info_list, &vid_info );

        title->width = vid_info.width;
        title->height = vid_info.height;
        title->pixel_aspect_width = vid_info.pixel_aspect_width;
        title->pixel_aspect_height = vid_info.pixel_aspect_height;

        // compute the aspect ratio based on the storage dimensions and the
        // pixel aspect ratio (if supplied) or just storage dimensions if no PAR.
        title->aspect = (double)title->width / (double)title->height;
        if( title->pixel_aspect_width && title->pixel_aspect_height )
        {
            title->aspect *= (double)title->pixel_aspect_width /
                             (double)title->pixel_aspect_height;

            // For unknown reasons some French PAL DVDs put the original
            // content's aspect ratio into the mpeg PAR even though it's
            // the wrong PAR for the DVD. Apparently they rely on the fact
            // that DVD players ignore the content PAR and just use the
            // aspect ratio from the DVD metadata. So, if the aspect computed
            // from the PAR is different from the container's aspect we use
            // the container's aspect & recompute the PAR from it.
            if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) )
            {
                hb_log("scan: content PAR gives wrong aspect %.2f; "
                       "using container aspect %.2f", title->aspect,
                       title->container_aspect );
                title->aspect = title->container_aspect;
                hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height,
                           (int)(title->aspect * title->height + 0.5), title->width );
            }
        }

        // don't try to crop unless we got at least 3 previews
        if ( crops->n > 2 )
        {
            sort_crops( crops );
            // The next line selects median cropping - at least
            // 50% of the frames will have their borders removed.
            // Other possible choices are loose cropping (i = 0) where 
            // no non-black pixels will be cropped from any frame and a
            // tight cropping (i = crops->n - (crops->n >> 2)) where at
            // least 75% of the frames will have their borders removed.
            i = crops->n >> 1;
            title->crop[0] = EVEN( crops->t[i] );
            title->crop[1] = EVEN( crops->b[i] );
            title->crop[2] = EVEN( crops->l[i] );
            title->crop[3] = EVEN( crops->r[i] );
        }
        free( crops );

        hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, "
                "aspect %s, PAR %d:%d",
                npreviews, title->width, title->height, (float) title->rate /
                (float) title->rate_base,
                title->crop[0], title->crop[1], title->crop[2], title->crop[3],
                aspect_to_string( title->aspect ), title->pixel_aspect_width,
                title->pixel_aspect_height );

        if( interlaced_preview_count >= ( npreviews / 2 ) )
        {
            hb_log("Title is likely interlaced or telecined (%i out of %i previews). You should do something about that.",
                   interlaced_preview_count, npreviews);
            title->detected_interlacing = 1;
        }
        else
        {
            title->detected_interlacing = 0;
        }
    }
Exemple #24
0
/***********************************************************************
 * DecodePreviews
 ***********************************************************************
 * Decode 10 pictures for the given title.
 * It assumes that data->reader and data->vts have successfully been
 * DVDOpen()ed and ifoOpen()ed.
 **********************************************************************/
static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
    int             i, npreviews = 0;
    hb_buffer_t   * buf, * buf_es;
    hb_list_t     * list_es;
    int progressive_count = 0;
    int pulldown_count = 0;
    int doubled_frame_count = 0;
    int interlaced_preview_count = 0;
    info_list_t * info_list = calloc( data->preview_count+1, sizeof(*info_list) );
    crop_record_t *crops = crop_record_init( data->preview_count );

    list_es  = hb_list_init();

    if( data->batch )
    {
        hb_log( "scan: decoding previews for title %d (%s)", title->index, title->path );
    }
    else
    {
        hb_log( "scan: decoding previews for title %d", title->index );
    }

    if (data->bd)
    {
        hb_bd_start( data->bd, title );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->dvd)
    {
        hb_dvd_start( data->dvd, title, 1 );
        title->angle_count = hb_dvd_angle_count( data->dvd );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->batch)
    {
        data->stream = hb_stream_open( title->path, title, 1 );
    }

    int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
#if defined(USE_FF_MPEG2)
    if (vcodec == WORK_DECMPEG2)
    {
        vcodec = WORK_DECAVCODECV;
        title->video_codec_param = CODEC_ID_MPEG2VIDEO;
    }
#endif
    hb_work_object_t *vid_decoder = hb_get_work( vcodec );
    vid_decoder->codec_param = title->video_codec_param;
    vid_decoder->title = title;
    vid_decoder->init( vid_decoder, NULL );

    for( i = 0; i < data->preview_count; i++ )
    {
        int j;

        if ( *data->die )
        {
            free( info_list );
            crop_record_free( crops );
            return 0;
        }
        if (data->bd)
        {
            if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        if (data->dvd)
        {
            if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        else if (data->stream)
        {
          /* we start reading streams at zero rather than 1/11 because
           * short streams may have only one sequence header in the entire
           * file and we need it to decode any previews. */
          if (!hb_stream_seek(data->stream, (float) i / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }

        hb_deep_log( 2, "scan: preview %d", i + 1 );

        if ( vid_decoder->flush )
            vid_decoder->flush( vid_decoder );

        hb_buffer_t * vid_buf = NULL;

        for( j = 0; j < 10240 ; j++ )
        {
            if (data->bd)
            {
              if( (buf = hb_bd_read( data->bd )) == NULL )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            else if (data->dvd)
            {
              if( (buf = hb_dvd_read( data->dvd )) == NULL )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            else if (data->stream)
            {
              if ( (buf = hb_stream_read( data->stream )) == NULL )
              {
                  if ( vid_buf )
                  {
                    break;
                  }
                  hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
                  goto skip_preview;
              }
            }
            else
            {
                // Silence compiler warning
                buf = NULL;
                hb_error( "Error: This can't happen!" );
                goto skip_preview;
            }

            (hb_demux[title->demuxer])(buf, list_es, 0 );

            while( ( buf_es = hb_list_item( list_es, 0 ) ) )
            {
                hb_list_rem( list_es, buf_es );
                if( buf_es->s.id == title->video_id && vid_buf == NULL )
                {
                    vid_decoder->work( vid_decoder, &buf_es, &vid_buf );
                }
                else if( ! AllAudioOK( title ) ) 
                {
                    LookForAudio( title, buf_es );
                    buf_es = NULL;
                }
                if ( buf_es )
                    hb_buffer_close( &buf_es );
            }

            if( vid_buf && AllAudioOK( title ) )
                break;
        }

        if( ! vid_buf )
        {
            hb_log( "scan: could not get a decoded picture" );
            continue;
        }

        /* Get size and rate infos */

        hb_work_info_t vid_info;
        if( !vid_decoder->info( vid_decoder, &vid_info ) )
        {
            /*
             * Could not fill vid_info, don't continue and try to use vid_info
             * in this case.
             */
            if (vid_buf)
            {
                hb_buffer_close( &vid_buf );
            }
            hb_log( "scan: could not get a video information" );
            continue;
        }

        remember_info( info_list, &vid_info );

        if( is_close_to( vid_info.rate_base, 900900, 100 ) &&
            ( vid_buf->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD ) )
        {
            /* Potentially soft telecine material */
            pulldown_count++;
        }

        if( vid_buf->s.flags & PIC_FLAG_REPEAT_FRAME )
        {
            // AVCHD-Lite specifies that all streams are
            // 50 or 60 fps.  To produce 25 or 30 fps, camera
            // makers are repeating all frames.
            doubled_frame_count++;
        }

        if( is_close_to( vid_info.rate_base, 1126125, 100 ) )
        {
            // Frame FPS is 23.976 (meaning it's progressive), so start keeping
            // track of how many are reporting at that speed. When enough 
            // show up that way, we want to make that the overall title FPS.
            progressive_count++;
        }

        while( ( buf_es = hb_list_item( list_es, 0 ) ) )
        {
            hb_list_rem( list_es, buf_es );
            hb_buffer_close( &buf_es );
        }

        /* Check preview for interlacing artifacts */
        if( hb_detect_comb( vid_buf, 10, 30, 9, 10, 30, 9 ) )
        {
            hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1);
            interlaced_preview_count++;
        }
        
        if( data->store_previews )
        {
            hb_save_preview( data->h, title->index, i, vid_buf );
        }

        /* Detect black borders */

        int top, bottom, left, right;
        int h4 = vid_info.height / 4, w4 = vid_info.width / 4;

        // When widescreen content is matted to 16:9 or 4:3 there's sometimes
        // a thin border on the outer edge of the matte. On TV content it can be
        // "line 21" VBI data that's normally hidden in the overscan. For HD
        // content it can just be a diagnostic added in post production so that
        // the frame borders are visible. We try to ignore these borders so
        // we can crop the matte. The border width depends on the resolution
        // (12 pixels on 1080i looks visually the same as 4 pixels on 480i)
        // so we allow the border to be up to 1% of the frame height.
        const int border = vid_info.height / 100;

        for ( top = border; top < h4; ++top )
        {
            if ( ! row_all_dark( vid_buf, top ) )
                break;
        }
        if ( top <= border )
        {
            // we never made it past the border region - see if the rows we
            // didn't check are dark or if we shouldn't crop at all.
            for ( top = 0; top < border; ++top )
            {
                if ( ! row_all_dark( vid_buf, top ) )
                    break;
            }
            if ( top >= border )
            {
                top = 0;
            }
        }
        for ( bottom = border; bottom < h4; ++bottom )
        {
            if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) )
                break;
        }
        if ( bottom <= border )
        {
            for ( bottom = 0; bottom < border; ++bottom )
            {
                if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) )
                    break;
            }
            if ( bottom >= border )
            {
                bottom = 0;
            }
        }
        for ( left = 0; left < w4; ++left )
        {
            if ( ! column_all_dark( vid_buf, top, bottom, left ) )
                break;
        }
        for ( right = 0; right < w4; ++right )
        {
            if ( ! column_all_dark( vid_buf, top, bottom, vid_info.width - 1 - right ) )
                break;
        }

        // only record the result if all the crops are less than a quarter of
        // the frame otherwise we can get fooled by frames with a lot of black
        // like titles, credits & fade-thru-black transitions.
        if ( top < h4 && bottom < h4 && left < w4 && right < w4 )
        {
            record_crop( crops, top, bottom, left, right );
        }
        ++npreviews;

skip_preview:
        /* Make sure we found audio rates and bitrates */
        for( j = 0; j < hb_list_count( title->list_audio ); j++ )
        {
            hb_audio_t * audio = hb_list_item( title->list_audio, j );
            if ( audio->priv.scan_cache )
            {
                hb_fifo_flush( audio->priv.scan_cache );
            }
        }
        if (vid_buf)
        {
            hb_buffer_close( &vid_buf );
        }
    }
    vid_decoder->close( vid_decoder );
    free( vid_decoder );

    if ( data->batch && data->stream )
    {
        hb_stream_close( &data->stream );
    }

    if ( npreviews )
    {
        // use the most common frame info for our final title dimensions
        hb_work_info_t vid_info;
        most_common_info( info_list, &vid_info );

        title->has_resolution_change = has_resolution_change( info_list );
        if ( title->video_codec_name == NULL )
        {
            title->video_codec_name = strdup( vid_info.name );
        }
        title->width = vid_info.width;
        title->height = vid_info.height;
        if ( vid_info.rate && vid_info.rate_base )
        {
            // if the frame rate is very close to one of our "common" framerates,
            // assume it actually is said frame rate; e.g. some 24000/1001 sources
            // may have a rate_base of 1126124 (instead of 1126125)
            for( i = 0; i < hb_video_rates_count; i++ )
            {
                if( is_close_to( vid_info.rate_base, hb_video_rates[i].rate, 100 ) )
                {
                    vid_info.rate_base = hb_video_rates[i].rate;
                    break;
                }
            }
            title->rate = vid_info.rate;
            title->rate_base = vid_info.rate_base;
            if( vid_info.rate_base == 900900 )
            {
                if( pulldown_count >= npreviews / 4 )
                {
                    title->rate_base = 1126125;
                    hb_deep_log( 2, "Pulldown detected, setting fps to 23.976" );
                }
                if( progressive_count >= npreviews / 2 )
                {
                    // We've already deduced that the frame rate is 23.976,
                    // so set it back again.
                    title->rate_base = 1126125;
                    hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976" );
                }
            }
            if( doubled_frame_count >= 3 * npreviews / 4 )
            {
                // We've detected that a significant number of the frames
                // have been doubled in duration by repeat flags.
                title->rate_base = 2 * vid_info.rate_base;
                hb_deep_log( 2, "Repeat frames detected, setting fps to %.3f", (float)title->rate / title->rate_base );
            }
        }
        title->video_bitrate = vid_info.bitrate;

        if( vid_info.pixel_aspect_width && vid_info.pixel_aspect_height )
        {
            title->pixel_aspect_width = vid_info.pixel_aspect_width;
            title->pixel_aspect_height = vid_info.pixel_aspect_height;
        }
        title->color_prim = vid_info.color_prim;
        title->color_transfer = vid_info.color_transfer;
        title->color_matrix = vid_info.color_matrix;

        // compute the aspect ratio based on the storage dimensions and the
        // pixel aspect ratio (if supplied) or just storage dimensions if no PAR.
        title->aspect = (double)title->width / (double)title->height;
        title->aspect *= (double)title->pixel_aspect_width /
                         (double)title->pixel_aspect_height;

        // For unknown reasons some French PAL DVDs put the original
        // content's aspect ratio into the mpeg PAR even though it's
        // the wrong PAR for the DVD. Apparently they rely on the fact
        // that DVD players ignore the content PAR and just use the
        // aspect ratio from the DVD metadata. So, if the aspect computed
        // from the PAR is different from the container's aspect we use
        // the container's aspect & recompute the PAR from it.
        if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) )
        {
            hb_log("scan: content PAR gives wrong aspect %.2f; "
                   "using container aspect %.2f", title->aspect,
                   title->container_aspect );
            title->aspect = title->container_aspect;
            hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height,
                       (int)(title->aspect * title->height + 0.5), title->width );
        }

        // don't try to crop unless we got at least 3 previews
        if ( crops->n > 2 )
        {
            sort_crops( crops );
            // The next line selects median cropping - at least
            // 50% of the frames will have their borders removed.
            // Other possible choices are loose cropping (i = 0) where 
            // no non-black pixels will be cropped from any frame and a
            // tight cropping (i = crops->n - (crops->n >> 2)) where at
            // least 75% of the frames will have their borders removed.
            i = crops->n >> 1;
            title->crop[0] = EVEN( crops->t[i] );
            title->crop[1] = EVEN( crops->b[i] );
            title->crop[2] = EVEN( crops->l[i] );
            title->crop[3] = EVEN( crops->r[i] );
        }

        hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, "
                "aspect %s, PAR %d:%d",
                npreviews, title->width, title->height, (float) title->rate /
                (float) title->rate_base,
                title->crop[0], title->crop[1], title->crop[2], title->crop[3],
                aspect_to_string( title->aspect ), title->pixel_aspect_width,
                title->pixel_aspect_height );

        if( interlaced_preview_count >= ( npreviews / 2 ) )
        {
            hb_log("Title is likely interlaced or telecined (%i out of %i previews). You should do something about that.",
                   interlaced_preview_count, npreviews);
            title->detected_interlacing = 1;
        }
        else
        {
            title->detected_interlacing = 0;
        }
    }
Exemple #25
0
/***********************************************************************
 * ReaderFunc
 ***********************************************************************
 *
 **********************************************************************/
static void ReaderFunc( void * _r )
{
    hb_reader_t  * r = _r;
    hb_fifo_t   ** fifos;
    hb_buffer_t  * buf;
    hb_list_t    * list;
    int            n;
    int            chapter = -1;
    int            chapter_end = r->job->chapter_end;

    if ( r->title->type == HB_BD_TYPE )
    {
        if ( !( r->bd = hb_bd_init( r->title->path ) ) )
            return;
    }
    else if ( r->title->type == HB_DVD_TYPE )
    {
        if ( !( r->dvd = hb_dvd_init( r->title->path ) ) )
            return;
    }
    else if ( r->title->type == HB_STREAM_TYPE ||
              r->title->type == HB_FF_STREAM_TYPE )
    {
        if ( !( r->stream = hb_stream_open( r->title->path, r->title ) ) )
            return;
    }
    else
    {
        // Unknown type, should never happen
        return;
    }

    if (r->bd)
    {
        if( !hb_bd_start( r->bd, r->title ) )
        {
            hb_bd_close( &r->bd );
            return;
        }
        if ( r->job->start_at_preview )
        {
            // XXX code from DecodePreviews - should go into its own routine
            hb_bd_seek( r->bd, (float)r->job->start_at_preview /
                         ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
        }
        else if ( r->job->pts_to_start )
        {
            // Note, bd seeks always put us to an i-frame.  no need
            // to start decoding early using r->pts_to_start
            hb_bd_seek_pts( r->bd, r->job->pts_to_start );
            r->job->pts_to_start = 0;
            r->start_found = 1;
        }
        else
        {
            hb_bd_seek_chapter( r->bd, r->job->chapter_start );
        }
        if (r->job->angle > 1)
        {
            hb_bd_set_angle( r->bd, r->job->angle - 1 );
        }
    }
    else if (r->dvd)
    {
        /*
         * XXX this code is a temporary hack that should go away if/when
         *     chapter merging goes away in libhb/dvd.c
         * map the start and end chapter numbers to on-media chapter
         * numbers since chapter merging could cause the handbrake numbers
         * to diverge from the media numbers and, if our chapter_end is after
         * a media chapter that got merged, we'll stop ripping too early.
         */
        int start = r->job->chapter_start;
        hb_chapter_t *chap = hb_list_item( r->title->list_chapter, chapter_end - 1 );

        chapter_end = chap->index;
        if (start > 1)
        {
           chap = hb_list_item( r->title->list_chapter, start - 1 );
           start = chap->index;
        }
        /* end chapter mapping XXX */

        if( !hb_dvd_start( r->dvd, r->title, start ) )
        {
            hb_dvd_close( &r->dvd );
            return;
        }
        if (r->job->angle)
        {
            hb_dvd_set_angle( r->dvd, r->job->angle );
        }

        if ( r->job->start_at_preview )
        {
            // XXX code from DecodePreviews - should go into its own routine
            hb_dvd_seek( r->dvd, (float)r->job->start_at_preview /
                         ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
        }
    }
    else if ( r->stream && r->job->start_at_preview )
    {
        
        // XXX code from DecodePreviews - should go into its own routine
        hb_stream_seek( r->stream, (float)( r->job->start_at_preview - 1 ) /
                        ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );

    } 
    else if ( r->stream && r->job->pts_to_start )
    {
        int64_t pts_to_start = r->job->pts_to_start;
        
        // Find out what the first timestamp of the stream is
        // and then seek to the appropriate offset from it
        if ( ( buf = hb_stream_read( r->stream ) ) )
        {
            if ( buf->start > 0 )
            {
                pts_to_start += buf->start;
                r->pts_to_start += buf->start;
                r->job->pts_to_start += buf->start;
            }
        }
        
        if ( hb_stream_seek_ts( r->stream, pts_to_start ) >= 0 )
        {
            // Seek takes us to the nearest I-frame before the timestamp
            // that we want.  So we will retrieve the start time of the
            // first packet we get, subtract that from pts_to_start, and
            // inspect the reset of the frames in sync.
            r->start_found = 2;
            r->job->pts_to_start = pts_to_start;
        }
    } 
    else if( r->stream )
    {
        /*
         * Standard stream, seek to the starting chapter, if set, and track the
         * end chapter so that we end at the right time.
         */
        int start = r->job->chapter_start;
        hb_chapter_t *chap = hb_list_item( r->title->list_chapter, chapter_end - 1 );
        
        chapter_end = chap->index;
        if (start > 1)
        {
            chap = hb_list_item( r->title->list_chapter, start - 1 );
            start = chap->index;
        }
        
        /*
         * Seek to the start chapter.
         */
        hb_stream_seek_chapter( r->stream, start );
    }

    list  = hb_list_init();

    while( !*r->die && !r->job->done )
    {
        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" );
            break;
        }
        if( chapter > chapter_end )
        {
            hb_log( "reader: end of chapter %d (media %d) reached at media chapter %d",
                    r->job->chapter_end, chapter_end, chapter );
            break;
        }

        if (r->bd)
        {
          if( (buf = hb_bd_read( r->bd )) == NULL )
          {
              break;
          }
        }
        else if (r->dvd)
        {
          if( (buf = hb_dvd_read( r->dvd )) == NULL )
          {
              break;
          }
        }
        else if (r->stream)
        {
          if ( (buf = hb_stream_read( r->stream )) == NULL )
          {
            break;
          }
          if ( 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->start > 0 && buf->start < r->job->pts_to_start )
            {
                r->job->pts_to_start -= buf->start;
            }
            else if ( buf->start >= r->job->pts_to_start )
            {
                r->job->pts_to_start = 0;
                r->start_found = 1;
            }
          }
        }

        if( r->job->indepth_scan )
        {
            /*
             * Need to update the progress during a subtitle scan
             */
            hb_state_t state;

#define p state.param.working

            state.state = HB_STATE_WORKING;
            p.progress = (double)chapter / (double)r->job->chapter_end;
            if( p.progress > 1.0 )
            {
                p.progress = 1.0;
            }
            p.rate_avg = 0.0;
            p.hours    = -1;
            p.minutes  = -1;
            p.seconds  = -1;
            hb_set_state( r->job->h, &state );
        }

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

        while( ( buf = hb_list_item( list, 0 ) ) )
        {
            hb_list_rem( list, buf );
            fifos = GetFifoForId( r->job, buf->id );

            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->start != -1 && buf->renderOffset != -1 &&
                     ( buf->id == r->title->video_id || is_audio( r, buf->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->id, buf->renderOffset );
                }
                else
                {
                    fifos = NULL;
                }
            }
            if( fifos )
            {
                if ( buf->renderOffset != -1 )
                {
                    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 );
                        }
                        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.
                            buf->start = -1;
                            buf->renderOffset = -1;
                        }
                    }
                }
                if ( buf->start != -1 )
                {
                    int64_t start = buf->start - r->scr_offset;
                    if ( !r->start_found )
                        UpdateState( r, start );

                    if ( !r->start_found &&
                        start >= r->pts_to_start )
                    {
                        // pts_to_start point found
                        r->start_found = 1;
                    }
                    // This log is handy when you need to debug timing problems
                    //hb_log("id %x scr_offset %ld start %ld --> %ld", 
                    //        buf->id, r->scr_offset, buf->start, 
                    //        buf->start - r->scr_offset);
                    buf->start -= r->scr_offset;
                }
                if ( buf->renderOffset != -1 )
                {
                    if ( r->scr_changes == r->demux.scr_changes )
                    {
                        // 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->renderOffset -= r->scr_offset;
                        update_ipt( r, buf );
                    }
                }
                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( n = 1; fifos[n] != NULL; n++)
                {
                    hb_buffer_t *buf_copy = hb_buffer_init( buf->size );
                    hb_buffer_copy_settings( buf_copy, buf );
                    memcpy( buf_copy->data, buf->data, buf->size );
                    push_buf( r, fifos[n], buf_copy );
                }
                push_buf( r, fifos[0], buf );
            }
            else
            {
                hb_buffer_close( &buf );
            }
        }
    }

    // send empty buffers downstream to video & audio decoders to signal we're done.
    if( !*r->die && !r->job->done )
    {
        push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) );

        hb_audio_t *audio;
        for( n = 0; (audio = hb_list_item( r->job->title->list_audio, n)); ++n )
        {
            if ( audio->priv.fifo_in )
                push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) );
        }

        hb_subtitle_t *subtitle;
        for( n = 0; (subtitle = hb_list_item( r->job->title->list_subtitle, n)); ++n )
        {
            if ( subtitle->fifo_in && subtitle->source == VOBSUB)
                push_buf( r, subtitle->fifo_in, hb_buffer_init(0) );
        }
    }

    hb_list_empty( &list );
    if (r->bd)
    {
        hb_bd_stop( r->bd );
        hb_bd_close( &r->bd );
    }
    else if (r->dvd)
    {
        hb_dvd_stop( r->dvd );
        hb_dvd_close( &r->dvd );
    }
    else if (r->stream)
    {
        hb_stream_close(&r->stream);
    }

    if ( r->stream_timing )
    {
        free( r->stream_timing );
    }

    hb_log( "reader: done. %d scr changes", r->demux.scr_changes );
    if ( r->demux.dts_drops )
    {
        hb_log( "reader: %d drops because DTS out of range", r->demux.dts_drops );
    }

    free( r );
    _r = NULL;
}
void hb_srt_to_ssa(hb_buffer_t *sub_in, int line)
{
    if (sub_in->size == 0)
        return;

    // null terminate input if not already terminated
    if (sub_in->data[sub_in->size-1] != 0)
    {
        hb_buffer_realloc(sub_in, ++sub_in->size);
        sub_in->data[sub_in->size - 1] = 0;
    }
    char * srt = (char*)sub_in->data;
    // SSA markup expands a little over SRT, so allocate a bit of extra
    // space.  More will be realloc'd if needed.
    hb_buffer_t * sub = hb_buffer_init(sub_in->size + 80);
    char * ssa, *ssa_markup;
    int skip, len, pos, ii;

    // Exchange data between input sub and new ssa_sub
    // After this, sub_in contains ssa data
    hb_buffer_swap_copy(sub_in, sub);
    ssa = (char*)sub_in->data;

    sprintf((char*)sub_in->data, "%d,,Default,,0,0,0,,", line);
    pos = strlen((char*)sub_in->data);

    ii = 0;
    while (srt[ii] != '\0')
    {
        if ((ssa_markup = srt_markup_to_ssa(srt + ii, &skip)) != NULL)
        {
            len = strlen(ssa_markup);
            hb_buffer_realloc(sub_in, pos + len + 1);
            // After realloc, sub_in->data may change
            ssa = (char*)sub_in->data;
            sprintf(ssa + pos, "%s", ssa_markup);
            free(ssa_markup);
            pos += len;
            ii += skip;
        }
        else
        {
            hb_buffer_realloc(sub_in, pos + 4);
            // After realloc, sub_in->data may change
            ssa = (char*)sub_in->data;
            if (srt[ii] == '\r')
            {
                ssa[pos++] = '\\';
                ssa[pos++] = 'N';
                ii++;
                if (srt[ii] == '\n')
                {
                    ii++;
                }
            }
            else if (srt[ii] == '\n')
            {
                ssa[pos++] = '\\';
                ssa[pos++] = 'N';
                ii++;
            }
            else
            {
                ssa[pos++] = srt[ii++];
            }
        }
    }
    ssa[pos] = '\0';
    sub_in->size = pos + 1;
    hb_buffer_close(&sub);
}
Exemple #27
0
/*
 * Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets.
 * 
 * SSA packet format:
 * ( Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text CR LF ) +
 *             1      2     3   4     5    6       7       8       9      10
 */
static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in )
{
    // Store NULL after the end of the buffer to make using string processing safe
    hb_buffer_realloc(in, ++in->size);
    in->data[in->size - 1] = '\0';
    
    hb_buffer_t *out_list = NULL;
    hb_buffer_t **nextPtr = &out_list;

    const char *EOL = "\r\n";
    char *curLine, *curLine_parserData;
    for ( curLine = strtok_r( (char *) in->data, EOL, &curLine_parserData );
          curLine;
          curLine = strtok_r( NULL, EOL, &curLine_parserData ) )
    {
        // Skip empty lines and spaces between adjacent CR and LF
        if (curLine[0] == '\0')
            continue;
        
        // Decode an individual SSA line
        hb_buffer_t *out;
        if ( w->subtitle->config.dest == PASSTHRUSUB ) {
            out = ssa_decode_line_to_utf8( (uint8_t *) curLine, strlen( curLine ), in->sequence );
            if ( out == NULL )
                continue;
            
            // We shouldn't be storing the extra NULL character,
            // but the MP4 muxer expects this, unfortunately.
            if (out->size > 0 && out->data[out->size - 1] != '\0')
            {
                hb_buffer_realloc(out, ++out->size);
                out->data[out->size - 1] = '\0';
            }
            
            // If the input packet was non-empty, do not pass through
            // an empty output packet (even if the subtitle was empty),
            // as this would be interpreted as an end-of-stream
            if ( in->size > 0 && out->size == 0 ) {
                hb_buffer_close(&out);
                continue;
            }
        } else if ( w->subtitle->config.dest == RENDERSUB ) {
            out = ssa_decode_line_to_mkv_ssa( w, (uint8_t *) curLine, strlen( curLine ), in->sequence );
            if ( out == NULL )
                continue;
        }
        
        // Append 'out' to 'out_list'
        *nextPtr = out;
        nextPtr = &out->next;
    }

    // For point-to-point encoding, when the start time of the stream 
    // may be offset, the timestamps of the subtitles must be offset as well.
    //
    // HACK: Here we are making the assumption that, under normal circumstances,
    //       the output display time of the first output packet is equal to the
    //       display time of the input packet.
    //      
    //       During point-to-point encoding, the display time of the input 
    //       packet will be offset to compensate.
    //      
    //       Therefore we offset all of the output packets by a slip amount 
    //       such that first output packet's display time aligns with the 
    //       input packet's display time. This should give the correct time 
    //       when point-to-point encoding is in effect.
    if (out_list && out_list->s.start > in->s.start)
    {
        int64_t slip = out_list->s.start - in->s.start;
        hb_buffer_t *out;

        out = out_list;
        while (out)
        {
            out->s.start -= slip;
            out->s.stop -= slip;
            out = out->next;
        }
    }
    
    return out_list;
}
Exemple #28
0
/***********************************************************************
 * DecodePreviews
 ***********************************************************************
 * Decode 10 pictures for the given title.
 * It assumes that data->reader and data->vts have successfully been
 * DVDOpen()ed and ifoOpen()ed.
 **********************************************************************/
static int DecodePreviews( hb_scan_t * data, hb_title_t * title, int flush )
{
    int                i, npreviews = 0, abort = 0;
    hb_buffer_t      * buf, * buf_es;
    hb_buffer_list_t   list_es;
    int                progressive_count = 0;
    int                pulldown_count = 0;
    int                doubled_frame_count = 0;
    int                interlaced_preview_count = 0;
    int                vid_samples = 0;
    int                frame_wait = 0;
    int                cc_wait = 10;
    int                frames;
    hb_stream_t      * stream = NULL;
    info_list_t      * info_list;
    int                abort_audio = 0;

    info_list = calloc(data->preview_count+1, sizeof(*info_list));
    crop_record_t *crops = crop_record_init( data->preview_count );

    hb_buffer_list_clear(&list_es);

    if( data->batch )
    {
        hb_log( "scan: decoding previews for title %d (%s)", title->index, title->path );
    }
    else
    {
        hb_log( "scan: decoding previews for title %d", title->index );
    }

    if (data->bd)
    {
        hb_bd_start( data->bd, title );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->dvd)
    {
        hb_dvd_start( data->dvd, title, 1 );
        title->angle_count = hb_dvd_angle_count( data->dvd );
        hb_log( "scan: title angle(s) %d", title->angle_count );
    }
    else if (data->batch)
    {
        stream = hb_stream_open(data->h, title->path, title, 0);
    }
    else if (data->stream)
    {
        stream = hb_stream_open(data->h, data->path, title, 0);
    }

    if (title->video_codec == WORK_NONE)
    {
        hb_error("No video decoder set!");
        free(info_list);
        crop_record_free(crops);
        hb_stream_close(&stream);
        return 0;
    }
    hb_work_object_t *vid_decoder = hb_get_work(data->h, title->video_codec);
    vid_decoder->codec_param = title->video_codec_param;
    vid_decoder->title = title;

    if (vid_decoder->init(vid_decoder, NULL))
    {
        hb_error("Decoder init failed!");
        free(info_list);
        crop_record_free(crops);
        free( vid_decoder );
        hb_stream_close(&stream);
        return 0;
    }

    for( i = 0; i < data->preview_count; i++ )
    {
        int j;

        UpdateState3(data, i + 1);

        if ( *data->die )
        {
            free( info_list );
            crop_record_free( crops );
            vid_decoder->close( vid_decoder );
            free( vid_decoder );
            hb_stream_close(&stream);
            return 0;
        }
        if (data->bd)
        {
            if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        if (data->dvd)
        {
            if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
          {
              continue;
          }
        }
        else if (stream)
        {
            /* we start reading streams at zero rather than 1/11 because
             * short streams may have only one sequence header in the entire
             * file and we need it to decode any previews.
             *
             * Also, seeking to position 0 loses the palette of avi files
             * so skip initial seek */
            if (i != 0)
            {
                if (!hb_stream_seek(stream,
                                    (float)i / (data->preview_count + 1.0)))
                {
                    continue;
                }
            }
            else
            {
                hb_stream_set_need_keyframe(stream, 1);
            }
        }

        hb_deep_log( 2, "scan: preview %d", i + 1 );

        if (flush && vid_decoder->flush)
            vid_decoder->flush( vid_decoder );
        if (title->flags & HBTF_NO_IDR)
        {
            if (!flush)
            {
                // If we are doing the first previews decode attempt,
                // set this threshold high so that we get the best
                // quality frames possible.
                frame_wait = 100;
            }
            else
            {
                // If we failed to get enough valid frames in the first
                // previews decode attempt, lower the threshold to improve
                // our chances of getting something to work with.
                frame_wait = 10;
            }
        }
        else
        {
            // For certain mpeg-2 streams, libav is delivering a
            // dummy first frame that is all black.  So always skip
            // one frame
            frame_wait = 1;
        }
        frames = 0;

        hb_buffer_t * vid_buf = NULL, * last_vid_buf = NULL;

        int packets = 0;
        vid_decoder->frame_count = 0;
        while (vid_decoder->frame_count < PREVIEW_READ_THRESH ||
              (!AllAudioOK(title) && packets < 10000))
        {
            if ((buf = read_buf(data, stream)) == NULL)
            {
                // If we reach EOF and no audio, don't continue looking for
                // audio
                abort_audio = 1;
                if (vid_buf != NULL || last_vid_buf != NULL)
                {
                    break;
                }
                hb_log("Warning: Could not read data for preview %d, skipped",
                       i + 1 );

                // If we reach EOF and no video, don't continue looking for
                // video
                abort = 1;
                goto skip_preview;
            }

            packets++;
            if (buf->size <= 0)
            {
                // Ignore "null" frames
                hb_buffer_close(&buf);
                continue;
            }

            (hb_demux[title->demuxer])(buf, &list_es, 0 );

            while ((buf_es = hb_buffer_list_rem_head(&list_es)) != NULL)
            {
                if( buf_es->s.id == title->video_id && vid_buf == NULL )
                {
                    vid_decoder->work( vid_decoder, &buf_es, &vid_buf );
                    // There are 2 conditions we decode additional
                    // video frames for during scan.
                    // 1. We did not detect IDR frames, so the initial video
                    //    frames may be corrupt.  We docode extra frames to
                    //    increase the probability of a complete preview frame
                    // 2. Some frames do not contain CC data, even though
                    //    CCs are present in the stream.  So we need to decode
                    //    additional frames to find the CCs.
                    if (vid_buf != NULL && (frame_wait || cc_wait))
                    {
                        hb_work_info_t vid_info;
                        if (vid_decoder->info(vid_decoder, &vid_info))
                        {
                            if (is_close_to(vid_info.rate.den, 900900, 100) &&
                                (vid_buf->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD))
                            {
                                /* Potentially soft telecine material */
                                pulldown_count++;
                            }

                            if (vid_buf->s.flags & PIC_FLAG_REPEAT_FRAME)
                            {
                                // AVCHD-Lite specifies that all streams are
                                // 50 or 60 fps.  To produce 25 or 30 fps, camera
                                // makers are repeating all frames.
                                doubled_frame_count++;
                            }

                            if (is_close_to(vid_info.rate.den, 1126125, 100 ))
                            {
                                // Frame FPS is 23.976 (meaning it's
                                // progressive), so start keeping track of
                                // how many are reporting at that speed. When
                                // enough show up that way, we want to make
                                // that the overall title FPS.
                                progressive_count++;
                            }
                            vid_samples++;
                        }

                        if (frames > 0 && vid_buf->s.frametype == HB_FRAME_I)
                            frame_wait = 0;
                        if (frame_wait || cc_wait)
                        {
                            hb_buffer_close(&last_vid_buf);
                            last_vid_buf = vid_buf;
                            vid_buf = NULL;
                            if (frame_wait) frame_wait--;
                            if (cc_wait) cc_wait--;
                        }
                        frames++;
                    }
                }
                else if (!AllAudioOK(title) && !abort_audio)
                {
                    LookForAudio( data, title, buf_es );
                    buf_es = NULL;
                }
                if ( buf_es )
                    hb_buffer_close( &buf_es );
            }

            if (vid_buf && (abort_audio || AllAudioOK(title)))
                break;
        }
        hb_buffer_list_close(&list_es);

        if (vid_buf == NULL)
        {
            vid_buf = last_vid_buf;
            last_vid_buf = NULL;
        }
        hb_buffer_close(&last_vid_buf);

        if (vid_buf == NULL)
        {
            hb_log( "scan: could not get a decoded picture" );
            continue;
        }

        /* Get size and rate infos */

        hb_work_info_t vid_info;
        if( !vid_decoder->info( vid_decoder, &vid_info ) )
        {
            /*
             * Could not fill vid_info, don't continue and try to use vid_info
             * in this case.
             */
            hb_log( "scan: could not get a video information" );
            hb_buffer_close( &vid_buf );
            continue;
        }

        if (vid_info.geometry.width  != vid_buf->f.width ||
            vid_info.geometry.height != vid_buf->f.height)
        {
            hb_log( "scan: video geometry information does not match buffer" );
            hb_buffer_close( &vid_buf );
            continue;
        }
        remember_info( info_list, &vid_info );

        /* Check preview for interlacing artifacts */
        if( hb_detect_comb( vid_buf, 10, 30, 9, 10, 30, 9 ) )
        {
            hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1);
            interlaced_preview_count++;
        }

        if( data->store_previews )
        {
            hb_save_preview( data->h, title->index, i, vid_buf );
        }

        /* Detect black borders */

        int top, bottom, left, right;
        int h4 = vid_info.geometry.height / 4, w4 = vid_info.geometry.width / 4;

        // When widescreen content is matted to 16:9 or 4:3 there's sometimes
        // a thin border on the outer edge of the matte. On TV content it can be
        // "line 21" VBI data that's normally hidden in the overscan. For HD
        // content it can just be a diagnostic added in post production so that
        // the frame borders are visible. We try to ignore these borders so
        // we can crop the matte. The border width depends on the resolution
        // (12 pixels on 1080i looks visually the same as 4 pixels on 480i)
        // so we allow the border to be up to 1% of the frame height.
        const int border = vid_info.geometry.height / 100;

        for ( top = border; top < h4; ++top )
        {
            if ( ! row_all_dark( vid_buf, top ) )
                break;
        }
        if ( top <= border )
        {
            // we never made it past the border region - see if the rows we
            // didn't check are dark or if we shouldn't crop at all.
            for ( top = 0; top < border; ++top )
            {
                if ( ! row_all_dark( vid_buf, top ) )
                    break;
            }
            if ( top >= border )
            {
                top = 0;
            }
        }
        for ( bottom = border; bottom < h4; ++bottom )
        {
            if ( ! row_all_dark( vid_buf, vid_info.geometry.height - 1 - bottom ) )
                break;
        }
        if ( bottom <= border )
        {
            for ( bottom = 0; bottom < border; ++bottom )
            {
                if ( ! row_all_dark( vid_buf, vid_info.geometry.height - 1 - bottom ) )
                    break;
            }
            if ( bottom >= border )
            {
                bottom = 0;
            }
        }
        for ( left = 0; left < w4; ++left )
        {
            if ( ! column_all_dark( vid_buf, top, bottom, left ) )
                break;
        }
        for ( right = 0; right < w4; ++right )
        {
            if ( ! column_all_dark( vid_buf, top, bottom, vid_info.geometry.width - 1 - right ) )
                break;
        }

        // only record the result if all the crops are less than a quarter of
        // the frame otherwise we can get fooled by frames with a lot of black
        // like titles, credits & fade-thru-black transitions.
        if ( top < h4 && bottom < h4 && left < w4 && right < w4 )
        {
            record_crop( crops, top, bottom, left, right );
        }
        ++npreviews;

skip_preview:
        /* Make sure we found audio rates and bitrates */
        for( j = 0; j < hb_list_count( title->list_audio ); j++ )
        {
            hb_audio_t * audio = hb_list_item( title->list_audio, j );
            if ( audio->priv.scan_cache )
            {
                hb_fifo_flush( audio->priv.scan_cache );
            }
        }
        if (vid_buf)
        {
            hb_buffer_close( &vid_buf );
        }
        if (abort)
        {
            break;
        }
    }
    UpdateState3(data, i);

    vid_decoder->close( vid_decoder );
    free( vid_decoder );

    if (stream != NULL)
    {
        hb_stream_close(&stream);
    }

    if ( npreviews )
    {
        // use the most common frame info for our final title dimensions
        hb_work_info_t vid_info;
        most_common_info( info_list, &vid_info );

        title->has_resolution_change = has_resolution_change( info_list );
        if ( title->video_codec_name == NULL )
        {
            title->video_codec_name = strdup( vid_info.name );
        }
        title->geometry.width = vid_info.geometry.width;
        title->geometry.height = vid_info.geometry.height;
        if (vid_info.rate.num && vid_info.rate.den)
        {
            // if the frame rate is very close to one of our "common"
            // framerates, assume it actually is said frame rate;
            // e.g. some 24000/1001 sources may have a rate.den of 1126124
            // instead of 1126125
            const hb_rate_t *video_framerate = NULL;
            while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL)
            {
                if (is_close_to(vid_info.rate.den, video_framerate->rate, 100))
                {
                    vid_info.rate.den = video_framerate->rate;
                    break;
                }
            }
            title->vrate = vid_info.rate;
            if( vid_info.rate.den == 900900 )
            {
                if (vid_samples >= 4 && pulldown_count >= vid_samples / 4)
                {
                    title->vrate.den = 1126125;
                    hb_deep_log( 2, "Pulldown detected, setting fps to 23.976" );
                }
                if (vid_samples >= 2 && progressive_count >= vid_samples / 2)
                {
                    // We've already deduced that the frame rate is 23.976,
                    // so set it back again.
                    title->vrate.den = 1126125;
                    hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976" );
                }
            }
            if (vid_samples >= 2 && doubled_frame_count >= 3 * vid_samples / 4)
            {
                // We've detected that a significant number of the frames
                // have been doubled in duration by repeat flags.
                title->vrate.den = 2 * vid_info.rate.den;
                hb_deep_log(2, "Repeat frames detected, setting fps to %.3f",
                            (float)title->vrate.num / title->vrate.den );
            }
        }
        title->video_bitrate = vid_info.bitrate;

        if( vid_info.geometry.par.num && vid_info.geometry.par.den )
        {
            title->geometry.par = vid_info.geometry.par;
        }
        title->color_prim = vid_info.color_prim;
        title->color_transfer = vid_info.color_transfer;
        title->color_matrix = vid_info.color_matrix;

        title->video_decode_support = vid_info.video_decode_support;

        // compute the aspect ratio based on the storage dimensions and PAR.
        hb_reduce(&title->dar.num, &title->dar.den,
                  title->geometry.par.num * title->geometry.width,
                  title->geometry.height * title->geometry.par.den);

        // For unknown reasons some French PAL DVDs put the original
        // content's aspect ratio into the mpeg PAR even though it's
        // the wrong PAR for the DVD. Apparently they rely on the fact
        // that DVD players ignore the content PAR and just use the
        // aspect ratio from the DVD metadata. So, if the aspect computed
        // from the PAR is different from the container's aspect we use
        // the container's aspect & recompute the PAR from it.
        if (data->dvd &&
            (title->dar.num != title->container_dar.num ||
             title->dar.den != title->container_dar.den))
        {
            hb_log("scan: content PAR gives wrong aspect %d:%d; "
                   "using container aspect %d:%d",
                   title->dar.num, title->dar.den,
                   title->container_dar.num, title->container_dar.den);
            title->dar = title->container_dar;
            hb_reduce(&title->geometry.par.num, &title->geometry.par.den,
                      title->geometry.height * title->dar.num,
                      title->geometry.width * title->dar.den);
        }

        // don't try to crop unless we got at least 3 previews
        if ( crops->n > 2 )
        {
            sort_crops( crops );
            // The next line selects median cropping - at least
            // 50% of the frames will have their borders removed.
            // Other possible choices are loose cropping (i = 0) where
            // no non-black pixels will be cropped from any frame and a
            // tight cropping (i = crops->n - (crops->n >> 2)) where at
            // least 75% of the frames will have their borders removed.
            i = crops->n >> 1;
            title->crop[0] = EVEN( crops->t[i] );
            title->crop[1] = EVEN( crops->b[i] );
            title->crop[2] = EVEN( crops->l[i] );
            title->crop[3] = EVEN( crops->r[i] );
        }

        hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, "
                "aspect %s, PAR %d:%d",
                npreviews, title->geometry.width, title->geometry.height,
                (float)title->vrate.num / title->vrate.den,
                title->crop[0], title->crop[1], title->crop[2], title->crop[3],
                aspect_to_string(&title->dar),
                title->geometry.par.num, title->geometry.par.den);

        if (title->video_decode_support != HB_DECODE_SUPPORT_SW)
        {
            hb_log("scan: supported video decoders:%s%s",
                   !(title->video_decode_support & HB_DECODE_SUPPORT_SW)    ? "" : " avcodec",
                   !(title->video_decode_support & HB_DECODE_SUPPORT_QSV)   ? "" : " qsv");
        }

        if( interlaced_preview_count >= ( npreviews / 2 ) )
        {
            hb_log("Title is likely interlaced or telecined (%i out of %i previews). You should do something about that.",
                   interlaced_preview_count, npreviews);
            title->detected_interlacing = 1;
        }
        else
        {
            title->detected_interlacing = 0;
        }
    }
Exemple #29
0
hb_buffer_t* hb_audio_resample(hb_audio_resample_t *resample,
                               uint8_t **samples, int nsamples)
{
    if (resample == NULL)
    {
        hb_error("hb_audio_resample: resample is NULL");
        return NULL;
    }
    if (resample->resample_needed && resample->avresample == NULL)
    {
        hb_error("hb_audio_resample: resample needed but libavresample context "
                 "is NULL");
        return NULL;
    }

    hb_buffer_t *out;
    int out_size, out_samples;

    if (resample->resample_needed)
    {
        int in_linesize, out_linesize;
        // set in/out linesize and out_size
        av_samples_get_buffer_size(&in_linesize,
                                   resample->resample.channels, nsamples,
                                   resample->resample.sample_fmt, 0);
        out_size = av_samples_get_buffer_size(&out_linesize,
                                              resample->out.channels, nsamples,
                                              resample->out.sample_fmt, 0);
        out = hb_buffer_init(out_size);

        out_samples = avresample_convert(resample->avresample,
                                         &out->data, out_linesize, nsamples,
                                         samples,     in_linesize, nsamples);

        if (out_samples <= 0)
        {
            if (out_samples < 0)
                hb_log("hb_audio_resample: avresample_convert() failed");
            // don't send empty buffers downstream (EOF)
            hb_buffer_close(&out);
            return NULL;
        }
        out->size = (out_samples *
                     resample->out.sample_size * resample->out.channels);
    }
    else
    {
        out_samples = nsamples;
        out_size = (out_samples *
                    resample->out.sample_size * resample->out.channels);
        out = hb_buffer_init(out_size);
        memcpy(out->data, samples[0], out_size);
    }

    /*
     * Dual Mono to Mono.
     *
     * Copy all left or right samples to the first half of the buffer and halve
     * the buffer size.
     */
    if (resample->dual_mono_downmix)
    {
        int ii, jj = !!resample->dual_mono_right_only;
        int sample_size = resample->out.sample_size;
        uint8_t *audio_samples = out->data;
        for (ii = 0; ii < out_samples; ii++)
        {
            memcpy(audio_samples + (ii * sample_size),
                   audio_samples + (jj * sample_size), sample_size);
            jj += 2;
        }
        out->size = out_samples * sample_size;
    }

    return out;
}
Exemple #30
0
void muxClose( hb_work_object_t * w )
{
    hb_work_private_t * pv = w->private_data;
    hb_mux_t    * mux = pv->mux;
    hb_job_t    * job = pv->job;
    hb_track_t  * track;
    int           i;

    hb_lock( mux->mutex );
    if ( --mux->ref == 0 )
    {
        // Update state before closing muxer.  Closing the muxer
        // may initiate optimization which can take a while and
        // we want the muxing state to be visible while this is
        // happening.
        if( job->pass == 0 || job->pass == 2 )
        {
            /* Update the UI */
            hb_state_t state;
            state.state = HB_STATE_MUXING;
            state.param.muxing.progress = 0;
            hb_set_state( job->h, &state );
        }

        if( mux->m )
        {
            mux->m->end( mux->m );
            free( mux->m );
        }

        // we're all done muxing -- print final stats and cleanup.
        if( job->pass == 0 || job->pass == 2 )
        {
            struct stat sb;
            uint64_t bytes_total, frames_total;

            if( !stat( job->file, &sb ) )
            {
                hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size );

                bytes_total  = 0;
                frames_total = 0;
                for( i = 0; i < mux->ntracks; ++i )
                {
                    track = mux->track[i];
                    hb_log( "mux: track %d, %"PRId64" frames, %"PRId64" bytes, %.2f kbps, fifo %d",
                            i, track->frames, track->bytes,
                            90000.0 * track->bytes / mux->pts / 125,
                            track->mf.flen );
                    if( !i && ( job->vquality < 0.0 || job->vquality > 1.0 ) )
                    {
                        /* Video */
                        hb_deep_log( 2, "mux: video bitrate error, %+"PRId64" bytes",
                                (int64_t)(track->bytes - mux->pts * job->vbitrate * 125 / 90000) );
                    }
                    bytes_total  += track->bytes;
                    frames_total += track->frames;
                }

                if( bytes_total && frames_total )
                {
                    hb_deep_log( 2, "mux: overhead, %.2f bytes per frame",
                            (float) ( sb.st_size - bytes_total ) /
                            frames_total );
                }
            }
        }
    
        for( i = 0; i < mux->ntracks; ++i )
        {
            hb_buffer_t * b;
            track = mux->track[i];
            while ( (b = mf_pull( track )) != NULL )
            {
                hb_buffer_close( &b );
            }
            if( track->mux_data )
            {
                free( track->mux_data );
                free( track->mf.fifo );
            }
            free( track );
        }
        hb_unlock( mux->mutex );
        hb_lock_close( &mux->mutex );
        free( mux );
    }
    else
    {
        hb_unlock( mux->mutex );
    }
    free( pv );
    w->private_data = NULL;
}