Newer
Older
Fabrice Bellard
committed
}
}
/* get the current video clock value */
static double get_video_clock(VideoState *is)
{
Michael Niedermayer
committed
return is->video_current_pts_drift + av_gettime() / 1000000.0;
Fabrice Bellard
committed
}
/* get the current external clock value */
static double get_external_clock(VideoState *is)
{
int64_t ti;
ti = av_gettime();
return is->external_clock + ((ti - is->external_clock_time) * 1e-6);
}
/* get the current master clock value */
static double get_master_clock(VideoState *is)
{
double val;
if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
if (is->video_st)
val = get_video_clock(is);
else
val = get_audio_clock(is);
} else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
if (is->audio_st)
val = get_audio_clock(is);
else
val = get_video_clock(is);
} else {
Fabrice Bellard
committed
val = get_external_clock(is);
Fabrice Bellard
committed
return val;
}
Michael Niedermayer
committed
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
if (!is->seek_req) {
is->seek_pos = pos;
if (seek_by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
}
static void stream_toggle_pause(VideoState *is)
Michael Niedermayer
committed
if (is->paused) {
is->frame_timer += av_gettime() / 1000000.0 + is->video_current_pts_drift - is->video_current_pts;
if(is->read_pause_return != AVERROR(ENOSYS)){
Michael Niedermayer
committed
is->video_current_pts = is->video_current_pts_drift + av_gettime() / 1000000.0;
Michael Niedermayer
committed
is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;
Michael Niedermayer
committed
is->paused = !is->paused;
static double compute_target_delay(double delay, VideoState *is)
double sync_threshold, diff;
/* update delay to follow master synchronisation source */
if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) ||
is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) {
/* if video is slave, we try to correct big delays by
duplicating or deleting a frame */
diff = get_video_clock(is) - get_master_clock(is);
/* skip or repeat frame. We take into account the
delay to compute the threshold. I still don't know
if it is the best guess */
sync_threshold = FFMAX(AV_SYNC_THRESHOLD, delay);
if (fabs(diff) < AV_NOSYNC_THRESHOLD) {
if (diff <= -sync_threshold)
delay = 0;
else if (diff >= sync_threshold)
delay = 2 * delay;
}
}
av_dlog(NULL, "video: delay=%0.3f A-V=%f\n",
delay, -diff);
return delay;
}
static void pictq_next_picture(VideoState *is) {
/* update queue size and signal for next picture */
if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)
is->pictq_rindex = 0;
SDL_LockMutex(is->pictq_mutex);
is->pictq_size--;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
static void update_video_pts(VideoState *is, double pts, int64_t pos) {
double time = av_gettime() / 1000000.0;
/* update current video pts */
is->video_current_pts = pts;
is->video_current_pts_drift = is->video_current_pts - time;
is->video_current_pos = pos;
is->frame_last_pts = pts;
}
static void video_refresh(void *opaque)
{
VideoState *is = opaque;
VideoPicture *vp;
Fabrice Bellard
committed
SubPicture *sp, *sp2;
SDL_LockMutex(is->pictq_mutex);
if (is->frame_last_dropped_pts != AV_NOPTS_VALUE && is->frame_last_dropped_pts > is->frame_last_pts) {
update_video_pts(is, is->frame_last_dropped_pts, is->frame_last_dropped_pos);
is->frame_last_dropped_pts = AV_NOPTS_VALUE;
}
SDL_UnlockMutex(is->pictq_mutex);
//nothing to do, no picture to display in the que
double last_duration, duration, delay;
Fabrice Bellard
committed
/* dequeue the picture */
Fabrice Bellard
committed
if (vp->skip) {
pictq_next_picture(is);
goto retry;
}
/* compute nominal last_duration */
last_duration = vp->pts - is->frame_last_pts;
if (last_duration > 0 && last_duration < 10.0) {
/* if duration of the last frame was sane, update last_duration in video state */
is->frame_last_duration = last_duration;
}
delay = compute_target_delay(is->frame_last_duration, is);
if(time < is->frame_timer + delay)
Marton Balint
committed
if (delay > 0)
is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay));
SDL_LockMutex(is->pictq_mutex);
update_video_pts(is, vp->pts, vp->pos);
SDL_UnlockMutex(is->pictq_mutex);
if(is->pictq_size > 1) {
VideoPicture *nextvp= &is->pictq[(is->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE];
duration = nextvp->pts - vp->pts; // More accurate this way, 1/time_base is often not reflecting FPS
} else {
duration = vp->duration;
if((framedrop>0 || (framedrop && is->audio_st)) && time > is->frame_timer + duration){
if(is->pictq_size > 1){
pictq_next_picture(is);
Fabrice Bellard
committed
if(is->subtitle_st) {
if (is->subtitle_stream_changed) {
SDL_LockMutex(is->subpq_mutex);
while (is->subpq_size) {
free_subpicture(&is->subpq[is->subpq_rindex]);
/* update queue size and signal for next picture */
if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE)
is->subpq_rindex = 0;
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
is->subpq_size--;
}
is->subtitle_stream_changed = 0;
SDL_CondSignal(is->subpq_cond);
SDL_UnlockMutex(is->subpq_mutex);
} else {
if (is->subpq_size > 0) {
sp = &is->subpq[is->subpq_rindex];
if (is->subpq_size > 1)
sp2 = &is->subpq[(is->subpq_rindex + 1) % SUBPICTURE_QUEUE_SIZE];
else
sp2 = NULL;
if ((is->video_current_pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
|| (sp2 && is->video_current_pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
{
free_subpicture(sp);
/* update queue size and signal for next picture */
if (++is->subpq_rindex == SUBPICTURE_QUEUE_SIZE)
is->subpq_rindex = 0;
SDL_LockMutex(is->subpq_mutex);
is->subpq_size--;
SDL_CondSignal(is->subpq_cond);
SDL_UnlockMutex(is->subpq_mutex);
}
}
}
}
if (!display_disable)
pictq_next_picture(is);
}
} else if (is->audio_st) {
/* draw the next audio frame */
/* if only audio stream, then display the audio bars (better
than nothing, just to test the implementation */
if (!display_disable)
}
if (show_status) {
static int64_t last_time;
int64_t cur_time;
int aqsize, vqsize, sqsize;
Fabrice Bellard
committed
double av_diff;
if (!last_time || (cur_time - last_time) >= 30000) {
sqsize = 0;
if (is->audio_st)
aqsize = is->audioq.size;
if (is->video_st)
vqsize = is->videoq.size;
if (is->subtitle_st)
sqsize = is->subtitleq.size;
Fabrice Bellard
committed
av_diff = 0;
if (is->audio_st && is->video_st)
av_diff = get_audio_clock(is) - get_video_clock(is);
printf("%7.2f A-V:%7.3f aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \r",
get_master_clock(is),
av_diff,
aqsize / 1024,
vqsize / 1024,
sqsize,
is->video_st ? is->video_st->codec->pts_correction_num_faulty_dts : 0,
is->video_st ? is->video_st->codec->pts_correction_num_faulty_pts : 0);
fflush(stdout);
last_time = cur_time;
}
}
}
/* allocate a picture (needs to do that in main thread to avoid
potential locking problems */
static void alloc_picture(void *opaque)
{
VideoState *is = opaque;
VideoPicture *vp;
vp = &is->pictq[is->pictq_windex];
if (vp->bmp)
SDL_FreeYUVOverlay(vp->bmp);
#if CONFIG_AVFILTER
if (vp->picref)
avfilter_unref_buffer(vp->picref);
vp->picref = NULL;
vp->width = is->out_video_filter->inputs[0]->w;
vp->height = is->out_video_filter->inputs[0]->h;
vp->pix_fmt = is->out_video_filter->inputs[0]->format;
#else
vp->width = is->video_st->codec->width;
vp->height = is->video_st->codec->height;
vp->pix_fmt = is->video_st->codec->pix_fmt;
#endif
vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height,
Fabrice Bellard
committed
screen);
if (!vp->bmp || vp->bmp->pitches[0] < vp->width) {
/* SDL allocates a buffer smaller than requested if the video
* overlay hardware is unable to support the requested size. */
fprintf(stderr, "Error: the video system does not support an image\n"
"size of %dx%d pixels. Try using -lowres or -vf \"scale=w:h\"\n"
"to reduce the image size.\n", vp->width, vp->height );
SDL_LockMutex(is->pictq_mutex);
vp->allocated = 1;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
}
static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_t pos)
double frame_delay, pts = pts1;
/* compute the exact PTS for the picture if it is omitted in the stream
* pts1 is the dts of the pkt / pts of the frame */
if (pts != 0) {
/* update video clock with pts, if present */
is->video_clock = pts;
} else {
pts = is->video_clock;
}
/* update video clock for next frame */
frame_delay = av_q2d(is->video_st->codec->time_base);
/* for MPEG2, the frame can be repeated, so we update the
clock accordingly */
frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
is->video_clock += frame_delay;
#if defined(DEBUG_SYNC) && 0
printf("frame_type=%c clock=%0.3f pts=%0.3f\n",
Stefano Sabatini
committed
av_get_picture_type_char(src_frame->pict_type), pts, pts1);
/* wait until we have space to put a new picture */
SDL_LockMutex(is->pictq_mutex);
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
!is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex);
if (is->videoq.abort_request)
return -1;
vp = &is->pictq[is->pictq_windex];
vp->duration = frame_delay;
if (!vp->bmp || vp->reallocate ||
#if CONFIG_AVFILTER
vp->width != is->out_video_filter->inputs[0]->w ||
vp->height != is->out_video_filter->inputs[0]->h) {
#else
Michael Niedermayer
committed
vp->width != is->video_st->codec->width ||
vp->height != is->video_st->codec->height) {
vp->allocated = 0;
vp->reallocate = 0;
/* the allocation must be done in the main thread to avoid
locking problems */
event.type = FF_ALLOC_EVENT;
event.user.data1 = is;
SDL_PushEvent(&event);
/* wait until the picture is allocated */
SDL_LockMutex(is->pictq_mutex);
while (!vp->allocated && !is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
/* if the queue is aborted, we have to pop the pending ALLOC event or wait for the allocation to complete */
if (is->videoq.abort_request && SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(FF_ALLOC_EVENT)) != 1) {
while (!vp->allocated) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
}
SDL_UnlockMutex(is->pictq_mutex);
if (is->videoq.abort_request)
return -1;
}
Fabrice Bellard
committed
/* if the frame is not skipped, then display it */
AVPicture pict;
#if CONFIG_AVFILTER
if(vp->picref)
avfilter_unref_buffer(vp->picref);
vp->picref = src_frame->opaque;
#endif
/* get a pointer on the bitmap */
SDL_LockYUVOverlay (vp->bmp);
memset(&pict,0,sizeof(AVPicture));
pict.data[0] = vp->bmp->pixels[0];
pict.data[1] = vp->bmp->pixels[2];
pict.data[2] = vp->bmp->pixels[1];
pict.linesize[0] = vp->bmp->pitches[0];
pict.linesize[1] = vp->bmp->pitches[2];
pict.linesize[2] = vp->bmp->pitches[1];
#if CONFIG_AVFILTER
//FIXME use direct rendering
av_picture_copy(&pict, (AVPicture *)src_frame,
vp->pix_fmt, vp->width, vp->height);
#else
sws_flags = av_get_int(sws_opts, "sws_flags", NULL);
is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx,
vp->width, vp->height, vp->pix_fmt, vp->width, vp->height,
PIX_FMT_YUV420P, sws_flags, NULL, NULL, NULL);
if (is->img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context\n");
exit(1);
}
sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize,
0, vp->height, pict.data, pict.linesize);
#endif
/* update the bitmap content */
SDL_UnlockYUVOverlay(vp->bmp);
Fabrice Bellard
committed
vp->pts = pts;
Michael Niedermayer
committed
vp->pos = pos;
vp->skip = 0;
/* now we can update the picture count */
if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)
is->pictq_windex = 0;
SDL_LockMutex(is->pictq_mutex);
is->pictq_size++;
SDL_UnlockMutex(is->pictq_mutex);
}
Fabrice Bellard
committed
return 0;
}
static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacket *pkt)
Diego Biurrun
committed
int got_picture, i;
if (packet_queue_get(&is->videoq, pkt, 1) < 0)
return -1;
Michael Niedermayer
committed
if (pkt->data == flush_pkt.data) {
avcodec_flush_buffers(is->video_st->codec);
Michael Niedermayer
committed
SDL_LockMutex(is->pictq_mutex);
//Make sure there are no long delay timers (ideally we should just flush the que but thats harder)
for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++) {
is->pictq[i].skip = 1;
}
while (is->pictq_size && !is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
is->video_current_pos = -1;
is->frame_last_pts = AV_NOPTS_VALUE;
is->frame_last_duration = 0;
is->frame_timer = (double)av_gettime() / 1000000.0;
is->frame_last_dropped_pts = AV_NOPTS_VALUE;
SDL_UnlockMutex(is->pictq_mutex);
Diego Biurrun
committed
avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt);
if (decoder_reorder_pts == -1) {
*pts = frame->best_effort_timestamp;
} else if (decoder_reorder_pts) {
Stefano Sabatini
committed
*pts = frame->pkt_pts;
}
if (*pts == AV_NOPTS_VALUE) {
*pts = 0;
Michael Niedermayer
committed
}
if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) || is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK) &&
(framedrop>0 || (framedrop && is->audio_st))) {
SDL_LockMutex(is->pictq_mutex);
if (is->frame_last_pts != AV_NOPTS_VALUE && *pts) {
double clockdiff = get_video_clock(is) - get_master_clock(is);
double dpts = av_q2d(is->video_st->time_base) * *pts;
double ptsdiff = dpts - is->frame_last_pts;
if (fabs(clockdiff) < AV_NOSYNC_THRESHOLD &&
ptsdiff > 0 && ptsdiff < AV_NOSYNC_THRESHOLD &&
clockdiff + ptsdiff - is->frame_last_filter_delay < 0) {
is->frame_last_dropped_pos = pkt->pos;
is->frame_last_dropped_pts = dpts;
ret = 0;
}
}
SDL_UnlockMutex(is->pictq_mutex);
}
if (ret)
is->frame_last_returned_time = av_gettime() / 1000000.0;
return 0;
}
#if CONFIG_AVFILTER
typedef struct {
VideoState *is;
AVFrame *frame;
int use_dr1;
static int input_get_buffer(AVCodecContext *codec, AVFrame *pic)
{
AVFilterContext *ctx = codec->opaque;
AVFilterBufferRef *ref;
int perms = AV_PERM_WRITE;
int i, w, h, stride[4];
unsigned edge;
int pixel_size;
av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE);
if (codec->codec->capabilities & CODEC_CAP_NEG_LINESIZES)
perms |= AV_PERM_NEG_LINESIZES;
if(pic->buffer_hints & FF_BUFFER_HINTS_VALID) {
if(pic->buffer_hints & FF_BUFFER_HINTS_READABLE) perms |= AV_PERM_READ;
if(pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) perms |= AV_PERM_PRESERVE;
if(pic->buffer_hints & FF_BUFFER_HINTS_REUSABLE) perms |= AV_PERM_REUSE2;
}
if(pic->reference) perms |= AV_PERM_READ | AV_PERM_PRESERVE;
w = codec->width;
h = codec->height;
if(av_image_check_size(w, h, 0, codec))
return -1;
avcodec_align_dimensions2(codec, &w, &h, stride);
edge = codec->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width();
w += edge << 1;
h += edge << 1;
if(!(ref = avfilter_get_video_buffer(ctx->outputs[0], perms, w, h)))
return -1;
pixel_size = av_pix_fmt_descriptors[ref->format].comp[0].step_minus1+1;
S.N. Hemanth Meenakshisundaram
committed
ref->video->w = codec->width;
ref->video->h = codec->height;
for(i = 0; i < 4; i ++) {
unsigned hshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_w : 0;
unsigned vshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_h : 0;
if (ref->data[i]) {
ref->data[i] += ((edge * pixel_size) >> hshift) + ((edge * ref->linesize[i]) >> vshift);
pic->data[i] = ref->data[i];
pic->linesize[i] = ref->linesize[i];
}
pic->opaque = ref;
pic->age = INT_MAX;
pic->type = FF_BUFFER_TYPE_USER;
pic->reordered_opaque = codec->reordered_opaque;
Michael Niedermayer
committed
if(codec->pkt) pic->pkt_pts = codec->pkt->pts;
else pic->pkt_pts = AV_NOPTS_VALUE;
return 0;
}
static void input_release_buffer(AVCodecContext *codec, AVFrame *pic)
{
memset(pic->data, 0, sizeof(pic->data));
avfilter_unref_buffer(pic->opaque);
static int input_reget_buffer(AVCodecContext *codec, AVFrame *pic)
{
AVFilterBufferRef *ref = pic->opaque;
if (pic->data[0] == NULL) {
pic->buffer_hints |= FF_BUFFER_HINTS_READABLE;
return codec->get_buffer(codec, pic);
}
S.N. Hemanth Meenakshisundaram
committed
if ((codec->width != ref->video->w) || (codec->height != ref->video->h) ||
(codec->pix_fmt != ref->format)) {
av_log(codec, AV_LOG_ERROR, "Picture properties changed.\n");
return -1;
}
pic->reordered_opaque = codec->reordered_opaque;
Michael Niedermayer
committed
if(codec->pkt) pic->pkt_pts = codec->pkt->pts;
else pic->pkt_pts = AV_NOPTS_VALUE;
return 0;
}
static int input_init(AVFilterContext *ctx, const char *args, void *opaque)
{
FilterPriv *priv = ctx->priv;
AVCodecContext *codec;
if(!opaque) return -1;
priv->is = opaque;
codec = priv->is->video_st->codec;
codec->opaque = ctx;
if((codec->codec->capabilities & CODEC_CAP_DR1)
) {
av_assert0(codec->flags & CODEC_FLAG_EMU_EDGE);
priv->use_dr1 = 1;
codec->get_buffer = input_get_buffer;
codec->release_buffer = input_release_buffer;
codec->reget_buffer = input_reget_buffer;
codec->thread_safe_callbacks = 1;
priv->frame = avcodec_alloc_frame();
return 0;
}
static void input_uninit(AVFilterContext *ctx)
{
FilterPriv *priv = ctx->priv;
av_free(priv->frame);
}
static int input_request_frame(AVFilterLink *link)
{
FilterPriv *priv = link->src->priv;
AVFilterBufferRef *picref;
AVPacket pkt;
int ret;
while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt)))
av_free_packet(&pkt);
if (ret < 0)
return -1;
picref = avfilter_ref_buffer(priv->frame->opaque, ~0);
picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h);
av_image_copy(picref->data, picref->linesize,
priv->frame->data, priv->frame->linesize,
picref->format, link->w, link->h);
avfilter_copy_frame_props(picref, priv->frame);
avfilter_start_frame(link, picref);
avfilter_draw_slice(link, 0, link->h, 1);
avfilter_end_frame(link);
return 0;
}
static int input_query_formats(AVFilterContext *ctx)
{
FilterPriv *priv = ctx->priv;
enum PixelFormat pix_fmts[] = {
priv->is->video_st->codec->pix_fmt, PIX_FMT_NONE
};
avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
return 0;
}
static int input_config_props(AVFilterLink *link)
{
FilterPriv *priv = link->src->priv;
Stefano Sabatini
committed
AVStream *s = priv->is->video_st;
Stefano Sabatini
committed
link->w = s->codec->width;
link->h = s->codec->height;
Stefano Sabatini
committed
link->sample_aspect_ratio = s->sample_aspect_ratio.num ?
Stefano Sabatini
committed
s->sample_aspect_ratio : s->codec->sample_aspect_ratio;
link->time_base = s->time_base;
return 0;
}
static AVFilter input_filter =
{
.name = "ffplay_input",
.priv_size = sizeof(FilterPriv),
.init = input_init,
.uninit = input_uninit,
.query_formats = input_query_formats,
.inputs = (AVFilterPad[]) {{ .name = NULL }},
.outputs = (AVFilterPad[]) {{ .name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.request_frame = input_request_frame,
.config_props = input_config_props, },
{ .name = NULL }},
};
Stefano Sabatini
committed
static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters)
Stefano Sabatini
committed
int ret;
enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE };
AVBufferSinkParams *buffersink_params = av_buffersink_params_alloc();
AVFilterContext *filt_src = NULL, *filt_out = NULL;
snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags);
graph->scale_sws_opts = av_strdup(sws_flags_str);
Stefano Sabatini
committed
if ((ret = avfilter_graph_create_filter(&filt_src, &input_filter, "src",
NULL, is, graph)) < 0)
return ret;
#if FF_API_OLD_VSINK_API
ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out",
NULL, pix_fmts, graph);
#else
buffersink_params->pixel_fmts = pix_fmts;
ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), "out",
NULL, buffersink_params, graph);
#endif
av_freep(&buffersink_params);
if (ret < 0)
return ret;
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
outputs->name = av_strdup("in");
Stefano Sabatini
committed
outputs->filter_ctx = filt_src;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
Stefano Sabatini
committed
inputs->filter_ctx = filt_out;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse(graph, vfilters, &inputs, &outputs, NULL)) < 0)
return ret;
Stefano Sabatini
committed
if ((ret = avfilter_link(filt_src, 0, filt_out, 0)) < 0)
return ret;
Stefano Sabatini
committed
if ((ret = avfilter_graph_config(graph, NULL)) < 0)
return ret;
is->out_video_filter = filt_out;
Stefano Sabatini
committed
return ret;
}
#endif /* CONFIG_AVFILTER */
static int video_thread(void *arg)
{
VideoState *is = arg;
AVFrame *frame= avcodec_alloc_frame();
int64_t pts_int = AV_NOPTS_VALUE, pos = -1;
Stefano Sabatini
committed
double pts;
int ret;
#if CONFIG_AVFILTER
AVFilterGraph *graph = avfilter_graph_alloc();
AVFilterContext *filt_out = NULL;
int last_w = is->video_st->codec->width;
int last_h = is->video_st->codec->height;
Stefano Sabatini
committed
if ((ret = configure_video_filters(graph, is, vfilters)) < 0)
goto the_end;
filt_out = is->out_video_filter;
#endif
for(;;) {
#if !CONFIG_AVFILTER
AVPacket pkt;
#else
AVFilterBufferRef *picref;
AVRational tb = filt_out->inputs[0]->time_base;
#endif
while (is->paused && !is->videoq.abort_request)
SDL_Delay(10);
#if CONFIG_AVFILTER
if ( last_w != is->video_st->codec->width
|| last_h != is->video_st->codec->height) {
Stefano Sabatini
committed
av_log(NULL, AV_LOG_INFO, "Frame changed from size:%dx%d to size:%dx%d\n",
last_w, last_h, is->video_st->codec->width, is->video_st->codec->height);
avfilter_graph_free(&graph);
graph = avfilter_graph_alloc();
if ((ret = configure_video_filters(graph, is, vfilters)) < 0)
goto the_end;
filt_out = is->out_video_filter;
last_w = is->video_st->codec->width;
last_h = is->video_st->codec->height;
}
ret = av_buffersink_get_buffer_ref(filt_out, &picref, 0);
if (picref) {
avfilter_fill_frame_from_video_buffer_ref(frame, picref);
pts_int = picref->pts;
pos = picref->pos;
frame->opaque = picref;
}
if (av_cmp_q(tb, is->video_st->time_base)) {
av_unused int64_t pts1 = pts_int;
pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base);
av_dlog(NULL, "video_thread(): "
"tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n",
tb.num, tb.den, pts1,
is->video_st->time_base.num, is->video_st->time_base.den, pts_int);
}
#else
ret = get_video_frame(is, frame, &pts_int, &pkt);
pos = pkt.pos;
#endif
if (ret < 0) goto the_end;
is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time;
if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
is->frame_last_filter_delay = 0;
pts = pts_int*av_q2d(is->video_st->time_base);
ret = queue_picture(is, frame, pts, pos);
if (ret < 0)
goto the_end;
if (is->step)
stream_toggle_pause(is);
avfilter_graph_free(&graph);
static int subtitle_thread(void *arg)
{
VideoState *is = arg;
SubPicture *sp;
AVPacket pkt1, *pkt = &pkt1;
Diego Biurrun
committed
int got_subtitle;
double pts;
int i, j;
int r, g, b, y, u, v, a;
for(;;) {
while (is->paused && !is->subtitleq.abort_request) {
SDL_Delay(10);
}
if (packet_queue_get(&is->subtitleq, pkt, 1) < 0)
break;
if(pkt->data == flush_pkt.data){
avcodec_flush_buffers(is->subtitle_st->codec);
continue;
}
SDL_LockMutex(is->subpq_mutex);
while (is->subpq_size >= SUBPICTURE_QUEUE_SIZE &&
!is->subtitleq.abort_request) {
SDL_CondWait(is->subpq_cond, is->subpq_mutex);
}
SDL_UnlockMutex(is->subpq_mutex);
if (is->subtitleq.abort_request)
return 0;
sp = &is->subpq[is->subpq_windex];
/* NOTE: ipts is the PTS of the _first_ picture beginning in
this packet, if any */
pts = 0;
if (pkt->pts != AV_NOPTS_VALUE)
pts = av_q2d(is->subtitle_st->time_base)*pkt->pts;
Diego Biurrun
committed
avcodec_decode_subtitle2(is->subtitle_st->codec, &sp->sub,
&got_subtitle, pkt);
if (got_subtitle && sp->sub.format == 0) {
sp->pts = pts;
for (i = 0; i < sp->sub.num_rects; i++)
{
for (j = 0; j < sp->sub.rects[i]->nb_colors; j++)
{
RGBA_IN(r, g, b, a, (uint32_t*)sp->sub.rects[i]->pict.data[1] + j);
y = RGB_TO_Y_CCIR(r, g, b);
u = RGB_TO_U_CCIR(r, g, b, 0);
v = RGB_TO_V_CCIR(r, g, b, 0);
YUVA_OUT((uint32_t*)sp->sub.rects[i]->pict.data[1] + j, y, u, v, a);
}
}
/* now we can update the picture count */
if (++is->subpq_windex == SUBPICTURE_QUEUE_SIZE)
is->subpq_windex = 0;
SDL_LockMutex(is->subpq_mutex);
is->subpq_size++;
SDL_UnlockMutex(is->subpq_mutex);
}
av_free_packet(pkt);
}
return 0;
}
/* copy samples for viewing in editor window */
static void update_sample_display(VideoState *is, short *samples, int samples_size)
{
size = samples_size / sizeof(short);
while (size > 0) {
len = SAMPLE_ARRAY_SIZE - is->sample_array_index;
if (len > size)
len = size;
memcpy(is->sample_array + is->sample_array_index, samples, len * sizeof(short));
samples += len;
is->sample_array_index += len;
if (is->sample_array_index >= SAMPLE_ARRAY_SIZE)
is->sample_array_index = 0;
size -= len;
}
}
/* return the new audio buffer size (samples can be added or deleted
to get better sync if video or external master clock) */
static int synchronize_audio(VideoState *is, short *samples,
Fabrice Bellard
committed
int samples_size1, double pts)
Fabrice Bellard
committed
int n, samples_size;
n = av_get_bytes_per_sample(is->audio_tgt_fmt) * is->audio_tgt_channels;
Fabrice Bellard
committed
samples_size = samples_size1;
/* if not master, then we try to remove or add samples to correct the clock */
if (((is->av_sync_type == AV_SYNC_VIDEO_MASTER && is->video_st) ||
Fabrice Bellard
committed
is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) {
double diff, avg_diff;
int wanted_size, min_size, max_size, nb_samples;
Fabrice Bellard
committed
ref_clock = get_master_clock(is);
diff = get_audio_clock(is) - ref_clock;
Fabrice Bellard
committed
if (diff < AV_NOSYNC_THRESHOLD) {
is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum;
if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {
/* not enough measures to have a correct estimate */
is->audio_diff_avg_count++;
} else {
/* estimate the A-V difference */
avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
if (fabs(avg_diff) >= is->audio_diff_threshold) {
wanted_size = samples_size + ((int)(diff * is->audio_tgt_freq) * n);
Fabrice Bellard
committed
nb_samples = samples_size / n;
Fabrice Bellard
committed
min_size = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n;
max_size = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX)) / 100) * n;
if (wanted_size < min_size)
wanted_size = min_size;
else if (wanted_size > FFMIN3(max_size, sizeof(is->audio_buf1), sizeof(is->audio_buf2)))
wanted_size = FFMIN3(max_size, sizeof(is->audio_buf1), sizeof(is->audio_buf2));
Fabrice Bellard
committed
/* add or remove samples to correction the synchro */
if (wanted_size < samples_size) {
/* remove samples */
samples_size = wanted_size;
} else if (wanted_size > samples_size) {
uint8_t *samples_end, *q;