📄 ffplay.c
字号:
int wanted_size, min_size, max_size, nb_samples; ref_clock = get_master_clock(is); diff = get_audio_clock(is) - ref_clock; 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_st->codec.sample_rate) * n); nb_samples = samples_size / n; 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; /* 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; /* 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, is->audio_clock, is->video_clock, is->audio_diff_threshold);#endif } } 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 */static int audio_decode_frame(VideoState *is, uint8_t *audio_buf, double *pts_ptr){ AVPacket *pkt = &is->audio_pkt; int n, len1, data_size; double pts; for(;;) { /* NOTE: the audio packet can contain several frames */ while (is->audio_pkt_size > 0) { len1 = avcodec_decode_audio(&is->audio_st->codec, (int16_t *)audio_buf, &data_size, is->audio_pkt_data, is->audio_pkt_size); if (len1 < 0) { /* if error, we skip the frame */ is->audio_pkt_size = 0; break; } 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; n = 2 * is->audio_st->codec.channels; is->audio_clock += (double)data_size / (double)(n * is->audio_st->codec.sample_rate);#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; }#endif return data_size; } /* free the current packet */ if (pkt->data) av_free_packet(pkt); if (is->paused || is->audioq.abort_request) { return -1; } /* read next packet */ if (packet_queue_get(&is->audioq, pkt, 1) < 0) return -1; 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 = (double)pkt->pts / AV_TIME_BASE; } }}/* 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_hw_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) { audio_size = audio_decode_frame(is, 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, 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; 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; /* hack for AC3. XXX: suppress that */ if (enc->channels > 2) enc->channels = 2; wanted_spec.channels = enc->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = sdl_audio_callback; wanted_spec.userdata = is; if (SDL_OpenAudio(&wanted_spec, &spec) < 0) { fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); return -1; } is->audio_hw_buf_size = spec.size; } codec = avcodec_find_decoder(enc->codec_id); enc->debug_mv = debug_mv; enc->debug = debug; enc->workaround_bugs = workaround_bugs; if (!codec || avcodec_open(enc, codec) < 0) return -1;#if defined(HAVE_PTHREADS) || defined(HAVE_W32THREADS) if(thread_count>1) avcodec_thread_init(enc, thread_count);#endif enc->thread_count= thread_count; 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; /* 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); SDL_PauseAudio(0); break; case CODEC_TYPE_VIDEO: is->video_stream = stream_index; is->video_st = ic->streams[stream_index]; 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); break; default: break; } return 0;}static void stream_component_close(VideoState *is, int stream_index){ AVFormatContext *ic = is->ic; AVCodecContext *enc; enc = &ic->streams[stream_index]->codec; 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; 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; default: break; }}void dump_stream_info(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->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);}/* 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; AVPacket pkt1, *pkt = &pkt1; AVFormatParameters params, *ap = ¶ms; video_index = -1; audio_index = -1; is->video_stream = -1; is->audio_stream = -1; global_video_state = is; url_set_interrupt_cb(decode_interrupt_cb); memset(ap, 0, sizeof(*ap)); ap->image_format = image_format; ap->initial_pause = 1; /* we force a pause when starting an RTSP stream */ err = av_open_input_file(&ic, is->filename, is->iformat, 0, ap); if (err < 0) { print_error(is->filename, err); ret = -1; goto fail; } is->ic = ic;#ifdef CONFIG_NETWORK use_play = (ic->iformat == &rtsp_demux);#else use_play = 0;#endif 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; } } /* 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); if (ret < 0) { 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; } } for(i = 0; i < ic->nb_streams; i++) { AVCodecContext *enc = &ic->streams[i]->codec; switch(enc->codec_type) { case CODEC_TYPE_AUDIO: if (audio_index < 0 && !audio_disable) audio_index = i; break; case CODEC_TYPE_VIDEO: if (video_index < 0 && !video_disable) video_index = i; break; default: break; } } if (show_status) { dump_format(ic, 0, is->filename, 0); dump_stream_info(ic); } /* 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) { fprintf(stderr, "%s: could not open codecs\n", is->filename); ret = -1; goto fail; } for(;;) { if (is->abort_request) break;#ifdef CONFIG_NETWORK if (is->paused != is->last_paused) { is->last_paused = is->paused; if (is->paused) av_read_pause(ic); else av_read_play(ic); } if (is->paused && ic->iformat == &rtsp_demux) { /* wait 10 ms to avoid trying to get another packet */ /* XXX: horrible */ SDL_Delay(10); continue; }#endif if (is->seek_req) { /* XXX: must lock decoder threads */ ret = av_seek_frame(is->ic, -1, is->seek_pos); if (ret < 0) { fprintf(stderr, "%s: error while seeking\n", is->ic->filename); }else{ if (is->audio_stream >= 0) { packet_queue_flush(&is->audioq); } if (is->video_stream >= 0) { packet_queue_flush(&is->videoq); avcodec_flush_buffers(&ic->streams[video_index]->codec); } } is->seek_req = 0; } /* if the queue are full, no need to read more */ if (is->audioq.size > MAX_AUDIOQ_SIZE || is->videoq.size > MAX_VIDEOQ_SIZE || url_feof(&ic->pb)) { /* wait 10 ms */ SDL_Delay(10); continue; } ret = av_read_frame(ic, pkt); if (ret < 0) { break; } if (pkt->stream_index == is->audio_stream) { packet_queue_put(&is->audioq, pkt); } else if (pkt->stream_index == is->video_stream) { packet_queue_put(&is->videoq, pkt); } else { av_free_packet(pkt); } } /* wait until the end */ while (!is->abort_request) { SDL_Delay(100); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -