static void stream_close(VideoState *is)
{
/* XXX: use a special url_shutdown call to abort parse cleanly */
is->abort_request = 1;
SDL_WaitThread(is->read_tid, NULL);
/* close each stream */
if (is->audio_stream >= 0)
stream_component_close(is, is->audio_stream);
if (is->video_stream >= 0)
stream_component_close(is, is->video_stream);
if (is->subtitle_stream >= 0)
stream_component_close(is, is->subtitle_stream);
avformat_close_input(&is->ic);
packet_queue_destroy(&is->videoq);
packet_queue_destroy(&is->audioq);
packet_queue_destroy(&is->subtitleq);
/* free all pictures */
frame_queue_destory(&is->pictq);
frame_queue_destory(&is->sampq);
frame_queue_destory(&is->subpq);
SDL_DestroyCond(is->continue_read_thread);
sws_freeContext(is->img_convert_ctx);
sws_freeContext(is->sub_convert_ctx);
av_free(is->filename);
if (is->vis_texture)
SDL_DestroyTexture(is->vis_texture);
if (is->vid_texture)
SDL_DestroyTexture(is->vid_texture);
if (is->sub_texture)
SDL_DestroyTexture(is->sub_texture);
av_free(is);
}
static void do_exit(VideoState *is)
{
if (is) {
stream_close(is);
}
if (renderer)
SDL_DestroyRenderer(renderer);
if (window)
SDL_DestroyWindow(window);
//uninit_opts();
#if CONFIG_AVFILTER
av_freep(&vfilters_list);
#endif
avformat_network_deinit();
if (show_status)
printf("\n");
SDL_Quit();
av_log(NULL, AV_LOG_QUIET, "%s", "");
exit(0);
}
static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
{
int i;
memset(f, 0, sizeof(FrameQueue));
// 创建互斥量
if (!(f->mutex = SDL_CreateMutex())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
// 创建条件变量
if (!(f->cond = SDL_CreateCond())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
// 使pktq指向相应的ES流队列
f->pktq = pktq;
f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
f->keep_last = !!keep_last;//!!只要是非零的值都变成1
// 为帧队列中的各个Frame创建AvFrame
for (i = 0; i < f->max_size; i++)
if (!(f->queue[i].frame = av_frame_alloc()))
return AVERROR(ENOMEM);
return 0;
}
/* packet queue handling */
static int packet_queue_init(PacketQueue *q)
{
memset(q, 0, sizeof(PacketQueue));
q->mutex = SDL_CreateMutex();
if (!q->mutex) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
q->cond = SDL_CreateCond();
if (!q->cond) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
q->abort_request = 1;
return 0;
}
/** 获取当前时钟 ,返回当前播放到的位置,单位秒**/
static double get_clock(Clock *c)
{
if (*c->queue_serial != c->serial)
return NAN;
if (c->paused) {
return c->pts;
}
else {
//c->pts_drift = c->pts - c->last_updated;
//默认的情况下,根据上一次的drift计算下一次要出现的时间。
double time = av_gettime_relative() / 1000000.0;
//av_log(NULL, AV_LOG_INFO, "speed %f last_updated %f pts_drift %f currentTime %f clockType %d time1 %f\n",
// c->speed,
// c->last_updated,
// c->pts_drift,
// c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed),
// c->clockType,
// (time - c->last_updated) * (1.0 - c->speed));//此值计算出来基本上都是0
//c->pts_drift + time即是time - c->last_updated + c->pts,time - c->last_updated是当前时间和上次更新时间的差值,
//c->pts是上次更新时播放的时长,所以加起来就是当前播放的时长。
//因为以音频同步时c->speed一直是1,所以后面(time - c->last_updated) * (1.0 - c->speed)始终是0
return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
}
}
/** 设置时钟 **/
static void set_clock_at(Clock *c, double pts, int serial, double time)
{
c->pts = pts;
c->last_updated = time;
c->pts_drift = c->pts - time;
c->serial = serial;
/*if (c->clockType == 1)
{
av_log(NULL, AV_LOG_INFO, "pts %f last_updated %f pts_drift %f clockType %d serial %f\n",
c->pts,
c->last_updated,
c->pts_drift,
c->clockType,
c->serial);
}*/
}
/** 设置时钟 内部调用set_clock_at()**/
static void set_clock(Clock *c, double pts, int serial)
{
double time = av_gettime_relative() / 1000000.0;
set_clock_at(c, pts, serial, time);
}
/** 设置时钟速度 ,已音频时钟同步用不到**/
static void set_clock_speed(Clock *c, double speed)
{
set_clock(c, get_clock(c), c->serial);
c->speed = speed;
}
/** 初始化时钟 **/
static void init_clock(Clock *c, int *queue_serial)
{
c->speed = 1.0;
c->paused = 0;
c->queue_serial = queue_serial;
set_clock(c, NAN, -1);
}
//读取文件的中断回调,一旦出现中断,返回abort_request
static int decode_interrupt_cb(void *ctx)
{
VideoState *is = (VideoState *)ctx;
return is->abort_request;
}
static int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
{
int ret = avformat_match_stream_specifier(s, st, spec);
if (ret < 0)
av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
return ret;
}
static AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, AVCodec *codec)
{
AVDictionary *ret = NULL;
AVDictionaryEntry *t = NULL;
int flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM
: AV_OPT_FLAG_DECODING_PARAM;
char prefix = 0;
const AVClass *cc = avcodec_get_class();
if (!codec)
codec = s->oformat ? avcodec_find_encoder(codec_id)
: avcodec_find_decoder(codec_id);
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_VIDEO:
prefix = 'v';
flags |= AV_OPT_FLAG_VIDEO_PARAM;
break;
case AVMEDIA_TYPE_AUDIO:
prefix = 'a';
flags |= AV_OPT_FLAG_AUDIO_PARAM;
break;
case AVMEDIA_TYPE_SUBTITLE:
prefix = 's';
flags |= AV_OPT_FLAG_SUBTITLE_PARAM;
break;
}
while (t = av_dict_get(opts, "", t, AV_DICT_IGNORE_SUFFIX)) {
char *p = strchr(t->key, ':');
/* check stream specification in opt name */
if (p)
switch (check_stream_specifier(s, st, p + 1)) {
case 1: *p = 0; break;
case 0: continue;
default: exit(1);
}
if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) ||
!codec ||
(codec->priv_class &&
av_opt_find(&codec->priv_class, t->key, NULL, flags,
AV_OPT_SEARCH_FAKE_OBJ)))
av_dict_set(&ret, t->key, t->value, 0);
else if (t->key[0] == prefix &&
av_opt_find(&cc, t->key + 1, NULL, flags,
AV_OPT_SEARCH_FAKE_OBJ))
av_dict_set(&ret, t->key + 1, t->value, 0);
if (p)
*p = ':';
}
return ret;
}
static void print_error(const char *filename, int err)
{
char errbuf[128];
const char *errbuf_ptr = errbuf;
if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
errbuf_ptr = strerror(AVUNERROR(err));
av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, errbuf_ptr);
}
static AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
AVDictionary *codec_opts)
{
int i;
AVDictionary **opts;
if (!s->nb_streams)
return NULL;
opts = (AVDictionary **)av_mallocz_array(s->nb_streams, sizeof(*opts));
if (!opts) {
av_log(NULL, AV_LOG_ERROR,
"Could not alloc memory for stream options.\n");
return NULL;
}
for (i = 0; i < s->nb_streams; i++)
opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
s, s->streams[i], NULL);
return opts;
}
static int is_realtime(AVFormatContext *s)
{
if (!strcmp(s->iformat->name, "rtp")
|| !strcmp(s->iformat->name, "rtsp")
|| !strcmp(s->iformat->name, "sdp")
)
return 1;
if (s->pb && (!strncmp(s->url, "rtp:", 4)
|| !strncmp(s->url, "udp:", 4)
)
)
return 1;
return 0;
}
static void calculate_display_rect(SDL_Rect *rect,
int scr_xleft, //强制渲染图像和窗口的x偏移量,设为0
int scr_ytop,//强制渲染图像和窗口的y偏移量,设为0
int scr_width, //窗口的宽度
int scr_height,//窗口的高度
int pic_width,//流中的宽度
int pic_height, //流中的高度
AVRational pic_sar)//用来计算真实渲染图像的长宽比,默认1/1
{
AVRational aspect_ratio = pic_sar;
int64_t width, height, x, y;
if (av_cmp_q(aspect_ratio, av_make_q(0, 1)) <= 0)
aspect_ratio = av_make_q(1, 1);
aspect_ratio = av_mul_q(aspect_ratio, av_make_q(pic_width, pic_height));
/* XXX: we suppose the screen has a 1.0 pixel ratio */
height = scr_height;
width = av_rescale(height, aspect_ratio.num, aspect_ratio.den) & ~1;
if (width > scr_width) {
width = scr_width;
height = av_rescale(width, aspect_ratio.den, aspect_ratio.num) & ~1;
}
x = (scr_width - width) / 2; //计算出渲染图像在窗口中的x起点
y = (scr_height - height) / 2; //计算出渲染图像在窗口中的y起点
rect->x = scr_xleft + x;
rect->y = scr_ytop + y;
rect->w = FFMAX((int)width, 1);//渲染图像的宽度
rect->h = FFMAX((int)height, 1);//渲染图像的高度
}
static void set_default_window_size(int width, int height, AVRational sar)
{
SDL_Rect rect;
int max_width = screen_width ? screen_width : INT_MAX;
int max_height = screen_height ? screen_height : INT_MAX;
if (max_width == INT_MAX && max_height == INT_MAX)
max_height = height;
calculate_display_rect(&rect, 0, 0, max_width, max_height, width, height, sar);//计算出渲染图像的宽度和高度,在窗口中的起始点
default_width = rect.w;
default_height = rect.h;
}
static inline
int64_t get_valid_channel_layout(int64_t channel_layout, int channels)
{
if (channel_layout && av_get_channel_layout_nb_channels(channel_layout) == channels)
return channel_layout;
else
return 0;
}
double get_rotation(AVStream *st)
{
uint8_t* displaymatrix = av_stream_get_side_data(st,
AV_PKT_DATA_DISPLAYMATRIX, NULL);
double theta = 0;
if (displaymatrix)
theta = -av_display_rotation_get((int32_t*)displaymatrix);
theta -= 360 * floor(theta / 360 + 0.9 / 360);
if (fabs(theta - 90 * round(theta / 90)) > 2)
av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n"
"If you want to help, upload a sample "
"of this file to ftp://upload.ffmpeg.org/incoming/ "
"and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");
return theta;
}
#if CONFIG_AVFILTER
static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph,
AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
{
int ret, i;
int nb_filters = graph->nb_filters;
AVFilterInOut *outputs = NULL, *inputs = NULL;
if (filtergraph) {
outputs = avfilter_inout_alloc();
inputs = avfilter_inout_alloc();
if (!outputs || !inputs) {
ret = AVERROR(ENOMEM);
goto fail;
}
outputs->name = av_strdup("in");
outputs->filter_ctx = source_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = sink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, NULL)) < 0)
goto fail;
}
else {
if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
goto fail;
}
/* Reorder the filters to ensure that inputs of the custom filters are merged first */
for (i = 0; i < graph->nb_filters - nb_filters; i++)
FFSWAP(AVFilterContext*, graph->filters[i], graph->filters[i + nb_filters]);
ret = avfilter_graph_config(graph, NULL);
fail:
avfilter_inout_free(&outputs);
avfilter_inout_free(&inputs);
return ret;
}
static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters, AVFrame *frame)
{
enum AVPixelFormat pix_fmts[FF_ARRAY_ELEMS(sdl_texture_format_map)];
char sws_flags_str[512] = "";
char buffersrc_args[256];
int ret;
AVFilterContext *filt_src = NULL, *filt_out = NULL, *last_filter = NULL;
AVCodecParameters *codecpar = is->video_st->codecpar;
AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL);
AVDictionaryEntry *e = NULL;
int nb_pix_fmts = 0;
int i, j;
for (i = 0; i < renderer_info.num_texture_formats; i++) {
for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map) - 1; j++) {
if (renderer_info.texture_formats[i] == sdl_texture_format_map[j].texture_fmt) {
pix_fmts[nb_pix_fmts++] = sdl_texture_format_map[j].format;
break;
}
}
}
pix_fmts[nb_pix_fmts] = AV_PIX_FMT_NONE;
while ((e = av_dict_get(sws_dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
if (!strcmp(e->key, "sws_flags")) {
av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", "flags", e->value);
}
else
av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", e->key, e->value);
}
if (strlen(sws_flags_str))
sws_flags_str[strlen(sws_flags_str) - 1] = '\0';
graph->scale_sws_opts = av_strdup(sws_flags_str);
snprintf(buffersrc_args, sizeof(buffersrc_args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
frame->width, frame->height, frame->format,
is->video_st->time_base.num, is->video_st->time_base.den,
codecpar->sample_aspect_ratio.num, FFMAX(codecpar->sample_aspect_ratio.den, 1));
if (fr.num && fr.den)
av_strlcatf(buffersrc_args, sizeof(buffersrc_args), ":frame_rate=%d/%d", fr.num, fr.den);
if ((ret = avfilter_graph_create_filter(&filt_src,
avfilter_get_by_name("buffer"),
"ffplay_buffer", buffersrc_args, NULL,
graph)) < 0)
goto fail;
ret = avfilter_graph_create_filter(&filt_out,
avfilter_get_by_name("buffersink"),
"ffplay_buffersink", NULL, NULL, graph);
if (ret < 0)
goto fail;
if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
goto fail;
last_filter = filt_out;
/* Note: this macro adds a filter before the lastly added filter, so the
* processing order of the filters is in reverse */
#define INSERT_FILT(name, arg) do { \
AVFilterContext *filt_ctx; \
\
ret = avfilter_graph_create_filter(&filt_ctx, \
avfilter_get_by_name(name), \
"ffplay_" name, arg, NULL, graph); \
if (ret < 0) \
goto fail; \
\
ret = avfilter_link(filt_ctx, 0, last_filter, 0); \
if (ret < 0) \
goto fail; \
\
last_filter = filt_ctx; \
} while (0)
if (autorotate) {
double theta = get_rotation(is->video_st);
if (fabs(theta - 90) < 1.0) {
INSERT_FILT("transpose", "clock");
}
else if (fabs(theta - 180) < 1.0) {
INSERT_FILT("hflip", NULL);
INSERT_FILT("vflip", NULL);
}
else if (fabs(theta - 270) < 1.0) {
INSERT_FILT("transpose", "cclock");
}
else if (fabs(theta) > 1.0) {
char rotate_buf[64];
snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
INSERT_FILT("rotate", rotate_buf);
}
}
if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
goto fail;
is->in_video_filter = filt_src;
is->out_video_filter = filt_out;
fail:
return ret;
}
static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format)
{
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
int sample_rates[2] = { 0, -1 };
int64_t channel_layouts[2] = { 0, -1 };
int channels[2] = { 0, -1 };
AVFilterContext *filt_asrc = NULL, *filt_asink = NULL;
char aresample_swr_opts[512] = "";
AVDictionaryEntry *e = NULL;
char asrc_args[256];
int ret;
avfilter_graph_free(&is->agraph);
if (!(is->agraph = avfilter_graph_alloc()))
return AVERROR(ENOMEM);
while ((e = av_dict_get(swr_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
av_strlcatf(aresample_swr_opts, sizeof(aresample_swr_opts), "%s=%s:", e->key, e->value);
if (strlen(aresample_swr_opts))
aresample_swr_opts[strlen(aresample_swr_opts) - 1] = '\0';
av_opt_set(is->agraph, "aresample_swr_opts", aresample_swr_opts, 0);
ret = snprintf(asrc_args, sizeof(asrc_args),
"sample_rate=%d:sample_fmt=%s:channels=%d:time_base=%d/%d",
is->audio_filter_src.freq, av_get_sample_fmt_name(is->audio_filter_src.fmt),
is->audio_filter_src.channels,
1, is->audio_filter_src.freq);
if (is->audio_filter_src.channel_layout)
snprintf(asrc_args + ret, sizeof(asrc_args) - ret,
":channel_layout=0x%" PRIx64, is->audio_filter_src.channel_layout);
ret = avfilter_graph_create_filter(&filt_asrc,
avfilter_get_by_name("abuffer"), "ffplay_abuffer",
asrc_args, NULL, is->agraph);
if (ret < 0)
goto end;
ret = avfilter_graph_create_filter(&filt_asink,
avfilter_get_by_name("abuffersink"), "ffplay_abuffersink",
NULL, NULL, is->agraph);
if (ret < 0)
goto end;
if ((ret = av_opt_set_int_list(filt_asink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
if (force_output_format) {
channel_layouts[0] = is->audio_tgt.channel_layout;
channels[0] = is->audio_tgt.channels;
sample_rates[0] = is->audio_tgt.freq;
if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 0, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
if ((ret = av_opt_set_int_list(filt_asink, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
if ((ret = av_opt_set_int_list(filt_asink, "channel_counts", channels, -1, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
if ((ret = av_opt_set_int_list(filt_asink, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN)) < 0)
goto end;
}
if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc, filt_asink)) < 0)
goto end;
is->in_audio_filter = filt_asrc;
is->out_audio_filter = filt_asink;
end:
if (ret < 0)
avfilter_graph_free(&is->agraph);
return ret;
}
#endif
/* return the number of undisplayed frames in the queue */
//返回队列中待显示帧的数目
static int frame_queue_nb_remaining(FrameQueue *f)
{
return f->size - f->rindex_shown;
}
//这方法和frame_queue_peek的作用一样, 都是获取待显示的第一帧
static Frame *frame_queue_peek_readable(FrameQueue *f)
{
/* wait until we have a readable a new frame */
SDL_LockMutex(f->mutex);
while (f->size - f->rindex_shown <= 0 &&
!f->pktq->abort_request) {
SDL_CondWait(f->cond, f->mutex);
}
SDL_UnlockMutex(f->mutex);
if (f->pktq->abort_request)
return NULL;
return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
}
//将读索引(队头)后移一位, 还有将这个队列中的Frame的数量减一
static void frame_queue_next(FrameQueue *f)
{
if (f->keep_last && !f->rindex_shown) {
f->rindex_shown = 1;
return;
}
frame_queue_unref_item(&f->queue[f->rindex]);
if (++f->rindex == f->max_size)
f->rindex = 0;
SDL_LockMutex(f->mutex);
f->size--;
SDL_CondSignal(f->cond);
SDL_UnlockMutex(f->mutex);
}
/** 获取做为基准的类型 音频 外部时钟 视频 **/
static int get_master_sync_type(VideoState *is) {
if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
if (is->video_st)
return AV_SYNC_VIDEO_MASTER;
else
return AV_SYNC_AUDIO_MASTER;
}
else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
if (is->audio_st)
return AV_SYNC_AUDIO_MASTER;
else
return AV_SYNC_EXTERNAL_CLOCK;
}
else {
return AV_SYNC_EXTERNAL_CLOCK;
}
}
/* get the current master clock value */
/** 获取主时间轴的时间(当前播放了多长时间),单位秒 **/
static double get_master_clock(VideoState *is)
{
double val;
switch (get_master_sync_type(is)) {
case AV_SYNC_VIDEO_MASTER:
val = get_clock(&is->vidclk);
break;
case AV_SYNC_AUDIO_MASTER:
val = get_clock(&is->audclk);
break;
default:
val = get_clock(&is->extclk);
break;
}
return val;
}
/* return the wanted number of samples to get better sync if sync_type is video
如果同步类型为视频或外部主时钟,则返回所需的采样数来更好的同步。
* or external master clock */
static int synchronize_audio(VideoState *is, int nb_samples)
{
int wanted_nb_samples = nb_samples;
/* if not master, then we try to remove or add samples to correct the clock */
if (get_master_sync_type(is) != AV_SYNC_AUDIO_MASTER) {
double diff, avg_diff;
int min_nb_samples, max_nb_samples;
//同样,先计算两个时间钟之间的diff
diff = get_clock(&is->audclk) - get_master_clock(is);
//两者的差距,在阀值的范围内,表示还能调整。AV_NOSYNC_THRESHOLD =10.0
if (!isnan(diff) && fabs(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 */
//累计的延迟还不够,继续累加。会累计20次的差值,来计算上面的平均数
is->audio_diff_avg_count++;
}
else {
/* estimate the A-V difference */
//进行修正。
//先计算通过累计的diff_cum平均进行估计
avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
//延迟的平均数,确实是大于diff
if (fabs(avg_diff) >= is->audio_diff_threshold) {
//diff*samplerate 可以计算补偿的样本数
wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq);
//最大和最小的当前的修正参数。SAMPLE_CORRECTION_PERCENT_MAX=10.
//min 90% max 110%
min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100));
max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100));
//为了避免音调过高的问题,只能在这个区间补偿
wanted_nb_samples = av_clip(wanted_nb_samples, min_nb_samples, max_nb_samples);
}
av_log(NULL, AV_LOG_TRACE, "diff=%f adiff=%f sample_diff=%d apts=%0.3f %f\n",
diff, avg_diff, wanted_nb_samples - nb_samples,
is->audio_clock, is->audio_diff_threshold);
}
}
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 wanted_nb_samples;
}
/**
* Decode one audio frame and return its uncompressed size.
*
* The processed audio frame is decoded, converted if required, and
* stored in is->audio_buf, with size in bytes given by the return
* value.
*/
static int audio_decode_frame(VideoState *is)
{
int data_size, resampled_data_size;
int64_t dec_channel_layout;
av_unused double audio_clock0;
int wanted_nb_samples;
Frame *af;
if (is->paused)
return -1;
do {
#if defined(_WIN32)
while (frame_queue_nb_remaining(&is->sampq) == 0) {
if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2)
return -1;
av_usleep(1000);
}
#endif
if (!(af = frame_queue_peek_readable(&is->sampq)))
return -1;
frame_queue_next(&is->sampq);
} while (af->serial != is->audioq.serial);//序列号不同,说明上一次的连续播放已经无效,开始了一次新的连续播放
data_size = av_samples_get_buffer_size(NULL, af->frame->channels,
af->frame->nb_samples,
(AVSampleFormat)af->frame->format, 1);
dec_channel_layout =
(af->frame->channel_layout && af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels);
//音频的同步,是通过控制frame的数量nb_samples,来进行同步的。
wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
if (af->frame->format != is->audio_src.fmt ||
dec_channel_layout != is->audio_src.channel_layout ||
af->frame->sample_rate != is->audio_src.freq ||
(wanted_nb_samples != af->frame->nb_samples && !is->swr_ctx)) {
swr_free(&is->swr_ctx);
is->swr_ctx = swr_alloc_set_opts(NULL,
is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq,
dec_channel_layout, (AVSampleFormat)af->frame->format, af->frame->sample_rate,
0, NULL);
if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
av_log(NULL, AV_LOG_ERROR,
"Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
af->frame->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->frame->format), af->frame->channels,
is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);
swr_free(&is->swr_ctx);
return -1;
}
is->audio_src.channel_layout = dec_channel_layout;
is->audio_src.channels = af->frame->channels;
is->audio_src.freq = af->frame->sample_rate;
is->audio_src.fmt = (AVSampleFormat)af->frame->format;
}
if (is->swr_ctx) {
const uint8_t **in = (const uint8_t **)af->frame->extended_data;
uint8_t **out = &is->audio_buf1;
int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256;
int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0);
int len2;
if (out_size < 0) {
av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
return -1;
}
if (wanted_nb_samples != af->frame->nb_samples) {
if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n");
return -1;
}
}
av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
if (!is->audio_buf1)
return AVERROR(ENOMEM);
len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);//重采样
if (len2 < 0) {
av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
return -1;
}
if (len2 == out_count) {
av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
if (swr_init(is->swr_ctx) < 0)
swr_free(&is->swr_ctx);
}
is->audio_buf = is->audio_buf1;
resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
}
else {
is->audio_buf = af->frame->data[0];
resampled_data_size = data_size;
}
audio_clock0 = is->audio_clock;
/* update the audio clock with the pts */
//因为上面结果调整,这里重新根据nb_samples计算一次
if (!isnan(af->pts))
is->audio_clock = af->pts + (double)af->frame->nb_samples / af->frame->sample_rate;
else
is->audio_clock = NAN;
is->audio_clock_serial = af->serial;
#ifdef DEBUG
{
static double last_clock;
printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
is->audio_clock - last_clock,
is->audio_clock, audio_clock0);
last_clock = is->audio_clock;
}
#endif
return resampled_data_size;
}
/* copy samples for viewing in editor window */
static void update_sample_display(VideoState *is, short *samples, int samples_size)
{
int size, len;
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;
}
}
/** 音/视频设置时钟的时候都回去跟外部时钟进行对比,防止丢帧或者丢包情况下时间差距比较大而进行的纠偏 **/
//同步从属时钟,如果c的时间离slave太远了,就把c设置为slave的时间
static void sync_clock_to_slave(Clock *c,
Clock *slave)
{
double clock = get_clock(c);
double slave_clock = get_clock(slave);
if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
set_clock(c, slave_clock, slave->serial);
}
/* prepare a new audio buffer */
//sdl_audio_callback进行SDL音频回调播放。
static void sdl_audio_callback(void *opaque,
Uint8 *stream,//解码后的数据放入此处
int len)//需要放入多少数据
{
VideoState *is = (VideoState *)opaque;
int audio_size, len1;
/*当前系统时间*/
audio_callback_time = av_gettime_relative();
/*len为SDL中audio buffer的大小,单位是字节,该大小是我们在打开音频设备时设置*/
while (len > 0) {//本次需要收集的数据还不够,当len为0时说明收集的数据已经够了
/*进行解码*/
if (is->audio_buf_index >= is->audio_buf_size) {//说明解码出的数据都已经被放入设备缓存,没有数据可用了,所以要继续解码
audio_size = audio_decode_frame(is);//进行音频数据解码
if (audio_size < 0) {
/* if error, just output silence */
is->audio_buf = NULL;
is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size;
}
else {
if (is->show_mode != SHOW_MODE_VIDEO)
update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
is->audio_buf_size = audio_size;//当前解码出的数据量
}
is->audio_buf_index = 0;//刚刚解码完,已经放入缓存的数据量肯定为0
}
len1 = is->audio_buf_size - is->audio_buf_index;//当前解码出的数据中,还剩多少
if (len1 > len)
{
len1 = len;//剩的数据太多,只需要发送len个数据就够了
}
if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
{
memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
//保存pcm数据
/*FILE* pFile = fopen("test.pcm", "ab");
if (pFile)
{
fwrite(is->audio_buf + is->audio_buf_index, 1, len1, pFile);
fclose(pFile);
}*/
}
else {
memset(stream, 0, len1);
if (!is->muted && is->audio_buf)
{
SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume);
//保存pcm数据
/*FILE* pFile = fopen("test.pcm", "ab");
if (pFile)
{
fwrite(is->audio_buf + is->audio_buf_index, 1, len1, pFile);
fclose(pFile);
}*/
}
}
len -= len1;//计算还要放入多少数据
stream += len1;//更新设备缓存的位置
is->audio_buf_index += len1;//更新已经放入设备缓存的数据量
}
is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;//解码出的音频数据中还没有放入
/* Let's assume the audio driver that is used by SDL has two periods. */
//is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec计算音频实际播放到的时间
//其中is->audio_clock是计算出的当前所有解码数据播放完,播放到的时间
//其中(double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec是放入缓存,但是还没来得及播放的数据的播放时间
//前者减去后者就是当前实际播放到的时间
if (!isnan(is->audio_clock)) {
set_clock_at(&is->audclk,
is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, //计算音频实际播放的时间
is->audio_clock_serial,
audio_callback_time / 1000000.0);//当前音频时钟开始更新的时间(开始把新数据包放入缓存),单位秒
//检查extclk的时间如果离audclk太远,设置为audclk的时间
sync_clock_to_slave(&is->extclk, &is->audclk);
}
}
//打开系统的音频设备,并设置回调函数播放音频数据
static int audio_open(void *opaque,
int64_t wanted_channel_layout,//希望的channel_layout
int wanted_nb_channels, //希望的channels
int wanted_sample_rate, //希望的码率
struct AudioParams *audio_hw_params)//打开音频设备成功后,的音频参数
{
SDL_AudioSpec wanted_spec, spec;
const char *env;
static const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };
static const int next_sample_rates[] = { 0, 44100, 48000, 96000, 192000 };
int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
env = SDL_getenv("SDL_AUDIO_CHANNELS");
if (env) {// 若环境变量有设置,优先从环境变量取得声道数和声道布局,一般env为空
wanted_nb_channels = atoi(env);
wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
}
if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);//由chanels获取chanel_layout
wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
}
wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);//由chanel_layout获取chanels
wanted_spec.channels = wanted_nb_channels;
wanted_spec.freq = wanted_sample_rate;
if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
return -1;
}
while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
next_sample_rate_idx--;
wanted_spec.format = AUDIO_S16SYS;//希望的样本数据格式
wanted_spec.silence = 0;
wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
wanted_spec.callback = sdl_audio_callback;//回调函数
wanted_spec.userdata = opaque;//回调函数被调用时,回传的参数
while (!(audio_dev = SDL_OpenAudioDevice(NULL,
0,
&wanted_spec,//想要的音频规格
&spec, //实际得到的音频规格
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {//打开音频设备,并返回音频设备的id
av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
wanted_spec.channels, wanted_spec.freq, SDL_GetError());
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
if (!wanted_spec.channels) {
wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
wanted_spec.channels = wanted_nb_channels;
if (!wanted_spec.freq) {
av_log(NULL, AV_LOG_ERROR,
"No more combinations to try, audio open failed\n");
return -1;
}
}
wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
}
if (spec.format != AUDIO_S16SYS) {
av_log(NULL, AV_LOG_ERROR,
"SDL advised audio format %d is not supported!\n", spec.format);
return -1;
}
if (spec.channels != wanted_spec.channels) {
wanted_channel_layout = av_get_default_channel_layout(spec.channels);
if (!wanted_channel_layout) {
av_log(NULL, AV_LOG_ERROR,
"SDL advised channel count %d is not supported!\n", spec.channels);
return -1;
}
}
//打开音频设备成功后,返回相关的参数
audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
//采样率,如48000
audio_hw_params->freq = spec.freq;
//chanel_layout 如3
audio_hw_params->channel_layout = wanted_channel_layout;
//channels,如2
audio_hw_params->channels = spec.channels;
//单个样本占用的字节数,比如双通道16位占用4字节
audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
//每秒的字节数,采样率*单个样本的大小,比如48000*4=192000
audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
return -1;
}
//返回数据大小,如8192
return spec.size;
}
static void decoder_init(Decoder *d, //要初始化的解码器
AVCodecContext *avctx,//用来初始化解码器的,解码器上下文
PacketQueue *queue,//解码器中的数据队列
SDL_cond *empty_queue_cond) {//解码器中条件变量
memset(d, 0, sizeof(Decoder));
d->avctx = avctx;
d->queue = queue;
d->empty_queue_cond = empty_queue_cond;
d->start_pts = AV_NOPTS_VALUE;
d->pkt_serial = -1;
}
/* return < 0 if aborted, 0 if no packet and > 0 if packet. */
//取出从read_thread加入的未解码包,取出成功返回值大于0
static int packet_queue_get(PacketQueue *q,
AVPacket *pkt, //取出的包数据
int block,//是否等待同步信号
int *serial)//取出的包的serial,运行中都是1
{
MyAVPacketList *pkt1;
int ret;
SDL_LockMutex(q->mutex);
for (;;) {
if (q->abort_request) {
ret = -1;
break;
}
pkt1 = q->first_pkt;
if (pkt1) {
q->first_pkt = pkt1->next;
if (!q->first_pkt)
q->last_pkt = NULL;
q->nb_packets--;
q->size -= pkt1->pkt.size + sizeof(*pkt1);
q->duration -= pkt1->pkt.duration;
*pkt = pkt1->pkt;
if (serial)
*serial = pkt1->serial;
av_free(pkt1);
ret = 1;
break;
}
else if (!block) {
ret = 0;
break;
}
else {
SDL_CondWait(q->cond, q->mutex);//等待信号,此信号在放弃,或者有新包加入时都会被发送
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
int ret = AVERROR(EAGAIN);
for (;;) {
AVPacket pkt;
//序列号一致,说明这是一次连续播放,否则可能是进行了快进,快退等,不是一次连续播放
if (d->queue->serial == d->pkt_serial) {
do {
if (d->queue->abort_request)
return -1;
switch (d->avctx->codec_type) {
case AVMEDIA_TYPE_VIDEO:
ret = avcodec_receive_frame(d->avctx, frame);//avcodec_receive_frame取出解码后的帧,和avcodec_send_packet配对使用
if (ret >= 0) {
if (decoder_reorder_pts == -1) {
//视频的时间戳pts
frame->pts = frame->best_effort_timestamp;
}
else if (!decoder_reorder_pts) {
frame->pts = frame->pkt_dts;
}
}
break;
case AVMEDIA_TYPE_AUDIO:
ret = avcodec_receive_frame(d->avctx, frame);
if (ret >= 0) {
AVRational tb;// = (AVRational) { 1, frame->sample_rate };
tb.num = 1;
tb.den = frame->sample_rate;
if (frame->pts != AV_NOPTS_VALUE)
//计算音频的时间戳
//av_rescale_q(a,b,c)是用来把时间戳从一个时基调整到另外一个时基时候用的函数
//也就是知道了在b基下的时间戳转换到c基下的时间戳。比如采样率100(时间基是1/100)下的时间戳是6,那么采样率200下的时间戳算出来就是3
frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
else if (d->next_pts != AV_NOPTS_VALUE)
frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
if (frame->pts != AV_NOPTS_VALUE) {
d->next_pts = frame->pts + frame->nb_samples;
d->next_pts_tb = tb;
}
}
break;
}
if (ret == AVERROR_EOF) {
d->finished = d->pkt_serial;
avcodec_flush_buffers(d->avctx);
return 0;
}
if (ret >= 0)
return 1;
} while (ret != AVERROR(EAGAIN));
}
do {
if (d->queue->nb_packets == 0)//如果PacketQueue中没有数据
SDL_CondSignal(d->empty_queue_cond);//发送继续读取的信号
if (d->packet_pending) {//上一次送入数据到编码器失败的标志,表示数据要重新放入
av_packet_move_ref(&pkt, &d->pkt);//把数据取到pkt,后面重新往编码器放
d->packet_pending = 0;
}
else {
if (packet_queue_get(d->queue,
&pkt,//取出的包数据
1,//取出后等待同步信号
&d->pkt_serial) < 0)//从队列中获取一包数据
return -1;
}
} while (d->queue->serial != d->pkt_serial);//序列号不同,说明上一次的连续播放已经无效,开始了一次新的连续播放
if (pkt.data == flush_pkt.data) {
avcodec_flush_buffers(d->avctx);
d->finished = 0;
d->next_pts = d->start_pts;
d->next_pts_tb = d->start_pts_tb;
}
else {
if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
int got_frame = 0;
ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &pkt);
if (ret < 0) {
ret = AVERROR(EAGAIN);
}
else {
if (got_frame && !pkt.data) {
d->packet_pending = 1;
av_packet_move_ref(&d->pkt, &pkt);
}
ret = got_frame ? 0 : (pkt.data ? AVERROR(EAGAIN) : AVERROR_EOF);
}
}
else {
if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {//avcodec_send_packet给解码器发送未解码包
av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
d->packet_pending = 1;
av_packet_move_ref(&d->pkt, &pkt);//送入数据到编码器失败,把数据转移到d->pkt
}
}
av_packet_unref(&pkt);
}
}
}