Newer
Older
Fabrice Bellard
committed
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
/* update current video pts */
is->video_current_pts = vp->pts;
is->video_current_pts_time = av_gettime();
/* compute nominal delay */
delay = vp->pts - is->frame_last_pts;
if (delay <= 0 || delay >= 1.0) {
/* if incorrect delay, use previous one */
delay = is->frame_last_delay;
}
is->frame_last_delay = delay;
is->frame_last_pts = vp->pts;
/* 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 */
ref_clock = get_master_clock(is);
diff = vp->pts - ref_clock;
Fabrice Bellard
committed
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
/* 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 = AV_SYNC_THRESHOLD;
if (delay > sync_threshold)
sync_threshold = delay;
if (fabs(diff) < AV_NOSYNC_THRESHOLD) {
if (diff <= -sync_threshold)
delay = 0;
else if (diff >= sync_threshold)
delay = 2 * delay;
}
}
is->frame_timer += delay;
/* compute the REAL delay (we need to do that to avoid
long term errors */
actual_delay = is->frame_timer - (av_gettime() / 1000000.0);
if (actual_delay < 0.010) {
/* XXX: should skip picture */
actual_delay = 0.010;
}
Fabrice Bellard
committed
schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));
#if defined(DEBUG_SYNC)
printf("video: delay=%0.3f actual_delay=%0.3f pts=%0.3f A-V=%f\n",
Fabrice Bellard
committed
delay, actual_delay, vp->pts, -diff);
#endif
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;
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
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);
}
}
}
}
/* 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);
}
} else if (is->audio_st) {
/* draw the next audio frame */
schedule_refresh(is, 40);
/* if only audio stream, then display the audio bars (better
than nothing, just to test the implementation */
/* display picture */
video_display(is);
} else {
schedule_refresh(is, 100);
}
if (show_status) {
static int64_t last_time;
int64_t cur_time;
int aqsize, vqsize, sqsize;
Fabrice Bellard
committed
double av_diff;
cur_time = av_gettime();
if (!last_time || (cur_time - last_time) >= 500 * 1000) {
aqsize = 0;
vqsize = 0;
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 \r",
get_master_clock(is), av_diff, aqsize / 1024, vqsize / 1024, sqsize);
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);
Fabrice Bellard
committed
#if 0
Fabrice Bellard
committed
/* XXX: disable overlay if no hardware acceleration or if RGB format */
Michael Niedermayer
committed
switch(is->video_st->codec->pix_fmt) {
case PIX_FMT_YUV420P:
case PIX_FMT_YUV422P:
case PIX_FMT_YUV444P:
case PIX_FMT_YUYV422:
case PIX_FMT_YUV410P:
case PIX_FMT_YUV411P:
is_yuv = 1;
break;
default:
is_yuv = 0;
break;
}
#endif
Michael Niedermayer
committed
vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width,
is->video_st->codec->height,
Fabrice Bellard
committed
screen);
Michael Niedermayer
committed
vp->width = is->video_st->codec->width;
vp->height = is->video_st->codec->height;
SDL_LockMutex(is->pictq_mutex);
vp->allocated = 1;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
}
/**
*
* @param pts the dts of the pkt / pts of the frame and guessed if not known
*/
Fabrice Bellard
committed
static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
{
VideoPicture *vp;
int dst_pix_fmt;
AVPicture pict;
static struct SwsContext *img_convert_ctx;
/* 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];
/* alloc or resize hardware picture buffer */
Michael Niedermayer
committed
vp->width != is->video_st->codec->width ||
vp->height != is->video_st->codec->height) {
SDL_Event event;
vp->allocated = 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);
}
SDL_UnlockMutex(is->pictq_mutex);
if (is->videoq.abort_request)
return -1;
}
Fabrice Bellard
committed
/* if the frame is not skipped, then display it */
if (vp->bmp) {
/* get a pointer on the bitmap */
SDL_LockYUVOverlay (vp->bmp);
dst_pix_fmt = PIX_FMT_YUV420P;
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];
img_convert_ctx = sws_getCachedContext(img_convert_ctx,
is->video_st->codec->width, is->video_st->codec->height,
is->video_st->codec->pix_fmt,
is->video_st->codec->width, is->video_st->codec->height,
dst_pix_fmt, sws_flags, NULL, NULL, NULL);
if (img_convert_ctx == NULL) {
fprintf(stderr, "Cannot initialize the conversion context\n");
exit(1);
}
sws_scale(img_convert_ctx, src_frame->data, src_frame->linesize,
0, is->video_st->codec->height, pict.data, pict.linesize);
/* update the bitmap content */
SDL_UnlockYUVOverlay(vp->bmp);
Fabrice Bellard
committed
vp->pts = pts;
/* 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;
}
/**
* compute the exact PTS for the picture if it is omitted in the stream
* @param pts1 the dts of the pkt / pts of the frame
*/
Fabrice Bellard
committed
static int output_picture2(VideoState *is, AVFrame *src_frame, double pts1)
{
double frame_delay, pts;
Fabrice Bellard
committed
pts = pts1;
Fabrice Bellard
committed
/* update video clock with pts, if present */
pts = is->video_clock;
}
/* update video clock for next frame */
Michael Niedermayer
committed
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);
Fabrice Bellard
committed
#if defined(DEBUG_SYNC) && 0
{
int ftype;
if (src_frame->pict_type == FF_B_TYPE)
ftype = 'B';
else if (src_frame->pict_type == FF_I_TYPE)
ftype = 'I';
else
ftype = 'P';
printf("frame_type=%c clock=%0.3f pts=%0.3f\n",
Fabrice Bellard
committed
}
#endif
Michael Niedermayer
committed
static uint64_t global_video_pkt_pts= AV_NOPTS_VALUE;
static int my_get_buffer(struct AVCodecContext *c, AVFrame *pic){
int ret= avcodec_default_get_buffer(c, pic);
uint64_t *pts= av_malloc(sizeof(uint64_t));
*pts= global_video_pkt_pts;
pic->opaque= pts;
return ret;
}
static void my_release_buffer(struct AVCodecContext *c, AVFrame *pic){
if(pic) av_freep(&pic->opaque);
avcodec_default_release_buffer(c, pic);
}
static int video_thread(void *arg)
{
VideoState *is = arg;
AVPacket pkt1, *pkt = &pkt1;
AVFrame *frame= avcodec_alloc_frame();
double pts;
for(;;) {
while (is->paused && !is->videoq.abort_request) {
SDL_Delay(10);
}
if (packet_queue_get(&is->videoq, pkt, 1) < 0)
break;
if(pkt->data == flush_pkt.data){
avcodec_flush_buffers(is->video_st->codec);
continue;
}
Fabrice Bellard
committed
/* NOTE: ipts is the PTS of the _first_ picture beginning in
this packet, if any */
global_video_pkt_pts= pkt->pts;
len1 = avcodec_decode_video(is->video_st->codec,
frame, &got_picture,
pkt->data, pkt->size);
if( (decoder_reorder_pts || pkt->dts == AV_NOPTS_VALUE)
&& frame->opaque && *(uint64_t*)frame->opaque != AV_NOPTS_VALUE)
pts= *(uint64_t*)frame->opaque;
else if(pkt->dts != AV_NOPTS_VALUE)
pts= pkt->dts;
else
pts= 0;
pts *= av_q2d(is->video_st->time_base);
Michael Niedermayer
committed
if (got_picture) {
if (output_picture2(is, frame, pts) < 0)
goto the_end;
}
Wolfgang Hesseler
committed
if (cur_stream)
stream_pause(cur_stream);
static int subtitle_thread(void *arg)
{
VideoState *is = arg;
SubPicture *sp;
AVPacket pkt1, *pkt = &pkt1;
int len1, 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)
goto the_end;
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;
len1 = avcodec_decode_subtitle(is->subtitle_st->codec,
&sp->sub, &got_subtitle,
pkt->data, pkt->size);
// if (len1 < 0)
// break;
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, sp->sub.rects[i].rgba_palette + 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(sp->sub.rects[i].rgba_palette + 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);
// if (cur_stream)
// stream_pause(cur_stream);
}
the_end:
return 0;
}
/* copy samples for viewing in editor window */
static void update_sample_display(VideoState *is, short *samples, int samples_size)
{
int size, len, channels;
Michael Niedermayer
committed
channels = is->audio_st->codec->channels;
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;
Michael Niedermayer
committed
n = 2 * is->audio_st->codec->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) {
Michael Niedermayer
committed
wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * 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 > max_size)
wanted_size = max_size;
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;
int nb;
Fabrice Bellard
committed
/* add samples */
nb = (samples_size - wanted_size);
samples_end = (uint8_t *)samples + samples_size - n;
q = samples_end + n;
while (nb > 0) {
memcpy(q, samples_end, n);
q += n;
nb -= n;
}
samples_size = wanted_size;
}
}
#if 0
printf("diff=%f adiff=%f sample_diff=%d apts=%0.3f vpts=%0.3f %f\n",
diff, avg_diff, samples_size - samples_size1,
Fabrice Bellard
committed
is->audio_clock, is->video_clock, is->audio_diff_threshold);
#endif
Fabrice Bellard
committed
} else {
/* too big difference : may be initial PTS errors, so
reset A-V filter */
is->audio_diff_avg_count = 0;
is->audio_diff_cum = 0;
}
}
return samples_size;
}
/* decode one audio frame and returns its uncompressed size */
Limin Wang
committed
static int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr)
/* NOTE: the audio packet can contain several frames */
Limin Wang
committed
data_size = buf_size;
len1 = avcodec_decode_audio2(is->audio_st->codec,
(int16_t *)audio_buf, &data_size,
if (len1 < 0) {
/* if error, we skip the frame */
is->audio_pkt_size = 0;
is->audio_pkt_data += len1;
is->audio_pkt_size -= len1;
if (data_size <= 0)
continue;
/* if no pts, then compute it */
pts = is->audio_clock;
*pts_ptr = pts;
Michael Niedermayer
committed
n = 2 * is->audio_st->codec->channels;
is->audio_clock += (double)data_size /
Michael Niedermayer
committed
(double)(n * is->audio_st->codec->sample_rate);
Fabrice Bellard
committed
#if defined(DEBUG_SYNC)
{
static double last_clock;
printf("audio: delay=%0.3f clock=%0.3f pts=%0.3f\n",
is->audio_clock - last_clock,
is->audio_clock, pts);
last_clock = is->audio_clock;
/* free the current packet */
if (pkt->data)
if (is->paused || is->audioq.abort_request) {
return -1;
}
/* read next packet */
if (packet_queue_get(&is->audioq, pkt, 1) < 0)
return -1;
if(pkt->data == flush_pkt.data){
avcodec_flush_buffers(is->audio_st->codec);
continue;
}
is->audio_pkt_data = pkt->data;
is->audio_pkt_size = pkt->size;
/* if update the audio clock with the pts */
if (pkt->pts != AV_NOPTS_VALUE) {
is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
Fabrice Bellard
committed
/* get the current audio output buffer size, in samples. With SDL, we
cannot have a precise information */
static int audio_write_get_buf_size(VideoState *is)
return is->audio_buf_size - is->audio_buf_index;
}
/* prepare a new audio buffer */
void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
{
VideoState *is = opaque;
int audio_size, len1;
double pts;
audio_callback_time = av_gettime();
while (len > 0) {
if (is->audio_buf_index >= is->audio_buf_size) {
Limin Wang
committed
audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts);
if (audio_size < 0) {
/* if error, just output silence */
is->audio_buf_size = 1024;
memset(is->audio_buf, 0, is->audio_buf_size);
} else {
if (is->show_audio)
update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, audio_size,
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
pts);
is->audio_buf_size = audio_size;
}
is->audio_buf_index = 0;
}
len1 = is->audio_buf_size - is->audio_buf_index;
if (len1 > len)
len1 = len;
memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
len -= len1;
stream += len1;
is->audio_buf_index += len1;
}
}
/* open a given stream. Return 0 if OK */
static int stream_component_open(VideoState *is, int stream_index)
{
AVFormatContext *ic = is->ic;
AVCodecContext *enc;
AVCodec *codec;
SDL_AudioSpec wanted_spec, spec;
if (stream_index < 0 || stream_index >= ic->nb_streams)
return -1;
Michael Niedermayer
committed
enc = ic->streams[stream_index]->codec;
/* prepare audio output */
if (enc->codec_type == CODEC_TYPE_AUDIO) {
wanted_spec.freq = enc->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
Fabrice Bellard
committed
/* hack for AC3. XXX: suppress that */
if (enc->channels > 2)
enc->channels = 2;
wanted_spec.channels = enc->channels;
wanted_spec.silence = 0;
Fabrice Bellard
committed
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = sdl_audio_callback;
wanted_spec.userdata = is;
Fabrice Bellard
committed
if (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
Fabrice Bellard
committed
}
is->audio_hw_buf_size = spec.size;
}
codec = avcodec_find_decoder(enc->codec_id);
enc->debug_mv = debug_mv;
enc->debug = debug;
if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;
if(fast) enc->flags2 |= CODEC_FLAG2_FAST;
enc->skip_frame= skip_frame;
enc->skip_idct= skip_idct;
enc->skip_loop_filter= skip_loop_filter;
Michael Niedermayer
committed
enc->error_resilience= error_resilience;
enc->error_concealment= error_concealment;
if (!codec ||
avcodec_open(enc, codec) < 0)
return -1;
if(thread_count>1)
avcodec_thread_init(enc, thread_count);
enc->thread_count= thread_count;
Fabrice Bellard
committed
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
is->audio_stream = stream_index;
is->audio_st = ic->streams[stream_index];
is->audio_buf_size = 0;
is->audio_buf_index = 0;
Fabrice Bellard
committed
/* init averaging filter */
is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
is->audio_diff_avg_count = 0;
/* since we do not have a precise anough audio fifo fullness,
we correct audio sync only if larger than this threshold */
is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / enc->sample_rate;
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
packet_queue_init(&is->audioq);
break;
case CODEC_TYPE_VIDEO:
is->video_stream = stream_index;
is->video_st = ic->streams[stream_index];
Fabrice Bellard
committed
is->frame_last_delay = 40e-3;
is->frame_timer = (double)av_gettime() / 1000000.0;
is->video_current_pts_time = av_gettime();
packet_queue_init(&is->videoq);
is->video_tid = SDL_CreateThread(video_thread, is);
Michael Niedermayer
committed
enc-> get_buffer= my_get_buffer;
enc->release_buffer= my_release_buffer;
case CODEC_TYPE_SUBTITLE:
is->subtitle_stream = stream_index;
is->subtitle_st = ic->streams[stream_index];
packet_queue_init(&is->subtitleq);
is->subtitle_tid = SDL_CreateThread(subtitle_thread, is);
break;
default:
break;
}
return 0;
}
static void stream_component_close(VideoState *is, int stream_index)
{
AVFormatContext *ic = is->ic;
AVCodecContext *enc;
if (stream_index < 0 || stream_index >= ic->nb_streams)
return;
Michael Niedermayer
committed
enc = ic->streams[stream_index]->codec;
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
packet_queue_abort(&is->audioq);
SDL_CloseAudio();
packet_queue_end(&is->audioq);
break;
case CODEC_TYPE_VIDEO:
packet_queue_abort(&is->videoq);
/* note: we also signal this mutex to make sure we deblock the
video thread in all cases */
SDL_LockMutex(is->pictq_mutex);
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
SDL_WaitThread(is->video_tid, NULL);
packet_queue_end(&is->videoq);
break;
case CODEC_TYPE_SUBTITLE:
packet_queue_abort(&is->subtitleq);
/* note: we also signal this mutex to make sure we deblock the
video thread in all cases */
SDL_LockMutex(is->subpq_mutex);
is->subtitle_stream_changed = 1;
SDL_CondSignal(is->subpq_cond);
SDL_UnlockMutex(is->subpq_mutex);
SDL_WaitThread(is->subtitle_tid, NULL);
packet_queue_end(&is->subtitleq);
break;
default:
break;
}
avcodec_close(enc);
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
is->audio_st = NULL;
is->audio_stream = -1;
break;
case CODEC_TYPE_VIDEO:
is->video_st = NULL;
is->video_stream = -1;
break;
case CODEC_TYPE_SUBTITLE:
is->subtitle_st = NULL;
is->subtitle_stream = -1;
break;
Stefan Huehner
committed
static void dump_stream_info(const AVFormatContext *s)
{
if (s->track != 0)
fprintf(stderr, "Track: %d\n", s->track);
if (s->title[0] != '\0')
fprintf(stderr, "Title: %s\n", s->title);
if (s->author[0] != '\0')
fprintf(stderr, "Author: %s\n", s->author);
if (s->copyright[0] != '\0')
fprintf(stderr, "Copyright: %s\n", s->copyright);
if (s->comment[0] != '\0')
fprintf(stderr, "Comment: %s\n", s->comment);
if (s->album[0] != '\0')
fprintf(stderr, "Album: %s\n", s->album);
if (s->year != 0)
fprintf(stderr, "Year: %d\n", s->year);
if (s->genre[0] != '\0')
fprintf(stderr, "Genre: %s\n", s->genre);
}
Fabrice Bellard
committed
/* since we have only one decoding thread, we can use a global
variable instead of a thread local variable */
static VideoState *global_video_state;
static int decode_interrupt_cb(void)
{
return (global_video_state && global_video_state->abort_request);
}
/* this thread gets the stream from the disk or the network */
static int decode_thread(void *arg)
{
VideoState *is = arg;
AVFormatContext *ic;
int err, i, ret, video_index, audio_index, use_play;
Fabrice Bellard
committed
AVFormatParameters params, *ap = ¶ms;
video_index = -1;
audio_index = -1;
is->video_stream = -1;
is->audio_stream = -1;
is->subtitle_stream = -1;
Fabrice Bellard
committed
global_video_state = is;
url_set_interrupt_cb(decode_interrupt_cb);
Fabrice Bellard
committed
memset(ap, 0, sizeof(*ap));
ap->initial_pause = 1; /* we force a pause when starting an RTSP
stream */
Limin Wang
committed
ap->width = frame_width;
ap->height= frame_height;
Limin Wang
committed
ap->pix_fmt = frame_pix_fmt;
Fabrice Bellard
committed
err = av_open_input_file(&ic, is->filename, is->iformat, 0, ap);
Fabrice Bellard
committed
if (err < 0) {
print_error(is->filename, err);
ret = -1;
goto fail;
}
#ifdef CONFIG_RTSP_DEMUXER
use_play = (ic->iformat == &rtsp_demuxer);
if(genpts)
ic->flags |= AVFMT_FLAG_GENPTS;
if (!use_play) {
err = av_find_stream_info(ic);
if (err < 0) {
fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
ret = -1;
goto fail;
}
ic->pb.eof_reached= 0; //FIXME hack, ffplay maybe should not use url_feof() to test for the end
Fabrice Bellard
committed
}
/* if seeking requested, we execute it */
if (start_time != AV_NOPTS_VALUE) {
int64_t timestamp;
timestamp = start_time;
/* add the stream start time */
if (ic->start_time != AV_NOPTS_VALUE)
timestamp += ic->start_time;
ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
fprintf(stderr, "%s: could not seek to position %0.3f\n",
is->filename, (double)timestamp / AV_TIME_BASE);
}
}
/* now we can begin to play (RTSP stream only) */
av_read_play(ic);
if (use_play) {
err = av_find_stream_info(ic);
if (err < 0) {
fprintf(stderr, "%s: could not find codec parameters\n", is->filename);
ret = -1;
goto fail;
}
}
Michael Niedermayer
committed
AVCodecContext *enc = ic->streams[i]->codec;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
if ((audio_index < 0 || wanted_audio_stream-- > 0) && !audio_disable)
audio_index = i;
break;
case CODEC_TYPE_VIDEO:
if ((video_index < 0 || wanted_video_stream-- > 0) && !video_disable)
video_index = i;
break;
default:
break;
}
}
if (show_status) {
dump_format(ic, 0, is->filename, 0);
}
/* open the streams */
if (audio_index >= 0) {
stream_component_open(is, audio_index);
}
if (video_index >= 0) {
stream_component_open(is, video_index);
} else {
if (!display_disable)
is->show_audio = 1;
}
if (is->video_stream < 0 && is->audio_stream < 0) {
Fabrice Bellard
committed
fprintf(stderr, "%s: could not open codecs\n", is->filename);
ret = -1;
goto fail;
}
for(;;) {
if (is->abort_request)
break;
Fabrice Bellard
committed
if (is->paused != is->last_paused) {
is->last_paused = is->paused;
if (is->paused)
av_read_pause(ic);
else
av_read_play(ic);
Fabrice Bellard
committed
}
if (is->paused && ic->iformat == &rtsp_demuxer) {
Fabrice Bellard
committed
/* wait 10 ms to avoid trying to get another packet */
/* XXX: horrible */
SDL_Delay(10);
continue;
}
Michael Niedermayer
committed
#endif
Michael Niedermayer
committed
int stream_index= -1;