qplayvid: x11grab, use libswscale, multichannel audio, fixes

This commit is contained in:
Hector Martin 2012-04-12 19:51:45 +01:00
parent 1df30ef8c4
commit ba4ff321e0
5 changed files with 172 additions and 58 deletions

View file

@ -33,6 +33,7 @@ find_package(JACK REQUIRED)
find_package(PythonInterp REQUIRED)
find_package(Qt4)
find_package(FFmpeg)
find_package(Swscale)
find_package(OpenGL)
find_package(GLUT)
find_package(ALSA)

View file

@ -63,6 +63,12 @@ endif (NOT WIN32)
${PC_LIBAVCODEC_LIBRARY_DIRS}
)
find_library(AVDEVICE_LIBRARIES NAMES avdevice
HINTS
${PC_LIBAVCODEC_LIBDIR}
${PC_LIBAVCODEC_LIBRARY_DIRS}
)
find_library(AVUTIL_LIBRARIES NAMES avutil
HINTS
${PC_LIBAVCODEC_LIBDIR}
@ -84,6 +90,10 @@ endif (NOT WIN32)
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVUTIL_LIBRARIES})
endif (AVUTIL_LIBRARIES)
if (AVDEVICE_LIBRARIES)
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${AVDEVICE_LIBRARIES})
endif (AVDEVICE_LIBRARIES)
if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
set(FFMPEG_FOUND TRUE)
endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
@ -110,6 +120,7 @@ endif (NOT WIN32)
mark_as_advanced(AVCODEC_LIBRARIES
AVFORMAT_LIBRARIES
AVUTIL_LIBRARIES
AVDEVICE_LIBRARIES
FFMPEG_INCLUDE_DIR
FFMPEG_INCLUDE_DIR_OLD_STYLE)

View file

@ -16,7 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
if(QT4_FOUND AND FFMPEG_FOUND)
if(QT4_FOUND AND FFMPEG_FOUND AND SWSCALE_FOUND)
include(${QT_USE_FILE})
QT4_WRAP_CPP(qplayvid_MOCS qplayvid_gui.h)
@ -24,7 +24,7 @@ if(QT4_FOUND AND FFMPEG_FOUND)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(qplayvid qplayvid.c qplayvid_gui.cpp ${qplayvid_MOCS})
target_link_libraries(qplayvid openlase ${FFMPEG_LIBRARIES} ${QT_LIBRARIES})
target_link_libraries(qplayvid openlase ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES} ${QT_LIBRARIES})
else()
message(STATUS "Will NOT build qplayvid (Qt4 or FFmpeg missing)")
endif()

View file

@ -33,9 +33,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#define OL_FRAMES_BUF 5
#define VIDEO_BUF 64
#define VIDEO_BUF 32
#define SAMPLE_RATE 48000
#define AUDIO_BUF 3
@ -104,10 +106,12 @@ struct PlayerCtx {
int v_buf_put;
VideoFrame *cur_frame;
double last_frame_pts;
struct SwsContext *v_sws_ctx;
int a_stride;
short *a_ibuf;
short *a_rbuf;
int a_ch;
AudioSample *a_buf;
int a_buf_len;
int a_buf_get;
@ -166,6 +170,7 @@ size_t decode_audio(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
for (i = 0; i < out_samples; i++) {
ctx->a_buf[put].l = *rbuf++;
ctx->a_buf[put].r = *rbuf++;
rbuf += ctx->a_ch - 2;
ctx->a_buf[put].seekid = seekid;
ctx->a_buf[put].pts = ctx->a_cur_pts;
put++;
@ -174,7 +179,7 @@ size_t decode_audio(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
put = 0;
}
printf("Put %d audio samples\n", out_samples);
printf("Put %d audio samples at pts %f\n", out_samples, ctx->a_cur_pts);
pthread_mutex_lock(&ctx->a_buf_mutex);
ctx->a_buf_put = put;
@ -204,7 +209,10 @@ size_t decode_video(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
// The pts magic guesswork
int64_t pts = AV_NOPTS_VALUE;
int64_t frame_pts = *(int64_t*)ctx->v_frame->opaque;
int64_t *p_pts = (int64_t*)ctx->v_frame->opaque;
int64_t frame_pts = AV_NOPTS_VALUE;
if (p_pts)
frame_pts = *p_pts;
if (packet->dts != AV_NOPTS_VALUE) {
ctx->v_faulty_dts += packet->dts <= ctx->v_last_dts;
ctx->v_last_dts = packet->dts;
@ -219,6 +227,24 @@ size_t decode_video(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
else
pts = packet->dts;
if (pts == AV_NOPTS_VALUE) {
if (ctx->v_last_pts != AV_NOPTS_VALUE) {
pts = ctx->v_last_pts++;
} else if (ctx->v_last_dts != AV_NOPTS_VALUE) {
pts = ctx->v_last_dts++;
}
}
if (pts == AV_NOPTS_VALUE) {
if (ctx->v_last_pts != AV_NOPTS_VALUE) {
pts = ctx->v_last_pts++;
} else if (ctx->v_last_dts != AV_NOPTS_VALUE) {
pts = ctx->v_last_dts++;
} else {
pts = 0;
}
}
pthread_mutex_lock(&ctx->v_buf_mutex);
while (((ctx->v_buf_put + 1) % ctx->v_buf_len) == ctx->v_buf_get) {
printf("Wait for space in video buffer\n");
@ -240,9 +266,20 @@ size_t decode_video(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
return decoded;
}
if (!ctx->v_sws_ctx) {
ctx->v_sws_ctx = sws_getContext(ctx->width, ctx->height, ctx->v_codec_ctx->pix_fmt,
ctx->width, ctx->height, PIX_FMT_GRAY8, SWS_BICUBIC,
NULL, NULL, NULL);
}
AVPicture pict;
pict.data[0] = frame->data;
pict.linesize[0] = frame->stride;
sws_scale(ctx->v_sws_ctx, ctx->v_frame->data, ctx->v_frame->linesize, 0, ctx->height, pict.data, pict.linesize);
frame->pts = av_q2d(ctx->v_stream->time_base) * pts;
frame->seekid = seekid;
memcpy(frame->data, ctx->v_frame->data[0], frame->data_size);
printf("Put frame %d (pts:%f seekid:%d)\n", ctx->v_buf_put, frame->pts, seekid);
pthread_mutex_lock(&ctx->v_buf_mutex);
@ -256,19 +293,21 @@ size_t decode_video(PlayerCtx *ctx, AVPacket *packet, int new_packet, int32_t se
void push_eof(PlayerCtx *ctx, int32_t seekid)
{
pthread_mutex_lock(&ctx->a_buf_mutex);
while (((ctx->a_buf_put + 1) % ctx->a_buf_len) == ctx->a_buf_get) {
printf("Wait for space in audio buffer\n");
pthread_cond_wait(&ctx->a_buf_not_full, &ctx->a_buf_mutex);
if (ctx->audio_idx != -1) {
pthread_mutex_lock(&ctx->a_buf_mutex);
while (((ctx->a_buf_put + 1) % ctx->a_buf_len) == ctx->a_buf_get) {
printf("Wait for space in audio buffer\n");
pthread_cond_wait(&ctx->a_buf_not_full, &ctx->a_buf_mutex);
}
ctx->a_buf[ctx->a_buf_put].l = 0;
ctx->a_buf[ctx->a_buf_put].r = 0;
ctx->a_buf[ctx->a_buf_put].pts = 0;
ctx->a_buf[ctx->a_buf_put].seekid = -seekid;
if (++ctx->a_buf_put == ctx->a_buf_len)
ctx->a_buf_put = 0;
pthread_cond_signal(&ctx->a_buf_not_empty);
pthread_mutex_unlock(&ctx->a_buf_mutex);
}
ctx->a_buf[ctx->a_buf_put].l = 0;
ctx->a_buf[ctx->a_buf_put].r = 0;
ctx->a_buf[ctx->a_buf_put].pts = 0;
ctx->a_buf[ctx->a_buf_put].seekid = -seekid;
if (++ctx->a_buf_put == ctx->a_buf_len)
ctx->a_buf_put = 0;
pthread_cond_signal(&ctx->a_buf_not_empty);
pthread_mutex_unlock(&ctx->a_buf_mutex);
pthread_mutex_lock(&ctx->v_buf_mutex);
while (((ctx->v_buf_put + 1) % ctx->v_buf_len) == ctx->v_buf_get) {
@ -309,7 +348,8 @@ void *decoder_thread(void *arg)
pthread_mutex_lock(&ctx->v_buf_mutex);
pthread_cond_signal(&ctx->v_buf_not_empty);
pthread_mutex_unlock(&ctx->v_buf_mutex);
avcodec_flush_buffers(ctx->a_codec_ctx);
if (ctx->audio_idx != -1)
avcodec_flush_buffers(ctx->a_codec_ctx);
avcodec_flush_buffers(ctx->v_codec_ctx);
}
if (av_read_frame(ctx->fmt_ctx, &packet) < 0) {
@ -323,7 +363,7 @@ void *decoder_thread(void *arg)
cpacket = packet;
new_packet = 1;
}
if (cpacket.stream_index == ctx->audio_idx) {
if (ctx->audio_idx != -1 && cpacket.stream_index == ctx->audio_idx) {
decoded_bytes = decode_audio(ctx, &cpacket, new_packet, seekid);
} else if (cpacket.stream_index == ctx->video_idx) {
decoded_bytes = decode_video(ctx, &cpacket, new_packet, seekid);
@ -366,11 +406,22 @@ int decoder_init(PlayerCtx *ctx, const char *file)
ctx->cur_seekid = 1;
ctx->a_cur_pts = 0;
if (av_open_input_file(&ctx->fmt_ctx, file, NULL, 0, NULL) != 0)
return -1;
AVInputFormat *format = NULL;
if (!strncmp(file, "x11grab://", 10)) {
printf("Using X11Grab\n");
format = av_find_input_format("x11grab");
file += 10;
}
if (av_find_stream_info(ctx->fmt_ctx) < 0)
if (av_open_input_file(&ctx->fmt_ctx, file, NULL, 0, NULL) != 0) {
printf("Couldn't open input file %s\n", file);
return -1;
}
if (av_find_stream_info(ctx->fmt_ctx) < 0) {
printf("Couldn't get stream info\n");
return -1;
}
ctx->duration = ctx->fmt_ctx->duration/(double)AV_TIME_BASE;
@ -392,28 +443,67 @@ int decoder_init(PlayerCtx *ctx, const char *file)
}
}
if (ctx->video_idx == -1 || ctx->audio_idx == -1)
if (ctx->video_idx == -1) {
printf("No video streams\n");
return -1;
}
if (ctx->audio_idx != -1) {
ctx->a_stream = ctx->fmt_ctx->streams[ctx->audio_idx];
ctx->a_codec_ctx = ctx->a_stream->codec;
ctx->a_codec = avcodec_find_decoder(ctx->a_codec_ctx->codec_id);
if (ctx->a_codec == NULL) {
return -1;
printf("No audio codec\n");
}
if (avcodec_open(ctx->a_codec_ctx, ctx->a_codec) < 0) {
printf("Failed to open audio codec\n");
return -1;
}
printf("Audio srate: %d\n", ctx->a_codec_ctx->sample_rate);
ctx->a_ch = 2;
if (ctx->a_codec_ctx->channels > 2)
ctx->a_ch = ctx->a_codec_ctx->channels;
ctx->a_resampler = av_audio_resample_init(ctx->a_ch, ctx->a_codec_ctx->channels,
SAMPLE_RATE, ctx->a_codec_ctx->sample_rate,
SAMPLE_FMT_S16, ctx->a_codec_ctx->sample_fmt,
16, 10, 0, 0.8);
if (!ctx->a_resampler)
return -1;
ctx->a_ratio = SAMPLE_RATE/(double)ctx->a_codec_ctx->sample_rate;
ctx->a_stride = av_get_bits_per_sample_fmt(ctx->a_codec_ctx->sample_fmt) / 8;
ctx->a_stride *= ctx->a_codec_ctx->channels;
ctx->a_ibuf = malloc(ctx->a_stride * AVCODEC_MAX_AUDIO_FRAME_SIZE);
ctx->a_rbuf = malloc(ctx->a_ch * sizeof(short) * AVCODEC_MAX_AUDIO_FRAME_SIZE * (ctx->a_ratio * 1.1));
ctx->a_buf_len = AUDIO_BUF*SAMPLE_RATE;
ctx->a_buf = malloc(sizeof(*ctx->a_buf) * ctx->a_buf_len);
pthread_mutex_init(&ctx->a_buf_mutex, NULL);
pthread_cond_init(&ctx->a_buf_not_full, NULL);
pthread_cond_init(&ctx->a_buf_not_empty, NULL);
}
ctx->a_stream = ctx->fmt_ctx->streams[ctx->audio_idx];
ctx->v_stream = ctx->fmt_ctx->streams[ctx->video_idx];
ctx->a_codec_ctx = ctx->a_stream->codec;
ctx->v_codec_ctx = ctx->v_stream->codec;
ctx->width = ctx->v_codec_ctx->width;
ctx->height = ctx->v_codec_ctx->height;
ctx->a_codec = avcodec_find_decoder(ctx->a_codec_ctx->codec_id);
if (ctx->a_codec == NULL)
return -1;
ctx->v_codec = avcodec_find_decoder(ctx->v_codec_ctx->codec_id);
if (ctx->v_codec == NULL)
if (ctx->v_codec == NULL) {
printf("No video codec\n");
return -1;
}
if (avcodec_open(ctx->a_codec_ctx, ctx->a_codec) < 0)
return -1;
if (avcodec_open(ctx->v_codec_ctx, ctx->v_codec) < 0)
if (avcodec_open(ctx->v_codec_ctx, ctx->v_codec) < 0) {
printf("Failed to open video codec\n");
return -1;
}
ctx->v_pkt_pts = AV_NOPTS_VALUE;
ctx->v_faulty_pts = ctx->v_faulty_dts = 0;
@ -423,31 +513,9 @@ int decoder_init(PlayerCtx *ctx, const char *file)
ctx->v_codec_ctx->release_buffer = hack_release_buffer;
ctx->v_codec_ctx->opaque = ctx;
printf("Audio srate: %d\n", ctx->a_codec_ctx->sample_rate);
ctx->a_resampler = av_audio_resample_init(2, ctx->a_codec_ctx->channels,
SAMPLE_RATE, ctx->a_codec_ctx->sample_rate,
SAMPLE_FMT_S16, ctx->a_codec_ctx->sample_fmt,
16, 10, 0, 0.8);
if (!ctx->a_resampler)
return -1;
ctx->a_ratio = SAMPLE_RATE/(double)ctx->a_codec_ctx->sample_rate;
ctx->a_stride = av_get_bits_per_sample_fmt(ctx->a_codec_ctx->sample_fmt) / 8;
ctx->a_stride *= ctx->a_codec_ctx->channels;
ctx->a_ibuf = malloc(ctx->a_stride * AVCODEC_MAX_AUDIO_FRAME_SIZE);
ctx->a_rbuf = malloc(2 * sizeof(short) * AVCODEC_MAX_AUDIO_FRAME_SIZE * (ctx->a_ratio * 1.1));
ctx->a_buf_len = AUDIO_BUF*SAMPLE_RATE;
ctx->a_buf = malloc(sizeof(*ctx->a_buf) * ctx->a_buf_len);
ctx->v_frame = avcodec_alloc_frame();
ctx->v_buf_len = VIDEO_BUF;
pthread_mutex_init(&ctx->a_buf_mutex, NULL);
pthread_cond_init(&ctx->a_buf_not_full, NULL);
pthread_cond_init(&ctx->a_buf_not_empty, NULL);
pthread_mutex_init(&ctx->v_buf_mutex, NULL);
pthread_cond_init(&ctx->v_buf_not_full, NULL);
pthread_cond_init(&ctx->v_buf_not_empty, NULL);
@ -464,7 +532,8 @@ void drop_audio(PlayerCtx *ctx, int by_pts)
{
if (!ctx->cur_frame)
return;
if (ctx->audio_idx == -1)
return;
while (1) {
pthread_mutex_lock(&ctx->a_buf_mutex);
int get = ctx->a_buf_get;
@ -497,6 +566,25 @@ void drop_audio(PlayerCtx *ctx, int by_pts)
}
}
void drop_all_video(PlayerCtx *ctx)
{
if (ctx->cur_frame && ctx->cur_frame->seekid == -ctx->cur_seekid) {
printf("No more video (EOF)\n");
return 0;
}
pthread_mutex_lock(&ctx->v_buf_mutex);
int last = (ctx->v_buf_put + ctx->v_buf_len - 1) % ctx->v_buf_len;
while (ctx->v_buf_get != ctx->v_buf_put) {
if (ctx->v_buf_get == last)
break;
ctx->v_buf_get++;
if (ctx->v_buf_get == ctx->v_buf_len)
ctx->v_buf_get = 0;
}
pthread_cond_signal(&ctx->v_buf_not_full);
pthread_mutex_unlock(&ctx->v_buf_mutex);
}
int next_video_frame(PlayerCtx *ctx)
{
if (ctx->cur_frame && ctx->cur_frame->seekid == -ctx->cur_seekid) {
@ -531,6 +619,12 @@ void get_audio(float *lb, float *rb, int samples)
{
PlayerCtx *ctx = g_ctx;
if (ctx->audio_idx == -1) {
memset(lb, 0, samples * sizeof(*lb));
memset(rb, 0, samples * sizeof(*rb));
return;
}
pthread_mutex_lock(&ctx->display_mode_mutex);
DisplayMode display_mode = ctx->display_mode;
pthread_mutex_unlock(&ctx->display_mode_mutex);
@ -722,6 +816,11 @@ void *display_thread(void *arg)
ctx->settings_changed = 0;
pthread_mutex_unlock(&ctx->settings_mutex);
if (ctx->audio_idx == -1) {
drop_all_video(ctx);
next_video_frame(ctx);
}
params.min_length = settings.minsize;
params.end_dwell = params.start_dwell = settings.dwell;
params.off_speed = settings.offspeed * 0.002;
@ -866,6 +965,7 @@ int player_init(PlayerCtx *ctx)
void playvid_init(void)
{
av_register_all();
avdevice_register_all();
}
int playvid_open(PlayerCtx **octx, const char *filename)

View file

@ -232,7 +232,9 @@ void PlayerUI::open(const char *filename)
qDebug() << "open:" << filename;
this->filename = filename;
loadSettings();
Q_ASSERT(playvid_open(&player, filename) == 0);
if(playvid_open(&player, filename)) {
qDebug() << "open failed";
}
playvid_set_eventcb(player, player_event_cb);
playvid_update_settings(player, &this->settings);
sl_time->setMaximum((int)(1000 * playvid_get_duration(player)));