Ejemplo n.º 1
0
/**
 * @brief Thread reading frames from the subprocess
 */
static void* outputThread(void* userData)
{
    filter_t*    intf = (filter_t*)userData;
    filter_sys_t* sys = intf->p_sys;

    msg_Info(intf, "outputThread: enter");

    bool gotHeader = false;

    while (true)
    {
        if (!gotHeader)
        {
            video_format_Init(&sys->outFormat, VLC_CODEC_I420);
            if (1 != readY4mHeader(intf, &sys->outFormat, sys->stdout))
                break;
            gotHeader = true;
        }

        picture_t* outPic = picture_NewFromFormat(&sys->outFormat);
        if(1 != readY4mFrame(intf, outPic, sys->stdout))
        {
            picture_Release(outPic);
            break;
        }

        //msg_Info(intf, "outputThread: read picture");

        // fixme: deinterlace filter does this, not sure if we need to;
        // y4m header contains this information
        outPic->b_progressive = true;
        outPic->i_nb_fields = 2;

        picture_fifo_Push(sys->outputFifo, outPic);

        vlc_cond_signal(&sys->outputCond);
    }

    msg_Info(intf, "outputThread: exit");

    sys->threadExit = true;
    vlc_cond_signal(&sys->outputCond);

    return userData;
}
Ejemplo n.º 2
0
Archivo: image.c Proyecto: tguillem/vlc
static int ImageQueueVideo( decoder_t *p_dec, picture_t *p_pic )
{
    image_handler_t *p_image = p_dec->p_queue_ctx;
    picture_fifo_Push( p_image->outfifo, p_pic );
    return 0;
}
Ejemplo n.º 3
0
Archivo: video.c Proyecto: AsamQi/vlc
static void OutputFrame( sout_stream_sys_t *p_sys, picture_t *p_pic, bool b_need_duplicate, sout_stream_t *p_stream, sout_stream_id_t *id, block_t **out )
{
    picture_t *p_pic2 = NULL;

    /*
     * Encoding
     */

    /* Check if we have a subpicture to overlay */
    if( p_sys->p_spu )
    {
        video_format_t fmt = id->p_encoder->fmt_in.video;
        if( fmt.i_visible_width <= 0 || fmt.i_visible_height <= 0 )
        {
            fmt.i_visible_width  = fmt.i_width;
            fmt.i_visible_height = fmt.i_height;
            fmt.i_x_offset       = 0;
            fmt.i_y_offset       = 0;
        }

        subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt,
                                             p_pic->date, p_pic->date, false );

        /* Overlay subpicture */
        if( p_subpic )
        {
            if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) )
            {
                /* We can't modify the picture, we need to duplicate it,
                 * in this point the picture is already p_encoder->fmt.in format*/
                picture_t *p_tmp = video_new_buffer_encoder( id->p_encoder );
                if( likely( p_tmp ) )
                {
                    picture_Copy( p_tmp, p_pic );
                    picture_Release( p_pic );
                    p_pic = p_tmp;
                }
            }
            if( unlikely( !p_sys->p_spu_blend ) )
                p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt );
            if( likely( p_sys->p_spu_blend ) )
                picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic );
            subpicture_Delete( p_subpic );
        }
    }

    if( p_sys->i_threads == 0 )
    {
        block_t *p_block;

        p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
        block_ChainAppend( out, p_block );
    }

    if( p_sys->b_master_sync )
    {
        mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
        mtime_t i_video_drift = p_pic->date - i_pts;
        if (unlikely ( i_video_drift  > MASTER_SYNC_MAX_DRIFT
              || i_video_drift < -MASTER_SYNC_MAX_DRIFT ) )
        {
            msg_Dbg( p_stream,
                "drift is too high (%"PRId64"), resetting master sync",
                i_video_drift );
            date_Set( &id->interpolated_pts, p_pic->date );
            i_pts = p_pic->date + 1;
        }
        date_Increment( &id->interpolated_pts, 1 );

        if( unlikely( b_need_duplicate ) )
        {

           if( p_sys->i_threads >= 1 )
           {
               /* We can't modify the picture, we need to duplicate it */
               p_pic2 = video_new_buffer_encoder( id->p_encoder );
               if( likely( p_pic2 != NULL ) )
               {
                   picture_Copy( p_pic2, p_pic );
                   p_pic2->date = i_pts;
               }
           }
           else
           {
               block_t *p_block;
               p_pic->date = i_pts;
               p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic);
               block_ChainAppend( out, p_block );
           }
       }
    }

    if( p_sys->i_threads == 0 )
    {
        picture_Release( p_pic );
    }
    else
    {
        vlc_mutex_lock( &p_sys->lock_out );
        picture_fifo_Push( p_sys->pp_pics, p_pic );
        if( p_pic2 != NULL )
        {
            picture_fifo_Push( p_sys->pp_pics, p_pic2 );
        }
        vlc_cond_signal( &p_sys->cond );
        vlc_mutex_unlock( &p_sys->lock_out );
    }
}
Ejemplo n.º 4
0
Archivo: video.c Proyecto: chucolin/vlc
static void OutputFrame( sout_stream_sys_t *p_sys, picture_t *p_pic, sout_stream_t *p_stream, sout_stream_id_t *id, block_t **out )
{

    picture_t *p_pic2 = NULL;
    bool b_need_duplicate=false;
    /* If input pts + input_frame_interval is lower than next_output_pts - output_frame_interval
     * Then the future input frame should fit better and we can drop this one 
     *
     * Duplication need is checked in OutputFrame */
    if( ( p_pic->date + (mtime_t)id->i_input_frame_interval ) <
        ( date_Get( &id->next_output_pts ) ) )
    {
#if 0
        msg_Dbg( p_stream, "dropping frame (%"PRId64" + %"PRId64" vs %"PRId64")",
                 p_pic->date, id->i_input_frame_interval, date_Get(&id->next_output_pts) );
#endif
        picture_Release( p_pic );
        return;
    }

    /*
     * Encoding
     */
    /* Check if we have a subpicture to overlay */
    if( p_sys->p_spu )
    {
        video_format_t fmt = id->p_encoder->fmt_in.video;
        if( fmt.i_visible_width <= 0 || fmt.i_visible_height <= 0 )
        {
            fmt.i_visible_width  = fmt.i_width;
            fmt.i_visible_height = fmt.i_height;
            fmt.i_x_offset       = 0;
            fmt.i_y_offset       = 0;
        }

        subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt,
                                             p_pic->date, p_pic->date, false );

        /* Overlay subpicture */
        if( p_subpic )
        {
            if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) )
            {
                /* We can't modify the picture, we need to duplicate it,
                 * in this point the picture is already p_encoder->fmt.in format*/
                picture_t *p_tmp = video_new_buffer_encoder( id->p_encoder );
                if( likely( p_tmp ) )
                {
                    picture_Copy( p_tmp, p_pic );
                    picture_Release( p_pic );
                    p_pic = p_tmp;
                }
            }
            if( unlikely( !p_sys->p_spu_blend ) )
                p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt );
            if( likely( p_sys->p_spu_blend ) )
                picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic );
            subpicture_Delete( p_subpic );
        }
    }
    /*This pts is handled, increase clock to next one*/
    date_Increment( &id->next_output_pts, id->p_encoder->fmt_in.video.i_frame_rate_base );

    if( p_sys->i_threads == 0 )
    {
        block_t *p_block;

        p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
        block_ChainAppend( out, p_block );
    }

    /* we need to duplicate while next_output_pts + output_frame_interval < input_pts (next input pts)*/
    b_need_duplicate = ( date_Get( &id->next_output_pts ) + id->i_output_frame_interval ) <
                       ( date_Get( &id->interpolated_pts ) );

    if( p_sys->i_threads )
    {
        if( p_sys->b_master_sync )
        {
            p_pic2 = video_new_buffer_encoder( id->p_encoder );
            if( likely( p_pic2 != NULL ) )
                picture_Copy( p_pic2, p_pic );
        }
        vlc_mutex_lock( &p_sys->lock_out );
        picture_fifo_Push( p_sys->pp_pics, p_pic );
        vlc_cond_signal( &p_sys->cond );
        vlc_mutex_unlock( &p_sys->lock_out );
    }

    while( (p_sys->b_master_sync && b_need_duplicate ))
    {
        if( p_sys->i_threads >= 1 )
        {
            picture_t *p_tmp = NULL;
            /* We can't modify the picture, we need to duplicate it */
            p_tmp = video_new_buffer_encoder( id->p_encoder );
            if( likely( p_tmp != NULL ) )
            {
                picture_Copy( p_tmp, p_pic2 );
                p_tmp->date = date_Get( &id->next_output_pts );
                vlc_mutex_lock( &p_sys->lock_out );
                picture_fifo_Push( p_sys->pp_pics, p_tmp );
                vlc_cond_signal( &p_sys->cond );
                vlc_mutex_unlock( &p_sys->lock_out );
            }
        }
        else
        {
            block_t *p_block;
            p_pic->date = date_Get( &id->next_output_pts );
            p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic);
            block_ChainAppend( out, p_block );
        }
#if 0
        msg_Dbg( p_stream, "duplicated frame");
#endif
        date_Increment( &id->next_output_pts, id->p_encoder->fmt_in.video.i_frame_rate_base );
        b_need_duplicate = ( date_Get( &id->next_output_pts ) + id->i_output_frame_interval ) <
                           ( date_Get( &id->interpolated_pts ) );
    }

    if( p_sys->i_threads && p_pic2 )
        picture_Release( p_pic2 );
    else if ( p_sys->i_threads == 0 )
        picture_Release( p_pic );
}
Ejemplo n.º 5
0
/**
 * @brief VLC filter callback
 * @return picture(s) containing the filtered frames
 */
 static picture_t* y4m_filter(filter_t* intf, picture_t* srcPic)
{
    filter_sys_t* sys = intf->p_sys;

    //msg_Info(intf, ">>>> filter");

    if (!srcPic)
    {
        //msg_Info(intf, ">>> filter: NULL INPUT");
        return NULL;
    }

    // will be stored to sys->lastDate on return
    mtime_t currDate = srcPic->date;


    // if there was a problem with the subprocess then send back
    // the picture unmodified, at least we won't freeze up vlc
    if (sys->startFailed || sys->threadExit)
        goto ECHO_RETURN;


    // start subprocess and write the y4m header
    // fixme: this can go in open() if fmt_in matches the srcPic
    if (!sys->startFailed && !sys->childPid)
    {
        sys->startFailed = !startProcess(intf);
        if (sys->startFailed)
            goto ECHO_RETURN;

        if (0 >= writeY4mHeader(intf, srcPic, sys->stdin))
        {
            msg_Err(intf, "writeY4mHeader failed: errno=%d %s",
                errno, strerror(errno));

            stopProcess(intf);
            sys->startFailed = true;

            goto ECHO_RETURN;
        }
    }

    //
    // control the buffering level by monitoring the input/output fifos
    //
    // the input/output fifos are emptied/filled by input/output threads
    // in response to the subprocess reading/writing frames
    //
    // if the input fifo is empty, then probably the subprocess wants
    // more input, so we should buffer more input
    //
    // if the output fifo is empty, and the input fifo is not empty, then
    // probably the subprocess is about to write out a frame and we
    // can wait for it to arrive. in practice, most of the time there
    // is no waiting needed, unless the filter is too slow to keep up.
    //
    bool inputEmpty = true;
    bool outputEmpty = true;

    picture_t* tmp = picture_fifo_Peek(sys->inputFifo);
    if (tmp)
    {
        picture_Release(tmp);
        inputEmpty = false;
    }

    tmp = picture_fifo_Peek(sys->outputFifo);
    if (tmp)
    {
        picture_Release(tmp);
        outputEmpty = false;
    }

    // copy picture to input fifo, we can't use picture_Hold or else
    // the decoder or vout would run out of pictures in its pool
    picture_t* inPic = picture_NewFromFormat(&srcPic->format);
    picture_Copy(inPic, srcPic);
    picture_fifo_Push(sys->inputFifo, inPic);

    // signal input thread to wake up and write some more data out
    vlc_mutex_lock(&sys->inputMutex);
    sys->bufferedIn++;
    vlc_cond_signal(&sys->inputCond);
    vlc_mutex_unlock(&sys->inputMutex);

    // if echo is enabled, we're done
    // todo: there should be a limiter on the input buffering in case
    // the subprocess can't keep up
    if (sys->echo)
        goto ECHO_RETURN;


    // keeps track of the number of buffered output pictures, assumes
    // an integer ratio
    // fixme: needs modification to support non-integer ratios
    // and ratios < 1
    sys->bufferedOut += sys->bufferRatio;

    // handle buffering
    if (outputEmpty && inputEmpty)
    {
        // we haven't supplied enough input, raise the minimum
        // level of buffer to keep and return
        sys->minBuffered += sys->bufferRatio;

        msg_Info(intf, "buffer more input: buffers:%d:%d:%d",
                sys->bufferedIn, sys->bufferedOut, sys->minBuffered);

        goto NULL_RETURN;
    }

    if (outputEmpty)
        waitForOutput(intf);

    // if we don't know what the frame interval is, make it 0 which
    // probably causes the next frames out to drop
    // note: this happens at least every time y4m_flush() is called
    // for example when seeking
    if (currDate <= sys->lastDate || sys->lastDate == 0)
    {
        //msg_Err(intf, "currDate <= lastDate");
        //goto ECHO_RETURN;
        sys->lastDate = currDate;
    }

    // reference to first and last picture we are returning
    picture_t* first = NULL;
    picture_t* last = NULL;


    picture_t* pic;
    while( (pic = picture_fifo_Pop(sys->outputFifo)) )
    {
        // do output setup when we see the first frame out from the filter,
        // it could have a different frame rate, chroma, size, etc than
        // the frame going in
        if (!sys->gotFirstOutput)
        {
            sys->gotFirstOutput = true;

            // get the in/out frame ratio by comparing frame rates
            float speed =
                    ((float)srcPic->format.i_frame_rate_base * (float)pic->format.i_frame_rate) /
                    ((float)srcPic->format.i_frame_rate      * (float)pic->format.i_frame_rate_base);

            if (speed < 1.0)
            {
                msg_Err(intf, "frame rate reduction isn't supported yet");
            }
            else
            if (speed > 1.0)
            {
                if (ceil(speed) != speed)
                    msg_Err(intf, "frame rate change must be integer ratio");

                sys->bufferRatio = speed;

                // initial ratio was 1.0, need to correct the number of buffered frames
                // now that we know what it is
                sys->bufferedOut *= sys->bufferRatio;
                sys->minBuffered *= sys->bufferRatio;
            }

            intf->fmt_out.video.i_frame_rate = pic->format.i_frame_rate;
            intf->fmt_out.video.i_frame_rate_base = pic->format.i_frame_rate_base;

            if (intf->fmt_out.video.i_chroma != pic->format.i_chroma)
                msg_Err(intf, "filter changed the chroma, expect corruption");

            // this can't be changed after open, crashes the GLX vout
            //intf->fmt_out.i_codec = pic->format.i_chroma;
            //intf->fmt_out.video.i_chroma = pic->format.i_chroma;

            msg_Info(intf, "first output: buffers=%d:%d", sys->bufferedOut, sys->minBuffered);
        }

        sys->numFrames++;
        sys->bufferedOut--;

        // it seems filter_NewPicture is required now. however,
        // sometimes it returns null in which case it seems like
        // the best thing to do is dump frames
        picture_t* copy = first == NULL ? srcPic : filter_NewPicture(intf);
        if (!copy)
        {
            picture_Release(pic);

            // throw away frames

            // vlc already prints warning for this
            //msg_Err(intf, "filter_NewPicture returns null");
            if (sys->bufferedOut < sys->minBuffered)
                break;
            else
                continue;
        }
        else
        {
            picture_CopyPixels(copy, pic);
            picture_Release(pic);
            pic = copy;
        }

        // the time per output frame interval is a fraction of the input frame time
        int frameTime = (currDate - sys->lastDate) / sys->bufferRatio;

        // the pts is whatever the current pts is minus any buffering
        // introduced by the filter
        pic->date = currDate - sys->bufferedOut*frameTime;

//        msg_Info(intf, "frame=%d buffered=%d:%d frameTime=%d ratio:%d:1 fin=%d:%d fout=%d:%d pts=%u",
//            sys->numFrames, sys->bufferedOut, sys->minBuffered,
//            frameTime, sys->bufferRatio,
//            srcPic->format.i_frame_rate, srcPic->format.i_frame_rate_base,
//            pic->format.i_frame_rate, pic->format.i_frame_rate_base,
//            (unsigned int)pic->date);

        if (last)
            last->p_next = pic;
        else
            first = pic;
        last = pic;


        // if we read too many frames on this iteration, on the next
        // one we might not have any frames available which would be
        // bad as vlc would think our intent was to drop frames
        //
        // if we stop reading before the output is completely empty,
        // there will always be some frames for the next iteration,
        // assuming the filter is fast enough to keep up
        if (sys->bufferedOut  < sys->minBuffered)
            break;

        // if there is still some input buffer left, but the fifo is
        // empty, wait for next frame to arrive. otherwise we can
        // build too much input buffering
        if (sys->bufferedIn > 1)
            waitForOutput(intf);
    }

    if (!first)
    {
        // the buffer checks should prevent from getting here, but
        // just in case prevent leaking the input picture
        picture_Release(srcPic);

        sys->minBuffered++;
    }

    sys->lastDate = currDate;
    return first;
ECHO_RETURN:
    sys->lastDate = currDate;
    //msg_Info(intf, "<<<< filter: ECHO");
    return srcPic;
NULL_RETURN:
    sys->lastDate = currDate;
    picture_Release(srcPic);
    //msg_Info(intf, "<<<< filter: NULL");
    return NULL;
}