From 0ce7757b2dd81a7f00b9def2b0bf41e22f3f9960 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 7 Jun 2013 18:18:30 +0800 Subject: [PATCH] jni: ffplay: introduce more code --- ijkmediaplayer/jni/ijkplayer/Android.mk | 5 +- .../jni/ijkplayer/ffplay_cmdutils.c | 130 +++ .../jni/ijkplayer/ffplay_cmdutils.h | 35 + ijkmediaplayer/jni/ijkplayer/ffplay_def.h | 28 +- .../jni/ijkplayer/ffplay_pkt_queue.h | 2 +- .../jni/ijkplayer/ffplay_read_thread.c | 998 ++++++++++++++++++ .../jni/ijkplayer/ffplay_subtitle_thread.c | 29 + .../jni/ijkplayer/ffplay_video_thread.c | 29 + ijkmediaplayer/jni/ijkplayer/ffplayer.h | 45 +- ijkmediaplayer/jni/ijkplayer/thread_demux.c | 309 ------ 10 files changed, 1273 insertions(+), 337 deletions(-) create mode 100644 ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.c create mode 100644 ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.h create mode 100644 ijkmediaplayer/jni/ijkplayer/ffplay_read_thread.c create mode 100644 ijkmediaplayer/jni/ijkplayer/ffplay_subtitle_thread.c create mode 100644 ijkmediaplayer/jni/ijkplayer/ffplay_video_thread.c delete mode 100644 ijkmediaplayer/jni/ijkplayer/thread_demux.c diff --git a/ijkmediaplayer/jni/ijkplayer/Android.mk b/ijkmediaplayer/jni/ijkplayer/Android.mk index a49b9e0a..c150fd2c 100644 --- a/ijkmediaplayer/jni/ijkplayer/Android.mk +++ b/ijkmediaplayer/jni/ijkplayer/Android.mk @@ -26,9 +26,12 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_JNI_ROOT) +LOCAL_SRC_FILES += ffplay_cmdutils.c LOCAL_SRC_FILES += ffplay_pkt_queue.c +LOCAL_SRC_FILES += ffplay_read_thread.c +LOCAL_SRC_FILES += ffplay_subtitle_thread.c +LOCAL_SRC_FILES += ffplay_video_thread.c LOCAL_SRC_FILES += ffplayer.c -LOCAL_SRC_FILES += thread_demux.c LOCAL_SRC_FILES += ijkplayer.c LOCAL_SRC_FILES += ijkplayer_jni.c diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.c b/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.c new file mode 100644 index 00000000..5a24bfa4 --- /dev/null +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.c @@ -0,0 +1,130 @@ +/***************************************************************************** + * ffplay_cmdutils.c + ***************************************************************************** + * + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2013 Zhang Rui + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ffplay_cmdutils.h" +#include + +/* cmdutils.c 942 */ +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); +} +/* cmdutils.c 942 */ + +/* cmdutils.c 1731 */ +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; +} +/* cmdutils.c 1731 */ + +/* cmdutils.c 1739 */ +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->codec->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; + default: + 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: return NULL; + } + + 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; +} +/* cmdutils.c 1739 */ + +/* cmdutils.c 1795 */ +AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, + AVDictionary *codec_opts) +{ + int i; + AVDictionary **opts; + + if (!s->nb_streams) + return NULL; + opts = av_mallocz(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]->codec->codec_id, + s, s->streams[i], NULL); + return opts; +} +/* cmdutils.c 1795 */ diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.h b/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.h new file mode 100644 index 00000000..236bc53f --- /dev/null +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_cmdutils.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * ffplay_cmdutils.h + ***************************************************************************** + * + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2013 Zhang Rui + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IJKPLAYER__FFPLAY_CMDUTILS_H +#define IJKPLAYER__FFPLAY_CMDUTILS_H + +#include + +void print_error(const char *filename, int err); +AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts); +AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, AVCodec *codec); + +#endif diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_def.h b/ijkmediaplayer/jni/ijkplayer/ffplay_def.h index f5ea5160..2944ee0c 100644 --- a/ijkmediaplayer/jni/ijkplayer/ffplay_def.h +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_def.h @@ -26,8 +26,12 @@ #define IJKPLAYER__FFPLAY_DEF_H #include +#include "libavformat/avformat.h" +#include "libavcodec/avfft.h" +#include "libswresample/swresample.h" #include "ijksdl/ijksdl.h" #include "ffplay_pkt_queue.h" +#include "ffplay_cmdutils.h" #ifdef CONFIG_AVFILTER #undef CONFIG_AVFILTER @@ -44,10 +48,10 @@ /* no AV sync correction is done if below the AV sync threshold */ // #define AV_SYNC_THRESHOLD 0.01 /* no AV correction is done if too big error */ -// #define AV_NOSYNC_THRESHOLD 10.0 +#define AV_NOSYNC_THRESHOLD 10.0 /* maximum audio speed change to get correct sync */ -// #define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 /* external clock speed adjustment constants for realtime sources based on buffer fullness */ // #define EXTERNAL_CLOCK_SPEED_MIN 0.900 @@ -55,7 +59,7 @@ // #define EXTERNAL_CLOCK_SPEED_STEP 0.001 /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */ -// #define AUDIO_DIFF_AVG_NB 20 +#define AUDIO_DIFF_AVG_NB 20 /* polls for possible required screen refresh at least this often, should be less than 1/fps */ // #define REFRESH_RATE 0.01 @@ -167,9 +171,9 @@ typedef struct VideoState { int16_t sample_array[SAMPLE_ARRAY_SIZE]; int sample_array_index; int last_i_start; - // RDFTContext *rdft; + RDFTContext *rdft; int rdft_bits; - // FFTSample *rdft_data; + FFTSample *rdft_data; int xpos; double last_vis_time; @@ -223,13 +227,8 @@ typedef struct VideoState { SDL_cond *continue_read_thread; } VideoState; -/* PLACEHOLD: options variables has been moved to ffplayer.h */ - -/* current context */ -// static int is_full_screen; -// static int64_t audio_callback_time; - -// static AVPacket flush_pkt; +/* PLACEHOLD: options has been moved to ffplayer.h */ +/* PLACEHOLD: current context has been moved to ffplayer.h */ /* PLACEHOLD: flush_pkt has been moved to pkt_queue.c */ #define FF_ALLOC_EVENT (SDL_USEREVENT) @@ -237,6 +236,9 @@ typedef struct VideoState { // static SDL_Surface *screen; -void print_error(const char *filename, int err); +/* extra forward declaration */ +int ijkff_read_thread(void *arg); +int ijkff_video_thread(void *arg); +int ijkff_subtitle_thread(void *arg); #endif diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_pkt_queue.h b/ijkmediaplayer/jni/ijkplayer/ffplay_pkt_queue.h index f9f96f8d..81d1500c 100644 --- a/ijkmediaplayer/jni/ijkplayer/ffplay_pkt_queue.h +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_pkt_queue.h @@ -25,7 +25,7 @@ #ifndef IJKPLAYER__FFPLAY_PKT_QUEUE_H #define IJKPLAYER__FFPLAY_PKT_QUEUE_H -#include +#include #include typedef struct MyAVPacketList { diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_read_thread.c b/ijkmediaplayer/jni/ijkplayer/ffplay_read_thread.c new file mode 100644 index 00000000..a6a3f438 --- /dev/null +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_read_thread.c @@ -0,0 +1,998 @@ +/***************************************************************************** + * ffplay_read_thread.c + ***************************************************************************** + * + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2013 Zhang Rui + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "ffplayer.h" + +/* ffplay.c 1082 */ +/* get the current audio clock value */ +static double get_audio_clock(VideoState *is) +{ + if (is->audio_clock_serial != is->audioq.serial) + return NAN; + if (is->paused) { + return is->audio_current_pts; + } else { + return is->audio_current_pts_drift + av_gettime() / 1000000.0; + } +} +/* ffplay.c 1082 */ + +/* ffplay.c 1094 */ +/* get the current video clock value */ +static double get_video_clock(VideoState *is) +{ + if (is->video_clock_serial != is->videoq.serial) + return NAN; + if (is->paused) { + return is->video_current_pts; + } else { + return is->video_current_pts_drift + av_gettime() / 1000000.0; + } +} +/* ffplay.c 1094 */ + +/* ffplay.c 1106 */ +/* get the current external clock value */ +static double get_external_clock(VideoState *is) +{ + if (is->paused) { + return is->external_clock; + } else { + double time = av_gettime() / 1000000.0; + return is->external_clock_drift + time - (time - is->external_clock_time / 1000000.0) * (1.0 - is->external_clock_speed); + } +} +/* ffplay.c 1106 */ + +/* ffplay.c 1117 */ +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; + } +} +/* ffplay.c 1117 */ + +/* ffplay.c 1133 */ +/* 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_video_clock(is); + break; + case AV_SYNC_AUDIO_MASTER: + val = get_audio_clock(is); + break; + default: + val = get_external_clock(is); + break; + } + return val; +} +/* ffplay.c 1133 */ + +/* ffplay.c 1152 */ +static void update_external_clock_pts(VideoState *is, double pts) +{ + is->external_clock_time = av_gettime(); + is->external_clock = pts; + is->external_clock_drift = pts - is->external_clock_time / 1000000.0; +} +/* ffplay.c 1152 */ + +/* ffplay.c 1159 */ +static void check_external_clock_sync(VideoState *is, double pts) { + double ext_clock = get_external_clock(is); + if (isnan(ext_clock) || fabs(ext_clock - pts) > AV_NOSYNC_THRESHOLD) { + update_external_clock_pts(is, pts); + } +} +/* ffplay.c 1159 */ + +/* ffplay.c 1185 */ +/* seek in the stream */ +static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes) +{ + if (!is->seek_req) { + is->seek_pos = pos; + is->seek_rel = rel; + is->seek_flags &= ~AVSEEK_FLAG_BYTE; + if (seek_by_bytes) + is->seek_flags |= AVSEEK_FLAG_BYTE; + is->seek_req = 1; + SDL_CondSignal(is->continue_read_thread); + } +} +/* ffplay.c 1185 */ + +/* ffplay.c 1199 */ +/* pause or resume the video */ +static void stream_toggle_pause(VideoState *is) +{ + 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)) { + is->video_current_pts = is->video_current_pts_drift + av_gettime() / 1000000.0; + } + is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0; + } + update_external_clock_pts(is, get_external_clock(is)); + is->paused = !is->paused; +} +/* ffplay.c 1199 */ + +/* ffplay.c 1219 */ +static void step_to_next_frame(VideoState *is) +{ + /* if the stream is paused unpause it, then step */ + if (is->paused) + stream_toggle_pause(is); + is->step = 1; +} +/* ffplay.c 1219 */ + +/* ffplay.c 2018 */ +/* 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; + } +} +/* ffplay.c 2018 */ + +/* ffplay.c 2037 */ +/* 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 = get_audio_clock(is) - get_master_clock(is); + + 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 */ + 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_nb_samples = nb_samples + (int)(diff * is->audio_src.freq); + 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 = FFMIN(FFMAX(wanted_nb_samples, min_nb_samples), max_nb_samples); + } + av_dlog(NULL, "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; +} +/* ffplay.c 2037 */ + +/* ffplay.c 2080 */ +/** + * 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) +{ + AVPacket *pkt_temp = &is->audio_pkt_temp; + AVPacket *pkt = &is->audio_pkt; + AVCodecContext *dec = is->audio_st->codec; + int len1, len2, data_size, resampled_data_size; + int64_t dec_channel_layout; + int got_frame; + av_unused double audio_clock0; + int new_packet = 0; + int flush_complete = 0; + int wanted_nb_samples; + + for (;;) { + /* NOTE: the audio packet can contain several frames */ + while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) { + if (!is->frame) { + if (!(is->frame = avcodec_alloc_frame())) + return AVERROR(ENOMEM); + } else + avcodec_get_frame_defaults(is->frame); + + if (is->audioq.serial != is->audio_pkt_temp_serial) + break; + + if (is->paused) + return -1; + + if (flush_complete) + break; + new_packet = 0; + len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp); + if (len1 < 0) { + /* if error, we skip the frame */ + pkt_temp->size = 0; + break; + } + + pkt_temp->data += len1; + pkt_temp->size -= len1; + + if (!got_frame) { + /* stop sending empty packets if the decoder is finished */ + if (!pkt_temp->data && (dec->codec->capabilities & CODEC_CAP_DELAY)) + flush_complete = 1; + continue; + } + data_size = av_samples_get_buffer_size(NULL, av_frame_get_channels(is->frame), + is->frame->nb_samples, + is->frame->format, 1); + + dec_channel_layout = + (is->frame->channel_layout && av_frame_get_channels(is->frame) == av_get_channel_layout_nb_channels(is->frame->channel_layout)) ? + is->frame->channel_layout : av_get_default_channel_layout(av_frame_get_channels(is->frame)); + wanted_nb_samples = synchronize_audio(is, is->frame->nb_samples); + + if (is->frame->format != is->audio_src.fmt || + dec_channel_layout != is->audio_src.channel_layout || + is->frame->sample_rate != is->audio_src.freq || + (wanted_nb_samples != is->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, is->frame->format, is->frame->sample_rate, + 0, NULL); + if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { + fprintf(stderr, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", + is->frame->sample_rate, av_get_sample_fmt_name(is->frame->format), av_frame_get_channels(is->frame), + is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels); + break; + } + is->audio_src.channel_layout = dec_channel_layout; + is->audio_src.channels = av_frame_get_channels(is->frame); + is->audio_src.freq = is->frame->sample_rate; + is->audio_src.fmt = is->frame->format; + } + + if (is->swr_ctx) { + const uint8_t **in = (const uint8_t **)is->frame->extended_data; + uint8_t **out = &is->audio_buf1; + int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / is->frame->sample_rate + 256; + int out_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0); + if (wanted_nb_samples != is->frame->nb_samples) { + if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - is->frame->nb_samples) * is->audio_tgt.freq / is->frame->sample_rate, + wanted_nb_samples * is->audio_tgt.freq / is->frame->sample_rate) < 0) { + fprintf(stderr, "swr_set_compensation() failed\n"); + break; + } + } + 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, is->frame->nb_samples); + if (len2 < 0) { + fprintf(stderr, "swr_convert() failed\n"); + break; + } + if (len2 == out_count) { + fprintf(stderr, "warning: audio buffer is probably too small\n"); + swr_init(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 = is->frame->data[0]; + resampled_data_size = data_size; + } + + audio_clock0 = is->audio_clock; + is->audio_clock += (double)data_size / + (av_frame_get_channels(is->frame) * is->frame->sample_rate * av_get_bytes_per_sample(is->frame->format)); +#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; + } + + /* free the current packet */ + if (pkt->data) + av_free_packet(pkt); + memset(pkt_temp, 0, sizeof(*pkt_temp)); + + if (is->audioq.abort_request) { + return -1; + } + + if (is->audioq.nb_packets == 0) + SDL_CondSignal(is->continue_read_thread); + + /* read next packet */ + if ((new_packet = packet_queue_get(&is->audioq, pkt, 1, &is->audio_pkt_temp_serial)) < 0) + return -1; + + if (pkt->data == packet_get_flush_pkt()->data) { + avcodec_flush_buffers(dec); + flush_complete = 0; + } + + *pkt_temp = *pkt; + + /* 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; + is->audio_clock_serial = is->audio_pkt_temp_serial; + } + } + return -1; +} +/* ffplay.c 2080 */ + +/* ffplay.c 2242 */ +/* prepare a new audio buffer */ +static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) +{ + FFPlayer *ffp = opaque; + VideoState *is = &ffp->is; + int audio_size, len1; + int bytes_per_sec; + int frame_size = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, 1, is->audio_tgt.fmt, 1); + + ffp->audio_callback_time = av_gettime(); + + while (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 = is->silence_buf; + is->audio_buf_size = sizeof(is->silence_buf) / frame_size * 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; + } + 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; + } + bytes_per_sec = is->audio_tgt.freq * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt); + 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_current_pts = is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / bytes_per_sec; + is->audio_current_pts_drift = is->audio_current_pts - ffp->audio_callback_time / 1000000.0; + if (is->audioq.serial == is->audio_clock_serial) + check_external_clock_sync(is, is->audio_current_pts); +} +/* ffplay.c 2242 */ + +/* ffplay.c 2282 */ +static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params) +{ + SDL_AudioSpec wanted_spec, spec; + const char *env; + const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6}; + + env = SDL_getenv("SDL_AUDIO_CHANNELS"); + if (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); + wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; + } + wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout); + wanted_spec.freq = wanted_sample_rate; + if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) { + fprintf(stderr, "Invalid sample rate or channel count!\n"); + return -1; + } + wanted_spec.format = AUDIO_S16SYS; + wanted_spec.silence = 0; + wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback = sdl_audio_callback; + wanted_spec.userdata = opaque; + while (SDL_OpenAudio(&wanted_spec, &spec) < 0) { + fprintf(stderr, "SDL_OpenAudio (%d channels): %s\n", wanted_spec.channels, SDL_GetError()); + wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; + if (!wanted_spec.channels) { + fprintf(stderr, "No more channel 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) { + fprintf(stderr, "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) { + fprintf(stderr, "SDL advised channel count %d is not supported!\n", spec.channels); + return -1; + } + } + + audio_hw_params->fmt = AV_SAMPLE_FMT_S16; + audio_hw_params->freq = spec.freq; + audio_hw_params->channel_layout = wanted_channel_layout; + audio_hw_params->channels = spec.channels; + return spec.size; +} +/* ffplay.c 2282 */ + +/* ffplay.c 2337 */ +/* open a given stream. Return 0 if OK */ +static int stream_component_open(FFPlayer *ffp, int stream_index) +{ + VideoState *is = &ffp->is; + AVFormatContext *ic = is->ic; + AVCodecContext *avctx; + AVCodec *codec; + const char *forced_codec_name = NULL; + AVDictionary *opts; + AVDictionaryEntry *t = NULL; + + if (stream_index < 0 || stream_index >= ic->nb_streams) + return -1; + avctx = ic->streams[stream_index]->codec; + + codec = avcodec_find_decoder(avctx->codec_id); + + switch(avctx->codec_type){ + case AVMEDIA_TYPE_AUDIO : is->last_audio_stream = stream_index; forced_codec_name = ffp->audio_codec_name; break; + case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = ffp->subtitle_codec_name; break; + case AVMEDIA_TYPE_VIDEO : is->last_video_stream = stream_index; forced_codec_name = ffp->video_codec_name; break; + default: break; + } + if (forced_codec_name) + codec = avcodec_find_decoder_by_name(forced_codec_name); + if (!codec) { + if (forced_codec_name) fprintf(stderr, "No codec could be found with name '%s'\n", forced_codec_name); + else fprintf(stderr, "No codec could be found with id %d\n", avctx->codec_id); + return -1; + } + + avctx->codec_id = codec->id; + avctx->workaround_bugs = ffp->workaround_bugs; + avctx->lowres = ffp->lowres; + if(avctx->lowres > codec->max_lowres){ + av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n", + codec->max_lowres); + avctx->lowres= codec->max_lowres; + } + avctx->idct_algo = ffp->idct; + avctx->skip_frame = ffp->skip_frame; + avctx->skip_idct = ffp->skip_idct; + avctx->skip_loop_filter = ffp->skip_loop_filter; + avctx->error_concealment = ffp->error_concealment; + + if(avctx->lowres) avctx->flags |= CODEC_FLAG_EMU_EDGE; + if (ffp->fast) avctx->flags2 |= CODEC_FLAG2_FAST; + if(codec->capabilities & CODEC_CAP_DR1) + avctx->flags |= CODEC_FLAG_EMU_EDGE; + + opts = filter_codec_opts(ffp->codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec); + if (!av_dict_get(opts, "threads", NULL, 0)) + av_dict_set(&opts, "threads", "auto", 0); + if (avcodec_open2(avctx, codec, &opts) < 0) + return -1; + if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { + av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key); + return AVERROR_OPTION_NOT_FOUND; + } + + /* prepare audio output */ + if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { + int audio_hw_buf_size = audio_open(ffp, avctx->channel_layout, avctx->channels, avctx->sample_rate, &is->audio_src); + if (audio_hw_buf_size < 0) + return -1; + is->audio_hw_buf_size = audio_hw_buf_size; + is->audio_tgt = is->audio_src; + } + + ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; + switch (avctx->codec_type) { + case AVMEDIA_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 * is->audio_hw_buf_size / av_samples_get_buffer_size(NULL, is->audio_tgt.channels, is->audio_tgt.freq, is->audio_tgt.fmt, 1); + + memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); + memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp)); + packet_queue_start(&is->audioq); + SDL_PauseAudio(0); + break; + case AVMEDIA_TYPE_VIDEO: + is->video_stream = stream_index; + is->video_st = ic->streams[stream_index]; + + packet_queue_start(&is->videoq); + is->video_tid = SDL_CreateThread(ijkff_video_thread, ffp); + break; + case AVMEDIA_TYPE_SUBTITLE: + is->subtitle_stream = stream_index; + is->subtitle_st = ic->streams[stream_index]; + packet_queue_start(&is->subtitleq); + + is->subtitle_tid = SDL_CreateThread(ijkff_subtitle_thread, ffp); + break; + default: + break; + } + return 0; +} +/* ffplay.c 2337 */ + +/* ffplay.c 2443 */ +static void stream_component_close(VideoState *is, int stream_index) +{ + AVFormatContext *ic = is->ic; + AVCodecContext *avctx; + + if (stream_index < 0 || stream_index >= ic->nb_streams) + return; + avctx = ic->streams[stream_index]->codec; + + switch (avctx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + packet_queue_abort(&is->audioq); + + SDL_CloseAudio(); + + packet_queue_flush(&is->audioq); + av_free_packet(&is->audio_pkt); + swr_free(&is->swr_ctx); + av_freep(&is->audio_buf1); + is->audio_buf1_size = 0; + is->audio_buf = NULL; + avcodec_free_frame(&is->frame); + + if (is->rdft) { + av_rdft_end(is->rdft); + av_freep(&is->rdft_data); + is->rdft = NULL; + is->rdft_bits = 0; + } + break; + case AVMEDIA_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_flush(&is->videoq); + break; + case AVMEDIA_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_flush(&is->subtitleq); + break; + default: + break; + } + + ic->streams[stream_index]->discard = AVDISCARD_ALL; + avcodec_close(avctx); +#if CONFIG_AVFILTER + free_buffer_pool(&is->buffer_pool); +#endif + switch (avctx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + is->audio_st = NULL; + is->audio_stream = -1; + break; + case AVMEDIA_TYPE_VIDEO: + is->video_st = NULL; + is->video_stream = -1; + break; + case AVMEDIA_TYPE_SUBTITLE: + is->subtitle_st = NULL; + is->subtitle_stream = -1; + break; + default: + break; + } +} +/* ffplay.c 2443 */ + +/* ffplay.c 2534 */ +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->filename, "rtp:", 4) + || !strncmp(s->filename, "udp:", 4) + ) + ) + return 1; + return 0; +} +/* ffplay.c 2534 */ + +/* ffplay.c 2551 */ +int ijkff_read_thread(void *arg) +{ + FFPlayer *ffp = arg; + VideoState *is = &ffp->is; + AVFormatContext *ic = NULL; + int err, i, ret; + int st_index[AVMEDIA_TYPE_NB]; + AVPacket pkt1, *pkt = &pkt1; + int eof = 0; + int pkt_in_play_range = 0; + AVDictionaryEntry *t; + AVDictionary **opts; + int orig_nb_streams; + SDL_mutex *wait_mutex = SDL_CreateMutex(); + + memset(st_index, -1, sizeof(st_index)); + is->last_video_stream = is->video_stream = -1; + is->last_audio_stream = is->audio_stream = -1; + is->last_subtitle_stream = is->subtitle_stream = -1; + + ic = avformat_alloc_context(); + ic->interrupt_callback.callback = ffp->decode_interrupt_cb; + ic->interrupt_callback.opaque = is; + err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts); + if (err < 0) { + print_error(is->filename, err); + ret = -1; + goto fail; + } + if ((t = av_dict_get(ffp->format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { + av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key); + ret = AVERROR_OPTION_NOT_FOUND; + goto fail; + } + is->ic = ic; + + if (ffp->genpts) + ic->flags |= AVFMT_FLAG_GENPTS; + + opts = setup_find_stream_info_opts(ic, ffp->codec_opts); + orig_nb_streams = ic->nb_streams; + + err = avformat_find_stream_info(ic, opts); + if (err < 0) { + fprintf(stderr, "%s: could not find codec parameters\n", is->filename); + ret = -1; + goto fail; + } + for (i = 0; i < orig_nb_streams; i++) + av_dict_free(&opts[i]); + av_freep(&opts); + + if (ic->pb) + ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use url_feof() to test for the end + + if (ffp->seek_by_bytes < 0) + ffp->seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT); + + is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0; + + /* if seeking requested, we execute it */ + if (ffp->start_time != AV_NOPTS_VALUE) { + int64_t timestamp; + + timestamp = ffp->start_time; + /* add the stream start time */ + if (ic->start_time != AV_NOPTS_VALUE) + timestamp += ic->start_time; + ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0); + if (ret < 0) { + fprintf(stderr, "%s: could not seek to position %0.3f\n", + is->filename, (double)timestamp / AV_TIME_BASE); + } + } + + is->realtime = is_realtime(ic); + + for (i = 0; i < ic->nb_streams; i++) + ic->streams[i]->discard = AVDISCARD_ALL; + if (!ffp->video_disable) + st_index[AVMEDIA_TYPE_VIDEO] = + av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, + ffp->wanted_stream[AVMEDIA_TYPE_VIDEO], -1, NULL, 0); + if (!ffp->audio_disable) + st_index[AVMEDIA_TYPE_AUDIO] = + av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, + ffp->wanted_stream[AVMEDIA_TYPE_AUDIO], + st_index[AVMEDIA_TYPE_VIDEO], + NULL, 0); + if (!ffp->video_disable && !ffp->subtitle_disable) + st_index[AVMEDIA_TYPE_SUBTITLE] = + av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, + ffp->wanted_stream[AVMEDIA_TYPE_SUBTITLE], + (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? + st_index[AVMEDIA_TYPE_AUDIO] : + st_index[AVMEDIA_TYPE_VIDEO]), + NULL, 0); + if (ffp->show_status) { + av_dump_format(ic, 0, is->filename, 0); + } + + is->show_mode = ffp->show_mode; + + /* open the streams */ + if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) { + stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]); + } + + ret = -1; + if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) { + ret = stream_component_open(ffp, st_index[AVMEDIA_TYPE_VIDEO]); + } + if (is->show_mode == SHOW_MODE_NONE) + is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT; + + if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) { + stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]); + } + + if (is->video_stream < 0 && is->audio_stream < 0) { + fprintf(stderr, "%s: could not open codecs\n", is->filename); + ret = -1; + goto fail; + } + + if (ffp->infinite_buffer < 0 && is->realtime) + ffp->infinite_buffer = 1; + + for (;;) { + if (is->abort_request) + break; + if (is->paused != is->last_paused) { + is->last_paused = is->paused; + if (is->paused) + is->read_pause_return = av_read_pause(ic); + else + av_read_play(ic); + } + + if (is->paused && + (!strcmp(ic->iformat->name, "rtsp") || + (ic->pb && !strncmp(ffp->input_filename, "mmsh:", 5)))) { + /* wait 10 ms to avoid trying to get another packet */ + /* XXX: horrible */ + SDL_Delay(10); + continue; + } + + if (is->seek_req) { + int64_t seek_target = is->seek_pos; + int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN; + int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX; +// FIXME the +-2 is due to rounding being not done in the correct direction in generation +// of the seek_pos/seek_rel variables + + ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags); + if (ret < 0) { + fprintf(stderr, "%s: error while seeking\n", is->ic->filename); + } else { + if (is->audio_stream >= 0) { + packet_queue_flush(&is->audioq); + packet_queue_put(&is->audioq, packet_get_flush_pkt()); + } + if (is->subtitle_stream >= 0) { + packet_queue_flush(&is->subtitleq); + packet_queue_put(&is->subtitleq, packet_get_flush_pkt()); + } + if (is->video_stream >= 0) { + packet_queue_flush(&is->videoq); + packet_queue_put(&is->videoq, packet_get_flush_pkt()); + } + if (is->seek_flags & AVSEEK_FLAG_BYTE) { + update_external_clock_pts(is, NAN); + } else { + update_external_clock_pts(is, seek_target / (double)AV_TIME_BASE); + } + } + is->seek_req = 0; + eof = 0; + if (is->paused) + step_to_next_frame(is); + } + if (is->queue_attachments_req) { + avformat_queue_attached_pictures(ic); + is->queue_attachments_req = 0; + } + + /* if the queue are full, no need to read more */ + if (ffp->infinite_buffer<1 && + (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE + || ( (is->audioq .nb_packets > MIN_FRAMES || is->audio_stream < 0 || is->audioq.abort_request) + && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0 || is->videoq.abort_request) + && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0 || is->subtitleq.abort_request)))) { + /* wait 10 ms */ + SDL_LockMutex(wait_mutex); + SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); + SDL_UnlockMutex(wait_mutex); + continue; + } + if (eof) { + if (is->video_stream >= 0) { + av_init_packet(pkt); + pkt->data = NULL; + pkt->size = 0; + pkt->stream_index = is->video_stream; + packet_queue_put(&is->videoq, pkt); + } + if (is->audio_stream >= 0 && + (is->audio_st->codec->codec->capabilities & CODEC_CAP_DELAY)) { + av_init_packet(pkt); + pkt->data = NULL; + pkt->size = 0; + pkt->stream_index = is->audio_stream; + packet_queue_put(&is->audioq, pkt); + } + SDL_Delay(10); + if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) { + if (ffp->loop != 1 && (!ffp->loop || --ffp->loop)) { + stream_seek(is, ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0, 0, 0); + } else if (ffp->autoexit) { + ret = AVERROR_EOF; + goto fail; + } + } + eof=0; + continue; + } + ret = av_read_frame(ic, pkt); + if (ret < 0) { + if (ret == AVERROR_EOF || url_feof(ic->pb)) + eof = 1; + if (ic->pb && ic->pb->error) + break; + SDL_LockMutex(wait_mutex); + SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); + SDL_UnlockMutex(wait_mutex); + continue; + } + /* check if packet is in play range specified by user, then queue, otherwise discard */ + pkt_in_play_range = ffp->duration == AV_NOPTS_VALUE || + (pkt->pts - ic->streams[pkt->stream_index]->start_time) * + av_q2d(ic->streams[pkt->stream_index]->time_base) - + (double)(ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0) / 1000000 + <= ((double)ffp->duration / 1000000); + if (pkt->stream_index == is->audio_stream && pkt_in_play_range) { + packet_queue_put(&is->audioq, pkt); + } else if (pkt->stream_index == is->video_stream && pkt_in_play_range) { + packet_queue_put(&is->videoq, pkt); + } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) { + packet_queue_put(&is->subtitleq, pkt); + } else { + av_free_packet(pkt); + } + } + /* wait until the end */ + while (!is->abort_request) { + SDL_Delay(100); + } + + ret = 0; + fail: + /* 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); + if (is->ic) { + avformat_close_input(&is->ic); + } + + if (ret != 0) { + SDL_Event event; + + event.type = FF_QUIT_EVENT; + event.user.data1 = is; + SDL_PushEvent(&event); + } + SDL_DestroyMutex(wait_mutex); + return 0; +} +/* ffplay.c 2551 */ + diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_subtitle_thread.c b/ijkmediaplayer/jni/ijkplayer/ffplay_subtitle_thread.c new file mode 100644 index 00000000..f02440f1 --- /dev/null +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_subtitle_thread.c @@ -0,0 +1,29 @@ +/***************************************************************************** + * ffplay_subtitle_thread.c + ***************************************************************************** + * + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2013 Zhang Rui + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +int ijkff_subtitle_thread(void *arg) +{ + // FIXME: implement + return 0; +} diff --git a/ijkmediaplayer/jni/ijkplayer/ffplay_video_thread.c b/ijkmediaplayer/jni/ijkplayer/ffplay_video_thread.c new file mode 100644 index 00000000..d1615380 --- /dev/null +++ b/ijkmediaplayer/jni/ijkplayer/ffplay_video_thread.c @@ -0,0 +1,29 @@ +/***************************************************************************** + * ffplay_video_thread.c + ***************************************************************************** + * + * copyright (c) 2001 Fabrice Bellard + * copyright (c) 2013 Zhang Rui + * + * This file is part of ijkPlayer. + * + * ijkPlayer is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * ijkPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with ijkPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +int ijkff_video_thread(void *arg) +{ + // FIXME: implement + return 0; +} diff --git a/ijkmediaplayer/jni/ijkplayer/ffplayer.h b/ijkmediaplayer/jni/ijkplayer/ffplayer.h index 28002d30..fac9ef8f 100644 --- a/ijkmediaplayer/jni/ijkplayer/ffplayer.h +++ b/ijkmediaplayer/jni/ijkplayer/ffplayer.h @@ -58,15 +58,15 @@ typedef struct FFPlayer { // int av_sync_type = AV_SYNC_AUDIO_MASTER; int64_t start_time; int64_t duration; - // int workaround_bugs = 1; - // int fast = 0; + int workaround_bugs; + int fast; int genpts; - // int lowres = 0; - // int idct = FF_IDCT_AUTO; - // enum AVDiscard skip_frame = AVDISCARD_DEFAULT; - // enum AVDiscard skip_idct = AVDISCARD_DEFAULT; - // enum AVDiscard skip_loop_filter = AVDISCARD_DEFAULT; - // int error_concealment = 3; + int lowres; + int idct; + enum AVDiscard skip_frame; + enum AVDiscard skip_idct; + enum AVDiscard skip_loop_filter; + int error_concealment; // int decoder_reorder_pts = -1; int autoexit; // int exit_on_keydown; @@ -75,9 +75,9 @@ typedef struct FFPlayer { // int framedrop = -1; int infinite_buffer; enum ShowMode show_mode; - // const char *audio_codec_name; - // const char *subtitle_codec_name; - // const char *video_codec_name; + char *audio_codec_name; + char *subtitle_codec_name; + char *video_codec_name; // double rdftspeed = 0.02; // int64_t cursor_last_shown; // int cursor_hidden = 0; @@ -85,10 +85,16 @@ typedef struct FFPlayer { // char *vfilters = NULL; #endif + /* current context */ + //static int is_full_screen; + int64_t audio_callback_time; + /* callback */ int (*decode_interrupt_cb)(void *ctx); } FFPlayer; +#define IJKFF_SAFE_FREE(p) do {free(p); p = NULL;} while(0) + inline static void ijkff_reset(FFPlayer *ffp) { /* FIXME: ffplay context reset */ @@ -98,8 +104,7 @@ inline static void ijkff_reset(FFPlayer *ffp) av_dict_free(&ffp->codec_opts); /* ffplay options specified by the user */ - free(ffp->input_filename); - ffp->input_filename = NULL; + IJKFF_SAFE_FREE(ffp->input_filename); ffp->audio_disable = 0; ffp->video_disable = 0; ffp->subtitle_disable = 0; @@ -110,11 +115,25 @@ inline static void ijkff_reset(FFPlayer *ffp) ffp->show_status = 1; ffp->start_time = AV_NOPTS_VALUE; ffp->duration = AV_NOPTS_VALUE; + ffp->workaround_bugs = 1; + ffp->fast = 0; ffp->genpts = 0; + ffp->lowres = 0; + ffp->idct = FF_IDCT_AUTO; + ffp->skip_frame = AVDISCARD_DEFAULT; + ffp->skip_idct = AVDISCARD_DEFAULT; + ffp->skip_loop_filter = AVDISCARD_DEFAULT; + ffp->error_concealment = 3; ffp->autoexit = 0; ffp->loop = 1; ffp->infinite_buffer = -1; ffp->show_mode = SHOW_MODE_NONE; + IJKFF_SAFE_FREE(ffp->audio_codec_name); + IJKFF_SAFE_FREE(ffp->subtitle_codec_name); + IJKFF_SAFE_FREE(ffp->video_codec_name); + + /* current context */ + ffp->audio_callback_time = 0; /* callback */ ffp->decode_interrupt_cb = NULL; diff --git a/ijkmediaplayer/jni/ijkplayer/thread_demux.c b/ijkmediaplayer/jni/ijkplayer/thread_demux.c deleted file mode 100644 index 83454582..00000000 --- a/ijkmediaplayer/jni/ijkplayer/thread_demux.c +++ /dev/null @@ -1,309 +0,0 @@ -/***************************************************************************** - * thread_demux.c - ***************************************************************************** - * - * copyright (c) 2001 Fabrice Bellard - * copyright (c) 2013 Zhang Rui - * - * This file is part of ijkPlayer. - * - * ijkPlayer is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * ijkPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with ijkPlayer; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "ffplayer.h" - -static int read_thread(void *arg) -{ - FFPlayer *ffp = arg; - VideoState *is = &ffp->is; - AVFormatContext *ic = NULL; - int err, i, ret; - int st_index[AVMEDIA_TYPE_NB]; - AVPacket pkt1, *pkt = &pkt1; - int eof = 0; - int pkt_in_play_range = 0; - AVDictionaryEntry *t; - AVDictionary **opts; - int orig_nb_streams; - SDL_mutex *wait_mutex = SDL_CreateMutex(); - - memset(st_index, -1, sizeof(st_index)); - is->last_video_stream = is->video_stream = -1; - is->last_audio_stream = is->audio_stream = -1; - is->last_subtitle_stream = is->subtitle_stream = -1; - - ic = avformat_alloc_context(); - ic->interrupt_callback.callback = ffp->decode_interrupt_cb; - ic->interrupt_callback.opaque = is; - err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts); - if (err < 0) { - print_error(is->filename, err); - ret = -1; - goto fail; - } - if ((t = av_dict_get(ffp->format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { - av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key); - ret = AVERROR_OPTION_NOT_FOUND; - goto fail; - } - is->ic = ic; - - if (ffp->genpts) - ic->flags |= AVFMT_FLAG_GENPTS; - - opts = setup_find_stream_info_opts(ic, ffp->codec_opts); - orig_nb_streams = ic->nb_streams; - - err = avformat_find_stream_info(ic, opts); - if (err < 0) { - fprintf(stderr, "%s: could not find codec parameters\n", is->filename); - ret = -1; - goto fail; - } - for (i = 0; i < orig_nb_streams; i++) - av_dict_free(&opts[i]); - av_freep(&opts); - - if (ic->pb) - ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use url_feof() to test for the end - - if (ffp->seek_by_bytes < 0) - ffp->seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT); - - is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0; - - /* if seeking requested, we execute it */ - if (ffp->start_time != AV_NOPTS_VALUE) { - int64_t timestamp; - - timestamp = ffp->start_time; - /* add the stream start time */ - if (ic->start_time != AV_NOPTS_VALUE) - timestamp += ic->start_time; - ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0); - if (ret < 0) { - fprintf(stderr, "%s: could not seek to position %0.3f\n", - is->filename, (double)timestamp / AV_TIME_BASE); - } - } - - is->realtime = is_realtime(ic); - - for (i = 0; i < ic->nb_streams; i++) - ic->streams[i]->discard = AVDISCARD_ALL; - if (!ffp->video_disable) - st_index[AVMEDIA_TYPE_VIDEO] = - av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, - ffp->wanted_stream[AVMEDIA_TYPE_VIDEO], -1, NULL, 0); - if (!ffp->audio_disable) - st_index[AVMEDIA_TYPE_AUDIO] = - av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, - ffp->wanted_stream[AVMEDIA_TYPE_AUDIO], - st_index[AVMEDIA_TYPE_VIDEO], - NULL, 0); - if (!ffp->video_disable && !ffp->subtitle_disable) - st_index[AVMEDIA_TYPE_SUBTITLE] = - av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, - ffp->wanted_stream[AVMEDIA_TYPE_SUBTITLE], - (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? - st_index[AVMEDIA_TYPE_AUDIO] : - st_index[AVMEDIA_TYPE_VIDEO]), - NULL, 0); - if (ffp->show_status) { - av_dump_format(ic, 0, is->filename, 0); - } - - is->show_mode = ffp->show_mode; - - /* open the streams */ - if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) { - stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]); - } - - ret = -1; - if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) { - ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]); - } - if (is->show_mode == SHOW_MODE_NONE) - is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT; - - if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) { - stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]); - } - - if (is->video_stream < 0 && is->audio_stream < 0) { - fprintf(stderr, "%s: could not open codecs\n", is->filename); - ret = -1; - goto fail; - } - - if (ffp->infinite_buffer < 0 && is->realtime) - ffp->infinite_buffer = 1; - - for (;;) { - if (is->abort_request) - break; - if (is->paused != is->last_paused) { - is->last_paused = is->paused; - if (is->paused) - is->read_pause_return = av_read_pause(ic); - else - av_read_play(ic); - } - - if (is->paused && - (!strcmp(ic->iformat->name, "rtsp") || - (ic->pb && !strncmp(ffp->input_filename, "mmsh:", 5)))) { - /* wait 10 ms to avoid trying to get another packet */ - /* XXX: horrible */ - SDL_Delay(10); - continue; - } - - if (is->seek_req) { - int64_t seek_target = is->seek_pos; - int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN; - int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX; -// FIXME the +-2 is due to rounding being not done in the correct direction in generation -// of the seek_pos/seek_rel variables - - ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags); - if (ret < 0) { - fprintf(stderr, "%s: error while seeking\n", is->ic->filename); - } else { - if (is->audio_stream >= 0) { - packet_queue_flush(&is->audioq); - packet_queue_put(&is->audioq, packet_get_flush_pkt()); - } - if (is->subtitle_stream >= 0) { - packet_queue_flush(&is->subtitleq); - packet_queue_put(&is->subtitleq, packet_get_flush_pkt()); - } - if (is->video_stream >= 0) { - packet_queue_flush(&is->videoq); - packet_queue_put(&is->videoq, packet_get_flush_pkt()); - } - if (is->seek_flags & AVSEEK_FLAG_BYTE) { - update_external_clock_pts(is, NAN); - } else { - update_external_clock_pts(is, seek_target / (double)AV_TIME_BASE); - } - } - is->seek_req = 0; - eof = 0; - if (is->paused) - step_to_next_frame(is); - } - if (is->queue_attachments_req) { - avformat_queue_attached_pictures(ic); - is->queue_attachments_req = 0; - } - - /* if the queue are full, no need to read more */ - if (ffp->infinite_buffer<1 && - (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE - || ( (is->audioq .nb_packets > MIN_FRAMES || is->audio_stream < 0 || is->audioq.abort_request) - && (is->videoq .nb_packets > MIN_FRAMES || is->video_stream < 0 || is->videoq.abort_request) - && (is->subtitleq.nb_packets > MIN_FRAMES || is->subtitle_stream < 0 || is->subtitleq.abort_request)))) { - /* wait 10 ms */ - SDL_LockMutex(wait_mutex); - SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); - SDL_UnlockMutex(wait_mutex); - continue; - } - if (eof) { - if (is->video_stream >= 0) { - av_init_packet(pkt); - pkt->data = NULL; - pkt->size = 0; - pkt->stream_index = is->video_stream; - packet_queue_put(&is->videoq, pkt); - } - if (is->audio_stream >= 0 && - (is->audio_st->codec->codec->capabilities & CODEC_CAP_DELAY)) { - av_init_packet(pkt); - pkt->data = NULL; - pkt->size = 0; - pkt->stream_index = is->audio_stream; - packet_queue_put(&is->audioq, pkt); - } - SDL_Delay(10); - if (is->audioq.size + is->videoq.size + is->subtitleq.size == 0) { - if (ffp->loop != 1 && (!ffp->loop || --ffp->loop)) { - stream_seek(is, ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0, 0, 0); - } else if (ffp->autoexit) { - ret = AVERROR_EOF; - goto fail; - } - } - eof=0; - continue; - } - ret = av_read_frame(ic, pkt); - if (ret < 0) { - if (ret == AVERROR_EOF || url_feof(ic->pb)) - eof = 1; - if (ic->pb && ic->pb->error) - break; - SDL_LockMutex(wait_mutex); - SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); - SDL_UnlockMutex(wait_mutex); - continue; - } - /* check if packet is in play range specified by user, then queue, otherwise discard */ - pkt_in_play_range = ffp->duration == AV_NOPTS_VALUE || - (pkt->pts - ic->streams[pkt->stream_index]->start_time) * - av_q2d(ic->streams[pkt->stream_index]->time_base) - - (double)(ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0) / 1000000 - <= ((double)ffp->duration / 1000000); - if (pkt->stream_index == is->audio_stream && pkt_in_play_range) { - packet_queue_put(&is->audioq, pkt); - } else if (pkt->stream_index == is->video_stream && pkt_in_play_range) { - packet_queue_put(&is->videoq, pkt); - } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) { - packet_queue_put(&is->subtitleq, pkt); - } else { - av_free_packet(pkt); - } - } - /* wait until the end */ - while (!is->abort_request) { - SDL_Delay(100); - } - - ret = 0; - fail: - /* 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); - if (is->ic) { - avformat_close_input(&is->ic); - } - - if (ret != 0) { - SDL_Event event; - - event.type = FF_QUIT_EVENT; - event.user.data1 = is; - SDL_PushEvent(&event); - } - SDL_DestroyMutex(wait_mutex); - return 0; -} -- GitLab