--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<title>视频</title>
+</head>
+
+<body>
+<h1>这是一个视频</h1>
+
+<video width="640" height="360">
+<source src="/home/yu/Videos/big_buck_bunny_720p_h264.mov" type="video/mp4">
+</video>
+
+</body>
+</html>
CFILES += ../util/scf_string.c
-CFILES += main.c
+#CFILES += main_img.c
+CFILES += main_player.c
+CFILES += abc_avio.c
+CFILES += abc_filter.c
CFILES += abc_ffmpeg_img.c
+CFILES += abc_ffmpeg_input.c
+CFILES += abc_ffmpeg_output.c
+CFILES += abc_ffmpeg_alsa.c
+CFILES += abc_vout_fifo.c
CFLAGS += -g
CFLAGS += -I../util
-LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil
+LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil -lavdevice
LDFLAGS += -lcairo
-LDFLAGS += -ldl
+LDFLAGS += -ldl -pthread
all:
gcc $(CFLAGS) $(CFILES) $(LDFLAGS)
--- /dev/null
+#include"abc_ffmpeg.h"
+
+extern abc_avio_ops_t abc_avio_ffmpeg_input;
+extern abc_avio_ops_t abc_avio_ffmpeg_output;
+extern abc_avio_ops_t abc_avio_ffmpeg_alsa;
+extern abc_avio_ops_t abc_avio_vout_fifo;
+
+static abc_avio_ops_t* avio_array[] =
+{
+ &abc_avio_ffmpeg_input,
+ &abc_avio_ffmpeg_output,
+ &abc_avio_ffmpeg_alsa,
+ &abc_avio_vout_fifo,
+ NULL,
+};
+
+abc_frame_t* abc_frame_alloc()
+{
+ abc_frame_t* f = calloc(1, sizeof(abc_frame_t));
+ if (!f)
+ return NULL;
+
+ f->frame = av_frame_alloc();
+ if (!f->frame) {
+ free(f);
+ return NULL;
+ }
+
+ return f;
+}
+
+void abc_frame_free (abc_frame_t* f)
+{
+ if (f) {
+ if (f->frame)
+ av_frame_free(&f->frame);
+
+ free(f);
+ }
+}
+
+int abc_avio_open(abc_avio_t** pio, const char* type, const char* in, const char* out)
+{
+ if (!pio || !type || (!in && !out))
+ return -EINVAL;
+
+ abc_avio_ops_t* ops = NULL;
+ abc_avio_t* io = NULL;
+
+ int i;
+ for (i = 0; avio_array[i]; i++) {
+
+ if (!strcmp(avio_array[i]->type, type)) {
+ ops = avio_array[i];
+ break;
+ }
+ }
+
+ if (!ops)
+ return -EINVAL;
+
+ io = calloc(1, sizeof(abc_avio_t));
+ if (!io)
+ return -ENOMEM;
+
+ scf_list_init(&io->vin);
+ scf_list_init(&io->ain);
+ scf_list_init(&io->vout);
+ scf_list_init(&io->aout);
+
+ pthread_mutex_init(&io->mutex, NULL);
+ pthread_cond_init (&io->cond , NULL);
+
+ io->tid = -1;
+ io->width = -1;
+ io->height = -1;
+ io->channels = -1;
+
+ io->ops = ops;
+ if (ops->open) {
+
+ int ret = ops->open(io, in, out);
+ if (ret < 0) {
+ free(io);
+ return ret;
+ }
+ }
+
+ *pio = io;
+ return 0;
+}
+
+int abc_avio_close(abc_avio_t* io)
+{
+ if (io) {
+ if (io->ops && io->ops->close)
+ io->ops->close(io);
+
+ scf_list_clear(&io->vin, abc_frame_t, list, abc_frame_free);
+ scf_list_clear(&io->ain, abc_frame_t, list, abc_frame_free);
+ scf_list_clear(&io->vout, abc_frame_t, list, abc_frame_free);
+ scf_list_clear(&io->aout, abc_frame_t, list, abc_frame_free);
+
+ free(io);
+ }
+ return 0;
+}
+
+int abc_avio_run(abc_avio_t* io)
+{
+ if (io && io->ops && io->ops->run)
+ return io->ops->run(io);
+
+ return -EINVAL;
+}
+
+int abc_avio_stop(abc_avio_t* io)
+{
+ if (io && io->ops && io->ops->stop)
+ return io->ops->stop(io);
+
+ return -EINVAL;
+}
#ifndef ABC_FFMPEG_H
#define ABC_FFMPEG_H
-#include"scf_def.h"
+#include"scf_list.h"
+#include"scf_vector.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
+#include "libavdevice/avdevice.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
+#include "libavutil/audio_fifo.h"
+
+typedef struct abc_avio_s abc_avio_t;
+typedef struct abc_avio_ops_s abc_avio_ops_t;
+
+typedef struct abc_frame_s abc_frame_t;
+
+typedef struct abc_filter_s abc_filter_t;
+
+struct abc_frame_s
+{
+ scf_list_t list;
+ AVFrame* frame;
+};
typedef struct {
- AVFormatContext* fmt_ctx;
- AVCodecContext* dec_ctx;
- int video_index;
+ AVFormatContext* fmt_ctx;
+ AVCodecContext* dec_ctx;
+ int video_index;
- AVFilterContext* buffersrc_ctx;
- AVFilterContext* buffersink_ctx;
- AVFilterGraph* filter_graph;
+ AVFilterContext* buffersrc_ctx;
+ AVFilterContext* buffersink_ctx;
+ AVFilterGraph* filter_graph;
- int width;
- int height;
+ int width;
+ int height;
} abc_img_t;
+typedef struct {
+ AVFormatContext* fmt_ctx;
+ AVCodecContext* vcodec_ctx;
+ AVCodecContext* acodec_ctx;
+
+ AVAudioFifo* afifo;
+
+ int vidx;
+ int aidx;
+
+ int nb_samples;
+
+ AVFrame* vframe;
+ AVFrame* aframe;
+ AVPacket* vpkt;
+ AVPacket* apkt;
+
+} abc_ffmpeg_t;
+
+typedef struct {
+ AVFormatContext* ctx_in;
+ AVFormatContext* ctx_out;
+ AVCodecContext* codec_in;
+ AVCodecContext* codec_out;
+
+ AVAudioFifo* afifo;
+
+ AVFilterContext* obuffersrc_ctx;
+ AVFilterContext* obuffersink_ctx;
+ AVFilterGraph* ograph;
+
+ AVFrame* iframe;
+ AVFrame* oframe;
+ AVFrame* fframe;
+
+ AVPacket* ipkt;
+ AVPacket* opkt;
+
+ int64_t prev_out;
+ int64_t diff_out;
+
+ int idx_in;
+ int idx_out;
+
+ int nb_samples;
+} abc_audio_t;
+
+struct abc_avio_s
+{
+ int refs;
+
+ int nb_vframes;
+ scf_list_t vin;
+ scf_list_t ain;
+
+ scf_list_t vout;
+ scf_list_t aout;
+
+ int64_t start_time;
+ AVRational speed;
+
+ int x;
+ int y;
+ int width;
+ int height;
+ enum AVPixelFormat pix_fmt;
+ AVRational frame_rate;
+ AVRational sample_aspect_ratio;
+ int vopen;
+
+ enum AVSampleFormat sample_fmt;
+ AVRational sample_rate;
+ int channels;
+ int aopen;
+ double volume;
+
+ AVFilterContext* abuffersrc_ctx;
+ AVFilterContext* vbuffersrc_ctx;
+
+ int error;
+ int exit;
+ int flush;
+ pthread_t tid;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ abc_avio_ops_t* ops;
+ void* priv;
+};
+
+struct abc_filter_s
+{
+ scf_vector_t* inputs;
+ scf_vector_t* outputs;
+
+ AVFilterGraph* vgraph;
+ AVFilterGraph* agraph;
+ AVFilterContext* abuffersink_ctx;
+ AVFilterContext* vbuffersink_ctx;
+
+ AVFrame* vframe;
+ AVFrame* aframe;
+
+ AVRational speed;
+
+ int error;
+ int exit;
+ int flush;
+ pthread_t tid;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
+struct abc_avio_ops_s
+{
+ const char* type;
+
+ int (*open )(abc_avio_t* io, const char* in, const char* out);
+ int (*close)(abc_avio_t* io);
+ int (*run )(abc_avio_t* io);
+ int (*stop )(abc_avio_t* io);
+};
+
+abc_frame_t* abc_frame_alloc();
+void abc_frame_free (abc_frame_t* f);
int abc_img_open (abc_img_t** pimg, const char* path);
void abc_img_close(abc_img_t* img);
-
-// ffmpeg BGRA == cario ARGB, Blue in byte[0].
int abc_img_read (abc_img_t* img, uint8_t* bgra, int size);
+// ffmpeg BGRA == cario ARGB, Blue in byte[0].
+
+int abc_avio_open (abc_avio_t** pio, const char* type, const char* in, const char* out);
+int abc_avio_close (abc_avio_t* io);
+int abc_avio_run (abc_avio_t* io);
+int abc_avio_stop (abc_avio_t* io);
+
+int abc_filter_open (abc_filter_t** pf);
+int abc_filter_close(abc_filter_t* f, int flush);
+int abc_filter_run (abc_filter_t* f);
+
+int abc_filter_add_input (abc_filter_t* f, abc_avio_t* input);
+int abc_filter_add_output(abc_filter_t* f, abc_avio_t* output);
#endif
--- /dev/null
+#include"abc_ffmpeg.h"
+#include"scf_def.h"
+
+static int _output_filter_init(abc_audio_t* priv)
+{
+ char args[512];
+ int ret = 0;
+
+ const AVFilter *abuffersrc = avfilter_get_by_name("abuffer");
+ const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
+
+ AVFilterInOut *outputs = NULL;
+ AVFilterInOut *inputs = NULL;
+ const char *afilters_descr = "[in]aresample=44100[out]";
+
+ priv->ograph = avfilter_graph_alloc();
+ if (!priv->ograph) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret = snprintf(args, sizeof(args),
+ "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=",
+ 1, 44100, 44100,
+ av_get_sample_fmt_name(AV_SAMPLE_FMT_FLTP));
+ av_channel_layout_describe(&(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO, args + ret, sizeof(args) - ret);
+
+ printf("%s\n", args);
+
+ ret = avfilter_graph_create_filter(&priv->obuffersrc_ctx, abuffersrc, "in", args, NULL, priv->ograph);
+ if (ret < 0)
+ goto end;
+
+ outputs = avfilter_inout_alloc();
+ if (!outputs) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = priv->obuffersrc_ctx;
+ outputs->pad_idx = 0;
+ outputs->next = NULL;
+
+ const enum AVSampleFormat sample_fmts[] = {priv->codec_out->sample_fmt, -1};
+ const int sample_rates[] = {priv->codec_out->sample_rate, -1};
+
+ ret = avfilter_graph_create_filter(&priv->obuffersink_ctx, abuffersink, "out", NULL, NULL, priv->ograph);
+ if (ret < 0)
+ goto end;
+
+ inputs = avfilter_inout_alloc();
+ if (!inputs) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = priv->obuffersink_ctx;
+ inputs->pad_idx = 0;
+ inputs->next = NULL;
+
+ ret = av_opt_set_int_list(priv->obuffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ ret = av_opt_set(priv->obuffersink_ctx, "ch_layouts", "stereo", AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ ret = av_opt_set_int_list(priv->obuffersink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ if ((ret = avfilter_graph_parse_ptr(priv->ograph, afilters_descr, &inputs, &outputs, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+ if ((ret = avfilter_graph_config(priv->ograph, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+end:
+ avfilter_inout_free(&inputs);
+ avfilter_inout_free(&outputs);
+ return ret;
+}
+
+static int _audio_input_init(abc_audio_t* priv, const char* path)
+{
+ const AVInputFormat* in = av_find_input_format("alsa");
+ if (!in)
+ return -EINVAL;
+
+ priv->ctx_in = avformat_alloc_context();
+ if (!priv->ctx_in)
+ return -ENOMEM;
+
+// priv->ctx_in->flags |= AVFMT_FLAG_NONBLOCK;
+
+ int ret = avformat_open_input(&priv->ctx_in, path, in, NULL);
+ if (ret < 0) {
+ scf_loge("ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ ret = avformat_find_stream_info(priv->ctx_in, NULL);
+ if (ret < 0) {
+ scf_loge("avformat_find_stream_info error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ AVStream* s;
+ int i;
+ for (i = 0; i < priv->ctx_in->nb_streams; i++) {
+ s = priv->ctx_in->streams[i];
+
+ if (AVMEDIA_TYPE_AUDIO == s->codecpar->codec_type) {
+ priv->idx_in = i;
+ break;
+ }
+ }
+
+ if (-1 == priv->idx_in)
+ return -EINVAL;
+
+ const AVCodec* c = avcodec_find_decoder(s->codecpar->codec_id);
+ if (!c) {
+ scf_loge("decoder not found, codec_id: %d\n", s->codecpar->codec_id);
+ return -EINVAL;
+ }
+
+ priv->codec_in = avcodec_alloc_context3(c);
+ if (!priv->codec_in)
+ return -ENOMEM;
+
+ ret = avcodec_parameters_to_context(priv->codec_in, s->codecpar);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_open2(priv->codec_in, c, NULL);
+ if (ret < 0) {
+ printf("avcodec_parameters_to_context error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int _audio_output_init(abc_audio_t* priv, const char* path)
+{
+ const AVOutputFormat* out = av_guess_format("alsa", NULL, NULL);
+
+ scf_logi("out: %p, path: %s\n", out, path);
+
+ avformat_alloc_output_context2(&priv->ctx_out, out, NULL, path);
+ if (!priv->ctx_out) {
+ scf_loge("\n");
+ return -ENOMEM;
+ }
+
+ priv->ctx_out->flags |= AVFMT_FLAG_NONBLOCK;
+
+ const AVCodec* c = avcodec_find_encoder(out->audio_codec);
+ if (!c)
+ return -EINVAL;
+
+ AVStream* s = avformat_new_stream(priv->ctx_out, c);
+ if (!s)
+ return -ENOMEM;
+
+ s->id = priv->ctx_out->nb_streams - 1;
+ priv->idx_out = s->id;
+
+ priv->codec_out = avcodec_alloc_context3(c);
+ if (!priv->codec_out)
+ return -ENOMEM;
+
+ priv->codec_out->codec_id = c->id;
+ priv->codec_out->sample_rate = 44100;
+ priv->codec_out->sample_fmt = c->sample_fmts[0];
+ priv->codec_out->time_base = (AVRational){1, priv->codec_out->sample_rate};
+ s->time_base = priv->codec_out->time_base;
+
+ av_channel_layout_copy(&priv->codec_out->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
+
+ if (priv->ctx_out->oformat->flags & AVFMT_GLOBALHEADER)
+ priv->codec_out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+ int ret = avcodec_open2(priv->codec_out, c, NULL);
+ if (ret < 0) {
+ scf_loge("avcodec_open2 error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ ret = avcodec_parameters_from_context(s->codecpar, priv->codec_out);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_from_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ ret = _output_filter_init(priv);
+ if (ret < 0) {
+ scf_loge("output filter init error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ priv->afifo = av_audio_fifo_alloc(priv->codec_out->sample_fmt, 2, 1024);
+ if (!priv->afifo)
+ return -ENOMEM;
+
+ priv->fframe = av_frame_alloc();
+ if (!priv->fframe)
+ return -ENOMEM;
+
+ priv->opkt = av_packet_alloc();
+ if (!priv->opkt)
+ return -ENOMEM;
+
+ priv->oframe = av_frame_alloc();
+ if (!priv->oframe)
+ return -ENOMEM;
+
+ priv->oframe->format = priv->codec_out->sample_fmt;
+ priv->oframe->nb_samples = 1024;
+ av_channel_layout_default(&priv->oframe->ch_layout, 2);
+
+ if (av_frame_get_buffer(priv->oframe, 1) < 0)
+ return -ENOMEM;
+
+ priv->diff_out = 1000000LL * 1024 / priv->codec_out->sample_rate;
+ return 0;
+}
+
+void abc_audio_free(abc_audio_t* priv)
+{
+ if (priv) {
+ if (priv->ctx_in)
+ avformat_close_input(&priv->ctx_in);
+
+ if (priv->ctx_out)
+ avformat_free_context(priv->ctx_out);
+
+ if (priv->codec_in)
+ avcodec_free_context(&priv->codec_in);
+
+ if (priv->codec_out)
+ avcodec_free_context(&priv->codec_out);
+
+ if (priv->ograph)
+ avfilter_graph_free(&priv->ograph);
+
+ if (priv->afifo)
+ av_audio_fifo_free(priv->afifo);
+
+ if (priv->iframe)
+ av_frame_free(&priv->iframe);
+
+ if (priv->oframe)
+ av_frame_free(&priv->oframe);
+
+ if (priv->fframe)
+ av_frame_free(&priv->fframe);
+
+ if (priv->ipkt)
+ av_packet_free(&priv->ipkt);
+
+ if (priv->opkt)
+ av_packet_free(&priv->opkt);
+
+ free(priv);
+ }
+}
+
+static int _ffmpeg_alsa_open(abc_avio_t* io, const char* in, const char* out)
+{
+ abc_audio_t* priv;
+ AVStream* s;
+
+ int ret;
+ int i;
+
+ priv = calloc(1, sizeof(abc_audio_t));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->idx_in = -1;
+ priv->idx_out = -1;
+
+ if (in) {
+ scf_logi("in: %s\n", in);
+ ret = _audio_input_init(priv, in);
+ if (ret < 0)
+ goto error;
+ }
+
+ if (out) {
+ scf_logi("out: %s\n", out);
+ ret = _audio_output_init(priv, out);
+ if (ret < 0)
+ goto error;
+ }
+
+ io->aopen = 1;
+ io->priv = priv;
+ return 0;
+
+error:
+ abc_audio_free(priv);
+ return ret;
+}
+
+static int _ffmpeg_alsa_close(abc_avio_t* io)
+{
+ abc_audio_t* priv;
+
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+
+ priv = io->priv;
+
+ abc_audio_free(priv);
+ }
+
+ return 0;
+}
+
+static int __output_audio(abc_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVFrame* frame)
+{
+ abc_audio_t* priv = io->priv;
+ abc_frame_t* f = NULL;
+ scf_list_t* l = NULL;
+ AVStream* s = priv->ctx_out->streams[priv->idx_out];
+
+ int ret = 0;
+
+ pthread_mutex_lock(&io->mutex);
+
+ if (!scf_list_empty(&io->aout)) {
+
+ l = scf_list_head(&io->aout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+ pthread_mutex_unlock(&io->mutex);
+
+ scf_logd("priv->aidx: %d, frame->pts: %ld, frame->nb_samples: %d, afifo->size: %d\n",
+ priv->aidx, f->frame->pts, f->frame->nb_samples, av_audio_fifo_size(priv->afifo));
+
+ ret = av_buffersrc_add_frame_flags(priv->obuffersrc_ctx, f->frame, AV_BUFFERSRC_FLAG_KEEP_REF);
+ abc_frame_free(f);
+ f = NULL;
+ if (ret < 0)
+ return ret;
+
+ while (1) {
+ ret = av_buffersink_get_frame(priv->obuffersink_ctx, priv->fframe);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("av_buffersink_get_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ ret = av_audio_fifo_write(priv->afifo, (void**)priv->fframe->data, priv->fframe->nb_samples);
+ av_frame_unref(priv->fframe);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ } else {
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ if (av_audio_fifo_size(priv->afifo) >= 1024) {
+
+ ret = av_audio_fifo_read(priv->afifo, (void**)frame->data, 1024);
+ if (ret < 0)
+ return ret;
+
+ if (1024 != ret)
+ return -1;
+
+ priv ->nb_samples += frame->nb_samples;
+ frame->pts = priv ->nb_samples;
+
+ scf_logd("frame->pts: %ld\n", frame->pts);
+
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ scf_loge("avcodec_send_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, pkt);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("avcodec_receive_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ pkt->stream_index = priv->idx_out;
+ pkt->pts = pkt->pts * av_q2d(c->time_base) / av_q2d(s->time_base);
+ pkt->dts = pkt->dts * av_q2d(c->time_base) / av_q2d(s->time_base);
+
+ int64_t cur = gettime();
+
+ if (priv->prev_out <= 0)
+ priv->prev_out = cur;
+
+ int diff = priv->prev_out + priv->diff_out - cur;
+ if (diff > 0)
+ usleep(diff);
+
+ priv->prev_out = cur;
+
+ ret = av_write_frame(priv->ctx_out, pkt);
+
+ scf_logd("idx: %d, pkt->stream_index: %d, pkt->pts: %ld, c->time_base: %d:%d, s->time_base: %d:%d, diff: %d, %ld\n",
+ priv->idx_out, pkt->stream_index, pkt->pts, c->time_base.num, c->time_base.den, s->time_base.num, s->time_base.den,
+ diff, priv->diff_out);
+
+ av_packet_unref(pkt);
+
+ if (ret < 0) {
+ scf_loge("av_write_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __input_audio(abc_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVFrame* frame, scf_list_t* h)
+{
+ abc_audio_t* priv = io->priv;
+ abc_frame_t* f;
+ AVStream* s;
+
+ int ret = av_read_frame(priv->ctx_in, pkt);
+ if (ret < 0) {
+ scf_loge("av_read_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ if (pkt->stream_index != priv->idx_in)
+ return 0;
+
+ ret = avcodec_send_packet(c, pkt);
+ if (ret < 0) {
+ scf_loge("avcodec_send_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(c, frame);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ break;
+ else if (ret < 0) {
+ scf_loge("avcodec_receive_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ frame->pts = frame->best_effort_timestamp;
+
+ s = priv->ctx_in->streams[pkt->stream_index];
+
+ f = calloc(1, sizeof(abc_frame_t));
+ if (!f)
+ return -ENOMEM;
+
+ f->frame = av_frame_clone(frame);
+ if (!f->frame)
+ return -ENOMEM;
+
+ scf_list_add_tail(h, &f->list);
+
+ int64_t pts = f->frame->pts;
+
+ f->frame->pts = f->frame->pts * av_q2d(s->time_base) / av_q2d(io->sample_rate);
+
+ scf_logd("f->frame->pts: %ld, pts: %ld, stream_index: %d, io->sample_rate: %d:%d, s->time_base: %d:%d\n",
+ f->frame->pts, pts, pkt->stream_index,
+ io->sample_rate.num, io->sample_rate.den,
+ s->time_base.num, s->time_base.den);
+ }
+
+ return 0;
+}
+
+static void* __ffmpeg_alsa_run(void* arg)
+{
+ abc_avio_t* io = arg;
+ abc_audio_t* priv = io->priv;
+ scf_list_t ih;
+
+ int ret;
+
+ scf_list_init(&ih);
+
+ if (priv->ctx_out) {
+ av_dump_format(priv->ctx_out, 0, priv->ctx_out->url, 1);
+
+ if (!(priv->ctx_out->oformat->flags & AVFMT_NOFILE)) {
+
+ ret = avio_open(&priv->ctx_out->pb, priv->ctx_out->url, AVIO_FLAG_WRITE);
+ if (ret < 0) {
+ scf_loge("Could not open output file '%s'", priv->ctx_out->url);
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ ret = avformat_write_header(priv->ctx_out, NULL);
+ if (ret < 0) {
+ scf_loge("avformat_write_header error, ret: %s\n", av_err2str(ret));
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ while (!io->exit) {
+
+ if (priv->ctx_out) {
+ ret = __output_audio(io, priv->codec_out, priv->opkt, priv->oframe);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ if (priv->ctx_in) {
+ ret = __input_audio(io, priv->codec_in, priv->ipkt, priv->iframe, &ih);
+
+ av_packet_unref(priv->ipkt);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ // AEC: audio echo cancel
+
+ pthread_mutex_lock(&io->mutex);
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+end:
+ pthread_mutex_lock(&io->mutex);
+ io->tid = -1;
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+
+ return NULL;
+}
+
+static int _ffmpeg_alsa_stop(abc_avio_t* io)
+{
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static int _ffmpeg_alsa_run(abc_avio_t* io)
+{
+ if (pthread_create(&io->tid, NULL, __ffmpeg_alsa_run, io)) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+abc_avio_ops_t abc_avio_ffmpeg_alsa =
+{
+ .type = "ffmpeg_alsa",
+ .open = _ffmpeg_alsa_open,
+ .close = _ffmpeg_alsa_close,
+ .run = _ffmpeg_alsa_run,
+ .stop = _ffmpeg_alsa_stop,
+};
}
AVStream* s = NULL;
- AVCodec* c = NULL;
-
int i;
for (i = 0; i < img->fmt_ctx->nb_streams; i++) {
s = img->fmt_ctx->streams[i];
return -1;
}
- c = avcodec_find_decoder(s->codecpar->codec_id);
+ const AVCodec* c = avcodec_find_decoder(s->codecpar->codec_id);
if (!c) {
scf_loge("decoder not found, codec_id: %d\n", s->codecpar->codec_id);
return -EINVAL;
--- /dev/null
+#include"abc_ffmpeg.h"
+#include"scf_def.h"
+
+static int _video_init(abc_ffmpeg_t* priv)
+{
+ AVStream* s = priv->fmt_ctx->streams[priv->vidx];
+
+ const AVCodec* c = avcodec_find_decoder(s->codecpar->codec_id);
+ if (!c) {
+ scf_loge("decoder not found, codec_id: %d\n", s->codecpar->codec_id);
+ return -1;
+ }
+
+ priv->vcodec_ctx = avcodec_alloc_context3(c);
+ if (!priv->vcodec_ctx)
+ return -1;
+
+ int ret = avcodec_parameters_to_context(priv->vcodec_ctx, s->codecpar);
+ if (ret < 0)
+ return -1;
+
+ ret = avcodec_open2(priv->vcodec_ctx, c, NULL);
+ if (ret < 0) {
+ printf("avcodec_parameters_to_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _audio_init(abc_ffmpeg_t* priv)
+{
+ AVStream* s = priv->fmt_ctx->streams[priv->aidx];
+
+ const AVCodec* c = avcodec_find_decoder(s->codecpar->codec_id);
+ if (!c) {
+ scf_loge("decoder not found, codec_id: %d\n", s->codecpar->codec_id);
+ return -1;
+ }
+
+ priv->acodec_ctx = avcodec_alloc_context3(c);
+ if (!priv->acodec_ctx)
+ return -1;
+
+ int ret = avcodec_parameters_to_context(priv->acodec_ctx, s->codecpar);
+ if (ret < 0)
+ return -1;
+
+ ret = avcodec_open2(priv->acodec_ctx, c, NULL);
+ if (ret < 0) {
+ printf("avcodec_parameters_to_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _ffmpeg_input_open(abc_avio_t* io, const char* in, const char* out)
+{
+ abc_ffmpeg_t* priv;
+ AVStream* s;
+
+ int ret;
+ int i;
+
+ if (!in)
+ return -EINVAL;
+
+ priv = calloc(1, sizeof(abc_ffmpeg_t));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->vidx = -1;
+ priv->aidx = -1;
+
+ priv->vframe = av_frame_alloc();
+ if (!priv->vframe) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ priv->aframe = av_frame_alloc();
+ if (!priv->aframe) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = avformat_open_input(&priv->fmt_ctx, in, NULL, NULL);
+ if (ret < 0) {
+ scf_loge("ret: %s\n", av_err2str(ret));
+ goto error;
+ }
+
+ ret = avformat_find_stream_info(priv->fmt_ctx, NULL);
+ if (ret < 0) {
+ scf_loge("avformat_find_stream_info error, ret: %s\n", av_err2str(ret));
+ goto error;
+ }
+
+ for (i = 0; i < priv->fmt_ctx->nb_streams; i++) {
+ s = priv->fmt_ctx->streams[i];
+
+ if (AVMEDIA_TYPE_VIDEO == s->codecpar->codec_type)
+ priv->vidx = i;
+
+ else if (AVMEDIA_TYPE_AUDIO == s->codecpar->codec_type)
+ priv->aidx = i;
+
+ if (-1 != priv->vidx && -1 != priv->aidx)
+ break;
+ }
+
+ if (-1 == priv->vidx && -1 == priv->aidx) {
+ scf_loge("no stream found\n");
+
+ ret = -1;
+ goto error;
+ }
+
+ if (priv->vidx >= 0) {
+ s = priv->fmt_ctx->streams[priv->vidx];
+
+ ret = _video_init(priv);
+ if (ret < 0)
+ goto error;
+
+ io->width = priv->vcodec_ctx->width;
+ io->height = priv->vcodec_ctx->height;
+ io->pix_fmt = priv->vcodec_ctx->pix_fmt;
+ io->frame_rate.num = s->r_frame_rate.den;
+ io->frame_rate.den = s->r_frame_rate.num;
+ io->sample_aspect_ratio = priv->vcodec_ctx->sample_aspect_ratio;
+
+ io->x = 0;
+ io->y = 0;
+ io->vopen = 1;
+
+ scf_logi("io->frame_rate: %d:%d\n", io->frame_rate.num, io->frame_rate.den);
+ }
+
+ if (priv->aidx >= 0) {
+ ret = _audio_init(priv);
+ if (ret < 0)
+ goto error;
+
+ io->sample_fmt = priv->acodec_ctx->sample_fmt;
+ io->sample_rate = priv->acodec_ctx->time_base;
+ io->channels = priv->acodec_ctx->ch_layout.nb_channels;
+ io->aopen = 1;
+
+ scf_logi("io->sample_rate: %d:%d\n", io->sample_rate.num, io->sample_rate.den);
+ }
+
+ io->priv = priv;
+ return 0;
+
+error:
+ if (priv->fmt_ctx)
+ avformat_close_input(&priv->fmt_ctx);
+
+ if (priv->vcodec_ctx)
+ avcodec_free_context(&priv->vcodec_ctx);
+
+ if (priv->acodec_ctx)
+ avcodec_free_context(&priv->acodec_ctx);
+
+ if (priv->vframe)
+ av_frame_free(&priv->vframe);
+
+ if (priv->aframe)
+ av_frame_free(&priv->aframe);
+
+ free(priv);
+ return ret;
+}
+
+static int _ffmpeg_input_close(abc_avio_t* io)
+{
+ abc_ffmpeg_t* priv;
+
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+
+ priv = io->priv;
+
+ if (priv) {
+ if (priv->fmt_ctx)
+ avformat_close_input(&priv->fmt_ctx);
+
+ if (priv->vcodec_ctx)
+ avcodec_free_context(&priv->vcodec_ctx);
+
+ if (priv->acodec_ctx)
+ avcodec_free_context(&priv->acodec_ctx);
+
+ if (priv->vframe)
+ av_frame_free(&priv->vframe);
+
+ if (priv->aframe)
+ av_frame_free(&priv->aframe);
+
+ free(priv);
+ }
+ }
+
+ return 0;
+}
+
+static int __ffmpeg_decode(abc_avio_t* io, AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, scf_list_t* h, int* nb_vframes)
+{
+ abc_ffmpeg_t* priv = io->priv;
+ abc_frame_t* f;
+ AVStream* s;
+
+ int ret = avcodec_send_packet(dec_ctx, pkt);
+ if (ret < 0) {
+ scf_loge("avcodec_send_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(dec_ctx, frame);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ break;
+ else if (ret < 0) {
+ scf_loge("avcodec_receive_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ frame->pts = frame->best_effort_timestamp;
+
+ s = priv->fmt_ctx->streams[pkt->stream_index];
+
+ f = calloc(1, sizeof(abc_frame_t));
+ if (!f)
+ return -ENOMEM;
+
+ f->frame = av_frame_clone(frame);
+ if (!f->frame)
+ return -ENOMEM;
+
+ pthread_mutex_lock(&io->mutex);
+ scf_list_add_tail(h, &f->list);
+
+ if (nb_vframes) {
+ (*nb_vframes)++;
+
+ int64_t pts = f->frame->pts;
+
+ f->frame->pts = f->frame->pts * av_q2d(s->time_base) / av_q2d(io->frame_rate);
+
+ scf_logd("f->frame->pts: %ld, pts: %ld, stream_index: %d, io->frame_rate: %d:%d, s->time_base: %d:%d\n",
+ f->frame->pts, pts, pkt->stream_index,
+ io->frame_rate.num, io->frame_rate.den,
+ s->time_base.num, s->time_base.den);
+ } else {
+ int64_t pts = f->frame->pts;
+
+ f->frame->pts = f->frame->pts * av_q2d(s->time_base) / av_q2d(io->sample_rate);
+
+ scf_logd("f->frame->pts: %ld, pts: %ld, stream_index: %d, io->sample_rate: %d:%d, s->time_base: %d:%d\n",
+ f->frame->pts, pts, pkt->stream_index,
+ io->sample_rate.num, io->sample_rate.den,
+ s->time_base.num, s->time_base.den);
+ }
+
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static void* __ffmpeg_input_run(void* arg)
+{
+ abc_avio_t* io = arg;
+ abc_ffmpeg_t* priv = io->priv;
+ AVPacket* pkt = NULL;
+
+ int key_frame = 0;
+
+ pkt = av_packet_alloc();
+ if (!pkt)
+ goto end;
+
+ while (!io->exit) {
+
+ if (io->nb_vframes > 10) {
+ pthread_mutex_lock(&io->mutex);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ pthread_mutex_unlock(&io->mutex);
+ continue;
+ }
+
+ int ret = av_read_frame(priv->fmt_ctx, pkt);
+ if (ret < 0) {
+ scf_loge("av_read_frame error, ret: %s\n", av_err2str(ret));
+ av_packet_free(&pkt);
+ io->error = ret;
+ break;
+ }
+
+ if (pkt->stream_index == priv->aidx) {
+
+ if (0 == key_frame) {
+ av_packet_unref(pkt);
+ continue;
+ }
+
+ ret = __ffmpeg_decode(io, priv->acodec_ctx, pkt, priv->aframe, &io->ain, NULL);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+
+ } else if (pkt->stream_index == priv->vidx) {
+
+ if (pkt->flags & AV_PKT_FLAG_KEY)
+ key_frame++;
+
+ if (0 == key_frame) {
+ av_packet_unref(pkt);
+ continue;
+ }
+
+ ret = __ffmpeg_decode(io, priv->vcodec_ctx, pkt, priv->vframe, &io->vin, &io->nb_vframes);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ av_packet_unref(pkt);
+ }
+
+end:
+ if (pkt)
+ av_packet_free(&pkt);
+
+ pthread_mutex_lock(&io->mutex);
+ io->tid = -1;
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+
+ return NULL;
+}
+
+static int _ffmpeg_input_stop(abc_avio_t* io)
+{
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static int _ffmpeg_input_run(abc_avio_t* io)
+{
+ if (pthread_create(&io->tid, NULL, __ffmpeg_input_run, io)) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+abc_avio_ops_t abc_avio_ffmpeg_input =
+{
+ .type = "ffmpeg_input",
+ .open = _ffmpeg_input_open,
+ .close = _ffmpeg_input_close,
+ .run = _ffmpeg_input_run,
+ .stop = _ffmpeg_input_stop,
+};
--- /dev/null
+#include"abc_ffmpeg.h"
+#include"scf_def.h"
+
+static int _video_init(abc_ffmpeg_t* priv)
+{
+ const AVCodec* c = avcodec_find_encoder(AV_CODEC_ID_H264);
+ if (!c)
+ return -EINVAL;
+
+ AVStream* s = avformat_new_stream(priv->fmt_ctx, c);
+ if (!s)
+ return -ENOMEM;
+
+ s->id = priv->fmt_ctx->nb_streams - 1;
+ priv->vidx = s->id;
+
+ priv->vcodec_ctx = avcodec_alloc_context3(c);
+ if (!priv->vcodec_ctx)
+ return -1;
+
+ priv->vcodec_ctx->codec_id = c->id;
+ priv->vcodec_ctx->bit_rate = 1024 * 1024;
+ priv->vcodec_ctx->width = 1920;
+ priv->vcodec_ctx->height = 1080;
+ priv->vcodec_ctx->time_base = (AVRational){1, 24};
+ priv->vcodec_ctx->gop_size = 30;
+ priv->vcodec_ctx->max_b_frames = 1;
+ priv->vcodec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
+ s->time_base = priv->vcodec_ctx->time_base;
+
+ if (priv->fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
+ priv->vcodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+ if (c->id == AV_CODEC_ID_H264) {
+ av_opt_set(priv->vcodec_ctx->priv_data, "preset", "ultrafast", 0);
+ av_opt_set(priv->vcodec_ctx->priv_data, "tune", "zerolatency", 0);
+ }
+
+ int ret = avcodec_open2(priv->vcodec_ctx, c, NULL);
+ if (ret < 0) {
+ scf_loge("avcodec_open2 error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ ret = avcodec_parameters_from_context(s->codecpar, priv->vcodec_ctx);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_from_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ priv->vframe = av_frame_alloc();
+ if (!priv->vframe)
+ return -ENOMEM;
+
+ priv->vpkt = av_packet_alloc();
+ if (!priv->vpkt)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int _audio_init(abc_ffmpeg_t* priv)
+{
+ const AVCodec* c = avcodec_find_encoder(AV_CODEC_ID_AAC);
+ if (!c)
+ return -EINVAL;
+
+ AVStream* s = avformat_new_stream(priv->fmt_ctx, c);
+ if (!s)
+ return -ENOMEM;
+
+ s->id = priv->fmt_ctx->nb_streams - 1;
+ priv->aidx = s->id;
+
+ priv->acodec_ctx = avcodec_alloc_context3(c);
+ if (!priv->acodec_ctx)
+ return -1;
+
+ av_channel_layout_default(&priv->acodec_ctx->ch_layout, 2);
+
+ priv->acodec_ctx->codec_id = c->id;
+ priv->acodec_ctx->bit_rate = 64 * 1024;
+ priv->acodec_ctx->sample_rate = 44100;
+ priv->acodec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
+ priv->acodec_ctx->time_base = (AVRational){1, priv->acodec_ctx->sample_rate};
+ s->time_base = priv->acodec_ctx->time_base;
+
+ if (priv->fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
+ priv->acodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+
+ int ret = avcodec_open2(priv->acodec_ctx, c, NULL);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_to_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ ret = avcodec_parameters_from_context(s->codecpar, priv->acodec_ctx);
+ if (ret < 0) {
+ scf_loge("avcodec_parameters_from_context error, ret: %d, %s\n", ret, av_err2str(ret));
+ return ret;
+ }
+
+ priv->afifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_FLTP, 2, 1024);
+ if (!priv->afifo)
+ return -ENOMEM;
+
+ priv->apkt = av_packet_alloc();
+ if (!priv->apkt)
+ return -ENOMEM;
+
+ priv->aframe = av_frame_alloc();
+ if (!priv->aframe)
+ return -ENOMEM;
+
+ priv->aframe->format = AV_SAMPLE_FMT_FLTP;
+ priv->aframe->nb_samples = 1024;
+ av_channel_layout_default(&priv->aframe->ch_layout, 2);
+
+ if (av_frame_get_buffer(priv->aframe, 1) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int _ffmpeg_output_open(abc_avio_t* io, const char* in, const char* out)
+{
+ abc_ffmpeg_t* priv;
+ AVStream* s;
+
+ int ret;
+ int i;
+
+ if (!out)
+ return -EINVAL;
+
+ priv = calloc(1, sizeof(abc_ffmpeg_t));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->vidx = -1;
+ priv->aidx = -1;
+
+ avformat_alloc_output_context2(&priv->fmt_ctx, NULL, NULL, out);
+ if (!priv->fmt_ctx) {
+ scf_loge("\n");
+ goto error;
+ }
+
+ if (priv->fmt_ctx->oformat->video_codec != AV_CODEC_ID_NONE) {
+
+ ret = _video_init(priv);
+ if (ret < 0)
+ goto error;
+
+ io->vopen = 1;
+ }
+
+ if (priv->fmt_ctx->oformat->audio_codec != AV_CODEC_ID_NONE) {
+
+ ret = _audio_init(priv);
+ if (ret < 0)
+ goto error;
+
+ io->aopen = 1;
+ }
+
+
+ io->priv = priv;
+ return 0;
+
+error:
+ if (priv->fmt_ctx)
+ avformat_free_context(priv->fmt_ctx);
+
+ if (priv->vcodec_ctx)
+ avcodec_free_context(&priv->vcodec_ctx);
+
+ if (priv->acodec_ctx)
+ avcodec_free_context(&priv->acodec_ctx);
+
+ if (priv->afifo)
+ av_audio_fifo_free(priv->afifo);
+
+ if (priv->vframe)
+ av_frame_free(&priv->vframe);
+
+ if (priv->aframe)
+ av_frame_free(&priv->aframe);
+
+ if (priv->vpkt)
+ av_packet_free(&priv->vpkt);
+
+ if (priv->apkt)
+ av_packet_free(&priv->apkt);
+
+ free(priv);
+ return ret;
+}
+
+static int _ffmpeg_output_close(abc_avio_t* io)
+{
+ abc_ffmpeg_t* priv;
+
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ while (-1 != io->tid) {
+ pthread_cond_signal(&io->cond);
+ pthread_cond_wait(&io->cond, &io->mutex);
+ }
+ pthread_mutex_unlock(&io->mutex);
+
+ priv = io->priv;
+
+ if (priv) {
+ if (priv->fmt_ctx)
+ avformat_free_context(priv->fmt_ctx);
+
+ if (priv->vcodec_ctx)
+ avcodec_free_context(&priv->vcodec_ctx);
+
+ if (priv->acodec_ctx)
+ avcodec_free_context(&priv->acodec_ctx);
+
+ if (priv->afifo)
+ av_audio_fifo_free(priv->afifo);
+
+ if (priv->vframe)
+ av_frame_free(&priv->vframe);
+
+ if (priv->aframe)
+ av_frame_free(&priv->aframe);
+
+ if (priv->vpkt)
+ av_packet_free(&priv->vpkt);
+
+ if (priv->apkt)
+ av_packet_free(&priv->apkt);
+
+ free(priv);
+ }
+ }
+
+ return 0;
+}
+
+static int __ffmpeg_encode(abc_avio_t* io, AVCodecContext* c, AVPacket* pkt, AVFrame* frame, int idx)
+{
+ abc_ffmpeg_t* priv = io->priv;
+ AVStream* s = priv->fmt_ctx->streams[idx];
+
+ int ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ scf_loge("avcodec_send_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, pkt);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("avcodec_receive_packet error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ pkt->stream_index = idx;
+
+ pkt->pts = pkt->pts * av_q2d(c->time_base) / av_q2d(s->time_base);
+ pkt->dts = pkt->dts * av_q2d(c->time_base) / av_q2d(s->time_base);
+
+ scf_logw("idx: %d, pkt->stream_index: %d, pkt->pts: %ld, c->time_base: %d:%d, s->time_base: %d:%d\n",
+ idx, pkt->stream_index, pkt->pts, c->time_base.num, c->time_base.den, s->time_base.num, s->time_base.den);
+
+ ret = av_write_frame(priv->fmt_ctx, pkt);
+ av_packet_unref(pkt);
+
+ if (ret < 0) {
+ scf_loge("av_write_frame error, ret: %s\n", av_err2str(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void* __ffmpeg_output_run(void* arg)
+{
+ abc_avio_t* io = arg;
+ abc_ffmpeg_t* priv = io->priv;
+ AVPacket* pkt = NULL;
+
+ abc_frame_t* f;
+ scf_list_t* l;
+
+ int ret = 0;
+
+ av_dump_format(priv->fmt_ctx, 0, priv->fmt_ctx->url, 1);
+
+ if (!(priv->fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
+
+ ret = avio_open(&priv->fmt_ctx->pb, priv->fmt_ctx->url, AVIO_FLAG_WRITE);
+ if (ret < 0) {
+ scf_loge("Could not open output file '%s'", priv->fmt_ctx->url);
+ io->error = ret;
+ goto end;
+ }
+ }
+
+ ret = avformat_write_header(priv->fmt_ctx, NULL);
+ if (ret < 0) {
+ scf_loge("avformat_write_header error, ret: %s\n", av_err2str(ret));
+ io->error = ret;
+ goto end;
+ }
+
+ while (!io->exit) {
+ pthread_mutex_lock(&io->mutex);
+
+ if (!scf_list_empty(&io->vout)) {
+ l = scf_list_head(&io->vout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+ pthread_mutex_unlock(&io->mutex);
+
+ scf_logd("priv->vidx: %d, frame->pts: %ld\n", priv->vidx, f->frame->pts);
+
+ ret = __ffmpeg_encode(io, priv->vcodec_ctx, priv->vpkt, f->frame, priv->vidx);
+
+ abc_frame_free(f);
+ f = NULL;
+
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+
+ } else if (!scf_list_empty(&io->aout)) {
+ l = scf_list_head(&io->aout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+ pthread_mutex_unlock(&io->mutex);
+
+ ret = av_audio_fifo_write(priv->afifo, (void**)f->frame->data, f->frame->nb_samples);
+
+ scf_logd("priv->aidx: %d, frame->pts: %ld, frame->nb_samples: %d, afifo->size: %d\n",
+ priv->aidx, f->frame->pts, f->frame->nb_samples, av_audio_fifo_size(priv->afifo));
+
+ abc_frame_free(f);
+ f = NULL;
+
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+
+ while (av_audio_fifo_size(priv->afifo) >= 1024) {
+
+ ret = av_audio_fifo_read(priv->afifo, (void**)priv->aframe->data, 1024);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+
+ } else if (1024 != ret) {
+ io->error = -1;
+ goto end;
+ }
+
+ priv->nb_samples += priv->aframe->nb_samples;
+ priv->aframe->pts = priv->nb_samples;
+
+ scf_logd("aframe->pts: %ld\n", priv->aframe->pts);
+
+ ret = __ffmpeg_encode(io, priv->acodec_ctx, priv->apkt, priv->aframe, priv->aidx);
+ if (ret < 0) {
+ io->error = ret;
+ goto end;
+ }
+ }
+ } else {
+ pthread_cond_wait(&io->cond, &io->mutex);
+ pthread_mutex_unlock(&io->mutex);
+ }
+ }
+
+ pthread_mutex_lock(&io->mutex);
+ while(!scf_list_empty(&io->vout)) {
+ l = scf_list_head(&io->vout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+
+ scf_logd("priv->vidx: %d, frame->pts: %ld\n", priv->vidx, f->frame->pts);
+
+ if (0 == io->error && io->flush) {
+
+ ret = __ffmpeg_encode(io, priv->vcodec_ctx, priv->vpkt, f->frame, priv->vidx);
+ if (ret < 0)
+ scf_loge("ret: %d, frame->pts: %ld\n", ret, f->frame->pts);
+
+ io->error = ret;
+ }
+
+ abc_frame_free(f);
+ f = NULL;
+ }
+
+ ret = __ffmpeg_encode(io, priv->vcodec_ctx, priv->vpkt, NULL, priv->vidx);
+ if (ret < 0)
+ scf_loge("ret: %d\n", ret);
+ io->error = ret;
+
+ while (!scf_list_empty(&io->aout)) {
+ l = scf_list_head(&io->aout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+
+ if (0 == io->error && io->flush) {
+
+ ret = av_audio_fifo_write(priv->afifo, (void**)f->frame->data, f->frame->nb_samples);
+ if (ret < 0)
+ scf_loge("ret: %d\n", ret);
+
+ io->error = ret;
+ }
+
+ abc_frame_free(f);
+ f = NULL;
+ }
+
+ while (av_audio_fifo_size(priv->afifo) > 0) {
+
+ ret = av_audio_fifo_read(priv->afifo, (void**)priv->aframe->data, 1024);
+ if (ret < 0) {
+ scf_loge("ret: %d\n", ret);
+ io->error = ret;
+ break;
+ }
+
+ priv->nb_samples += priv->aframe->nb_samples;
+ priv->aframe->pts = priv->nb_samples;
+
+ scf_logd("aframe->pts: %ld\n", priv->aframe->pts);
+
+ if (0 == io->error && io->flush) {
+
+ ret = __ffmpeg_encode(io, priv->acodec_ctx, priv->apkt, priv->aframe, priv->aidx);
+ if (ret < 0) {
+ scf_loge("ret: %d\n", ret);
+ io->error = ret;
+ break;
+ }
+ }
+ }
+
+ ret = __ffmpeg_encode(io, priv->acodec_ctx, priv->apkt, NULL, priv->aidx);
+ if (ret < 0)
+ scf_loge("ret: %d\n", ret);
+ io->error = ret;
+
+ pthread_mutex_unlock(&io->mutex);
+
+end:
+ ret = av_write_trailer(priv->fmt_ctx);
+ if (ret < 0) {
+ io->error = ret;
+ scf_loge("av_write_trailer error, ret: %s\n", av_err2str(ret));
+ }
+
+ pthread_mutex_lock(&io->mutex);
+ io->tid = -1;
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+
+ return NULL;
+}
+
+static int _ffmpeg_output_run(abc_avio_t* io)
+{
+ if (pthread_create(&io->tid, NULL, __ffmpeg_output_run, io)) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+abc_avio_ops_t abc_avio_ffmpeg_output =
+{
+ .type = "ffmpeg_output",
+ .open = _ffmpeg_output_open,
+ .close = _ffmpeg_output_close,
+ .run = _ffmpeg_output_run,
+};
--- /dev/null
+#include"abc_ffmpeg.h"
+
+static int _init_filters(abc_filter_t* f)
+{
+ char args[512];
+ int ret = 0;
+
+ const AVFilter *buffersrc = avfilter_get_by_name("buffer");
+ const AVFilter *buffersink = avfilter_get_by_name("buffersink");
+ const AVFilter *abuffersrc = avfilter_get_by_name("abuffer");
+ const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
+
+ AVFilterInOut *voutputs = NULL;
+ AVFilterInOut *vinputs = NULL;
+ AVFilterInOut *aoutputs = NULL;
+ AVFilterInOut *ainputs = NULL;
+ AVFilterInOut *tmp = NULL;
+
+ f->vgraph = avfilter_graph_alloc();
+ if (!f->vgraph) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ f->agraph = avfilter_graph_alloc();
+ if (!f->agraph) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ const char* vfilters_descr = "[in]scale=1920x1080[out]";
+ const char* afilters_descr = "[in]aresample=44100[out]";
+
+ abc_avio_t* io;
+ scf_list_t* l;
+
+ int i;
+ int n = 0;
+ for (i = 0; i < f->inputs->size; i++) {
+ io = f->inputs->data[i];
+
+ if (io->width > 0 && io->height > 0 && io->vopen) {
+
+ snprintf(args, sizeof(args),
+ "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
+ io->width, io->height, io->pix_fmt,
+ io->frame_rate.num, io->frame_rate.den,
+ io->sample_aspect_ratio.num, io->sample_aspect_ratio.den);
+
+ printf("%s\n", args);
+
+ ret = avfilter_graph_create_filter(&io->vbuffersrc_ctx, buffersrc, "in", args, NULL, f->vgraph);
+ if (ret < 0)
+ goto end;
+
+ tmp = avfilter_inout_alloc();
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ tmp->name = av_strdup("in");
+ tmp->filter_ctx = io->vbuffersrc_ctx;
+ tmp->pad_idx = n;
+ tmp->next = voutputs;
+ voutputs = tmp;
+ }
+
+ if (io->sample_rate.den > 0 && io->channels > 0 && io->aopen) {
+
+ AVChannelLayout ch_layout;
+
+ av_channel_layout_default(&ch_layout, io->channels);
+
+ ret = snprintf(args, sizeof(args),
+ "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=",
+ io->sample_rate.num, io->sample_rate.den,
+ io->sample_rate.den /io->sample_rate.num,
+ av_get_sample_fmt_name(io->sample_fmt));
+
+ av_channel_layout_describe(&ch_layout, args + ret, sizeof(args) - ret);
+
+ printf("%s\n", args);
+
+ ret = avfilter_graph_create_filter(&io->abuffersrc_ctx, abuffersrc, "in", args, NULL, f->agraph);
+ if (ret < 0)
+ goto end;
+
+ tmp = avfilter_inout_alloc();
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ tmp->name = av_strdup("in");
+ tmp->filter_ctx = io->abuffersrc_ctx;
+ tmp->pad_idx = n;
+ tmp->next = aoutputs;
+ aoutputs = tmp;
+ }
+
+ n++;
+ }
+
+ const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
+ const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_FLTP, -1};
+ const int sample_rates[] = {44100, -1 };
+
+ ret = avfilter_graph_create_filter(&f->vbuffersink_ctx, buffersink, "out", NULL, NULL, f->vgraph);
+ if (ret < 0)
+ goto end;
+
+ ret = av_opt_set_int_list(f->vbuffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ vinputs = avfilter_inout_alloc();
+ if (!vinputs) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ vinputs->name = av_strdup("out");
+ vinputs->filter_ctx = f->vbuffersink_ctx;
+ vinputs->pad_idx = 0;
+ vinputs->next = NULL;
+
+ if ((ret = avfilter_graph_parse_ptr(f->vgraph, vfilters_descr, &vinputs, &voutputs, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+ if ((ret = avfilter_graph_config(f->vgraph, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+ ret = avfilter_graph_create_filter(&f->abuffersink_ctx, abuffersink, "out", NULL, NULL, f->agraph);
+ if (ret < 0)
+ goto end;
+
+ ainputs = avfilter_inout_alloc();
+ if (!ainputs) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ainputs->name = av_strdup("out");
+ ainputs->filter_ctx = f->abuffersink_ctx;
+ ainputs->pad_idx = 0;
+ ainputs->next = NULL;
+
+ ret = av_opt_set_int_list(f->abuffersink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ ret = av_opt_set(f->abuffersink_ctx, "ch_layouts", "stereo", AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ ret = av_opt_set_int_list(f->abuffersink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0)
+ goto end;
+
+ if ((ret = avfilter_graph_parse_ptr(f->agraph, afilters_descr, &ainputs, &aoutputs, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+ if ((ret = avfilter_graph_config(f->agraph, NULL)) < 0) {
+ scf_loge("\n");
+ goto end;
+ }
+
+end:
+ avfilter_inout_free(&vinputs);
+ avfilter_inout_free(&voutputs);
+ avfilter_inout_free(&ainputs);
+ avfilter_inout_free(&aoutputs);
+ return ret;
+}
+
+int abc_filter_open(abc_filter_t** pf)
+{
+ abc_filter_t* f = calloc(1, sizeof(abc_filter_t));
+ if (!f)
+ return -ENOMEM;
+
+ f->vframe = av_frame_alloc();
+ if (!f->vframe)
+ goto vframe_error;
+
+ f->aframe = av_frame_alloc();
+ if (!f->aframe)
+ goto aframe_error;
+
+ f->inputs = scf_vector_alloc();
+ if (!f->inputs)
+ goto inputs_error;
+
+ f->outputs = scf_vector_alloc();
+ if (!f->outputs)
+ goto outputs_error;
+
+ pthread_mutex_init(&f->mutex, NULL);
+ pthread_cond_init (&f->cond, NULL);
+
+ f->tid = -1;
+
+ *pf = f;
+ return 0;
+
+outputs_error:
+ scf_vector_free(f->inputs);
+inputs_error:
+ av_frame_free(&f->aframe);
+aframe_error:
+ av_frame_free(&f->vframe);
+vframe_error:
+ free(f);
+ return -ENOMEM;
+}
+
+int abc_filter_close(abc_filter_t* f, int flush)
+{
+ abc_avio_t* io;
+ scf_list_t* l;
+
+ if (!f)
+ return -EINVAL;
+
+ pthread_mutex_lock(&f->mutex);
+ scf_logi("flush: %d\n", flush);
+ if (flush)
+ f->flush = 1;
+ else
+ f->exit = 1;
+
+ while (-1 != f->tid) {
+ pthread_cond_signal(&f->cond);
+ pthread_cond_wait(&f->cond, &f->mutex);
+ }
+ pthread_mutex_unlock(&f->mutex);
+
+ avfilter_graph_free(&f->vgraph);
+ avfilter_graph_free(&f->agraph);
+
+ f->abuffersink_ctx = NULL;
+ f->vbuffersink_ctx = NULL;
+
+ av_frame_free(&f->vframe);
+ av_frame_free(&f->aframe);
+
+ int i;
+ for (i = 0; i < f->inputs->size; i++) {
+ io = f->inputs->data[i];
+
+ io->abuffersrc_ctx = NULL;
+ io->vbuffersrc_ctx = NULL;
+
+ if (flush)
+ io->flush = 1;
+
+ if (0 == --io->refs)
+ abc_avio_close(io);
+
+ f->inputs->data[i] = NULL;
+ }
+
+ for (i = 0; i < f->outputs->size; i++) {
+ io = f->outputs->data[i];
+
+ if (flush)
+ io->flush = 1;
+
+ if (0 == --io->refs)
+ abc_avio_close(io);
+
+ f->outputs->data[i] = NULL;
+ }
+
+ scf_vector_free(f->inputs);
+ scf_vector_free(f->outputs);
+ return 0;
+}
+
+static int _filter_add_video(abc_avio_t* io, AVRational speed)
+{
+ abc_frame_t* vf;
+ scf_list_t* vl;
+
+ pthread_mutex_lock(&io->mutex);
+ if (!scf_list_empty(&io->vin)) {
+
+ vl = scf_list_head(&io->vin);
+ vf = scf_list_data(vl, abc_frame_t, list);
+
+ int64_t time = gettime() - io->start_time;
+ int64_t vtime = vf->frame->pts * av_q2d(io->frame_rate) * 1000000LL;
+
+ if (speed.num > 0 && speed.den > 0)
+ time *= av_q2d(speed);
+
+ else if (io->speed.num > 0 && io->speed.num > 0)
+ time *= av_q2d(io->speed);
+
+ if (vtime < time) {
+ scf_list_del(&vf->list);
+ io->nb_vframes--;
+ pthread_mutex_unlock(&io->mutex);
+
+ int64_t usec = vtime % 1000000;
+ int64_t sec = vtime / 1000000;
+ int64_t hour = sec / 3600;
+ sec %= 3600;
+ int64_t min = sec / 60;
+ sec %= 60;
+
+ scf_logd("vf->pts: %ld, vtime: %ld, time: %ld, nb_vframes: %d, %ld:%ld:%ld.%ld\n",
+ vf->frame->pts, vtime, time, io->nb_vframes, hour, min, sec, usec);
+
+ int ret = av_buffersrc_add_frame_flags(io->vbuffersrc_ctx, vf->frame, AV_BUFFERSRC_FLAG_KEEP_REF);
+
+ abc_frame_free(vf);
+ vf = NULL;
+
+ if (ret < 0) {
+ scf_loge("av_buffersrc_add_frame_flags error\n");
+ return ret;
+ }
+ } else
+ pthread_mutex_unlock(&io->mutex);
+
+ } else {
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static int _filter_add_audio(abc_avio_t* io, AVRational speed)
+{
+ abc_frame_t* af;
+ scf_list_t* al;
+
+ pthread_mutex_lock(&io->mutex);
+ if (!scf_list_empty(&io->ain)) {
+
+ al = scf_list_head(&io->ain);
+ af = scf_list_data(al, abc_frame_t, list);
+
+ int64_t time = gettime() - io->start_time;
+ int64_t atime = af->frame->pts * av_q2d(io->sample_rate) * 1000000LL;
+
+ if (speed.num > 0 && speed.den > 0)
+ time *= av_q2d(speed);
+
+ else if (io->speed.num > 0 && io->speed.num > 0)
+ time *= av_q2d(io->speed);
+
+ if (atime < time) {
+ scf_list_del(&af->list);
+ pthread_mutex_unlock(&io->mutex);
+
+ scf_logd("af->pts: %ld, atime: %ld, time: %ld\n", af->frame->pts, atime, time);
+
+ int ret = av_buffersrc_add_frame_flags(io->abuffersrc_ctx, af->frame, AV_BUFFERSRC_FLAG_KEEP_REF);
+ abc_frame_free(af);
+ af = NULL;
+
+ if (ret < 0) {
+ scf_loge("av_buffersrc_add_frame_flags error\n");
+ return ret;
+ }
+ } else
+ pthread_mutex_unlock(&io->mutex);
+
+ } else {
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static void* __filter_run(void* arg)
+{
+ abc_filter_t* f = arg;
+
+ abc_avio_t* io;
+ abc_frame_t* vf;
+ abc_frame_t* af;
+ scf_list_t* l;
+
+ if (_init_filters(f) < 0) {
+ scf_loge("\n");
+ goto error;
+ }
+
+ while (!f->exit) {
+ usleep(1000);
+
+ int flushed = 0;
+ int i;
+
+ for (i = 0; i < f->inputs->size; i++) {
+ io = f->inputs->data[i];
+
+ if (io->start_time == 0)
+ io->start_time = gettime();
+
+ if (f->flush)
+ abc_avio_stop(io);
+
+ if (io->vopen) {
+ f->error = _filter_add_video(io, f->speed);
+ if (f->error < 0)
+ goto error;
+
+ if (f->flush) {
+ flushed++;
+
+ pthread_mutex_lock(&io->mutex);
+ if (scf_list_empty(&io->vin))
+ flushed--;
+ pthread_mutex_unlock(&io->mutex);
+ }
+ }
+
+ if (io->aopen) {
+ f->error = _filter_add_audio(io, f->speed);
+ if (f->error < 0)
+ goto error;
+
+ if (f->flush) {
+ flushed++;
+
+ pthread_mutex_lock(&io->mutex);
+ if (scf_list_empty(&io->ain))
+ flushed--;
+ pthread_mutex_unlock(&io->mutex);
+ }
+ }
+ }
+
+ if (f->flush && 0 == flushed)
+ f->exit = 1;
+
+ while (1) {
+ int ret = av_buffersink_get_frame(f->vbuffersink_ctx, f->vframe);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("av_buffersink_get_frame error, ret: %s\n", av_err2str(ret));
+
+ f->error = ret;
+ goto error;
+ }
+#if 0
+ static int fd = -1;
+ if (-1 == fd)
+ fd = open("1.yuv", O_RDWR | O_CREAT | O_TRUNC, 0666);
+
+ if (fd > 0) {
+ int i;
+ for (i = 0; i < f->vframe->height; i++)
+ write(fd, f->vframe->data[0] + i * f->vframe->linesize[0], f->vframe->width);
+
+ for (i = 0; i < f->vframe->height / 2; i++)
+ write(fd, f->vframe->data[1] + i * f->vframe->linesize[1], f->vframe->width / 2);
+
+ for (i = 0; i < f->vframe->height / 2; i++)
+ write(fd, f->vframe->data[2] + i * f->vframe->linesize[2], f->vframe->width / 2);
+ }
+#endif
+ for (i = 0; i < f->outputs->size; i++) {
+ io = f->outputs->data[i];
+
+ if (io->vopen) {
+ vf = calloc(1, sizeof(abc_frame_t));
+ if (!vf) {
+ f->error = -ENOMEM;
+ goto error;
+ }
+
+ vf->frame = av_frame_clone(f->vframe);
+ if (!vf->frame) {
+ abc_frame_free(vf);
+ vf = NULL;
+
+ f->error = -ENOMEM;
+ goto error;
+ }
+
+ scf_logd("vf->frame->pts: %ld\n", vf->frame->pts);
+
+ pthread_mutex_lock(&io->mutex);
+ scf_list_add_tail(&io->vout, &vf->list);
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ vf = NULL;
+ }
+
+ if (f->flush)
+ io->flush = 1;
+ }
+
+ av_frame_unref(f->vframe);
+ }
+
+ while (1) {
+ int ret = av_buffersink_get_frame(f->abuffersink_ctx, f->aframe);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
+ } else if (ret < 0) {
+ scf_loge("av_buffersink_get_frame error, ret: %s\n", av_err2str(ret));
+
+ f->error = ret;
+ goto error;
+ }
+
+ for (i = 0; i < f->outputs->size; i++) {
+ io = f->outputs->data[i];
+
+ if (io->aopen) {
+ af = calloc(1, sizeof(abc_frame_t));
+ if (!af) {
+ f->error = -ENOMEM;
+ goto error;
+ }
+
+ af->frame = av_frame_clone(f->aframe);
+ if (!af->frame) {
+ abc_frame_free(af);
+ af = NULL;
+
+ f->error = -ENOMEM;
+ goto error;
+ }
+
+ scf_logd("af->frame->pts: %ld\n", af->frame->pts);
+
+ pthread_mutex_lock(&io->mutex);
+ scf_list_add_tail(&io->aout, &af->list);
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ af = NULL;
+ }
+
+ if (f->flush)
+ io->flush = 1;
+ }
+
+ av_frame_unref(f->aframe);
+ }
+ }
+
+error:
+ pthread_mutex_lock(&f->mutex);
+ f->tid = -1;
+ pthread_cond_signal(&f->cond);
+ pthread_mutex_unlock(&f->mutex);
+ return NULL;
+}
+
+int abc_filter_run(abc_filter_t* f)
+{
+ if (!f)
+ return -EINVAL;
+
+ if (pthread_create(&f->tid, NULL, __filter_run, f))
+ return -EINVAL;
+
+ return 0;
+}
+
+int abc_filter_add_input(abc_filter_t* f, abc_avio_t* input)
+{
+ if (f && input) {
+
+ int ret = scf_vector_add(f->inputs, input);
+ if (ret < 0)
+ return ret;
+
+ input->refs++;
+ return 0;
+ }
+
+ scf_loge("f: %p, input: %p\n", f, input);
+ return -1;
+}
+
+int abc_filter_add_output(abc_filter_t* f, abc_avio_t* output)
+{
+ if (f && output) {
+
+ int ret = scf_vector_add(f->outputs, output);
+ if (ret < 0)
+ return ret;
+
+ output->refs++;
+ return 0;
+ }
+
+ scf_loge("\n");
+ return -1;
+}
--- /dev/null
+#include"abc_ffmpeg.h"
+#include"scf_def.h"
+
+static int _vout_fifo_open(abc_avio_t* io, const char* in, const char* out)
+{
+ io->vopen = 1;
+ return 0;
+}
+
+static int _vout_fifo_close(abc_avio_t* io)
+{
+ if (io) {
+ pthread_mutex_lock(&io->mutex);
+ io->exit = 1;
+ io->tid = -1;
+ pthread_cond_signal(&io->cond);
+ pthread_mutex_unlock(&io->mutex);
+ }
+
+ return 0;
+}
+
+static int _vout_fifo_run(abc_avio_t* io)
+{
+ return 0;
+}
+
+abc_avio_ops_t abc_avio_vout_fifo =
+{
+ .type = "vout_fifo",
+ .open = _vout_fifo_open,
+ .close = _vout_fifo_close,
+ .run = _vout_fifo_run,
+};
--- /dev/null
+#include<cairo/cairo.h>
+#include"abc_ffmpeg.h"
+
+int main(int argc, char* argv[])
+{
+ abc_img_t* img = NULL;
+
+ if (argc < 2) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_img_open(&img, argv[1]) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ scf_logi("img->width: %d, img->height: %d\n", img->width, img->height);
+
+ int size = img->width * img->height * 4;
+
+ uint8_t* rgba = calloc(1, size);
+ if (!rgba) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_img_read(img, rgba, size) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, img->width, img->height, img->width * 4);
+ cr = cairo_create(surface);
+
+ cairo_surface_write_to_png(surface, "1.png");
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ printf("\n");
+
+ scf_logi("main ok\n");
+ return 0;
+}
--- /dev/null
+#include"abc_ffmpeg.h"
+
+void usage()
+{
+ printf("./abc input output\n");
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc < 3) {
+ usage();
+ return -1;
+ }
+
+ avdevice_register_all();
+
+ abc_filter_t* f = NULL;
+ abc_avio_t* in = NULL;
+ abc_avio_t* out = NULL;
+ abc_avio_t* alsa = NULL;
+
+ if (abc_avio_open(&in, "ffmpeg_input", argv[1], NULL) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_avio_open(&out, "ffmpeg_output", NULL, argv[2]) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_avio_open(&alsa, "ffmpeg_alsa", NULL, "hw:0") < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_filter_open(&f) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ abc_filter_add_input (f, in);
+ abc_filter_add_output(f, out);
+ abc_filter_add_output(f, alsa);
+
+ f->speed.num = 1;
+ f->speed.den = 1;
+
+ if (abc_filter_run(f) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_avio_run(alsa) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_avio_run(out) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_avio_run(in) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ int c;
+
+ while ('q' != (c = getchar())) {
+ sleep(1);
+ }
+
+ scf_logw("----\n");
+ abc_filter_close(f, 1);
+
+ printf("main quit ok\n");
+ return 0;
+}
CFILES += abc_io_file.c
CFILES += abc_io_http.c
-CFLAGS += -g
+CFLAGS += -g -DABC_HTML_TEST
CFLAGS += -I../util
+CFLAGS += -I../ffmpeg
LDFLAGS += -ldl -pthread
static char* value_keys[] = {"value", "值", NULL};
static char* action_keys[] = {"action", "动作", NULL};
static char* method_keys[] = {"method", "方法", NULL};
+static char* control_keys[] = {"controls", "控件", NULL};
static char* xmlns_keys[] = {"xmlns", NULL};
static char* xmlang_keys[] = {"xml:lang", NULL};
static char* enctype_keys[] = {"enctype", NULL};
+
// HTML labels
static char* html_keys[] = {"html", "网页", NULL};
static char* head_keys[] = {"head", "头部", NULL};
static char* input_keys[] = {"input", "输入", NULL};
static char* label_keys[] = {"label", "标签", NULL};
+static char* video_keys[] = {"video", "视频", NULL};
+static char* audio_keys[] = {"audio", "音频", NULL};
+static char* source_keys[] = {"source", "源", NULL};
+
static char* table_keys[] = {"table", "表格", NULL};
static char* tr_keys[] = {"tr", "行", NULL};
static char* td_keys[] = {"td", "列", NULL};
static char* p_keys[] = {"p", "段落", NULL};
static char* a_keys[] = {"a", "超链接", NULL};
+static char* play_keys[] = {"play", "播放", NULL};
+static char* progress_keys[] = {"progress", "进度条", NULL};
+
+
static html_attr_t html_attrs[] =
{
{xmlns_keys, "", ABC_HTML_ATTR_XMLNS, 0},
{height_keys, "100", ABC_HTML_ATTR_HEIGHT, ABC_HTML_FLAG_SHOW},
};
+static html_attr_t video_attrs[] =
+{
+ {width_keys, "100", ABC_HTML_ATTR_WIDTH, ABC_HTML_FLAG_SHOW},
+ {height_keys, "100", ABC_HTML_ATTR_HEIGHT, ABC_HTML_FLAG_SHOW},
+ {control_keys, "", ABC_HTML_ATTR_CONTROLS, 0},
+};
+
+static html_attr_t audio_attrs[] =
+{
+ {control_keys, "", ABC_HTML_ATTR_CONTROLS, 0},
+};
+
+static html_attr_t source_attrs[] =
+{
+ {src_keys, "", ABC_HTML_ATTR_SRC, ABC_HTML_FLAG_SHOW},
+ {type_keys, "", ABC_HTML_ATTR_TYPE, ABC_HTML_FLAG_SHOW},
+};
+
static html_attr_t input_attrs[] =
{
{type_keys, "text", ABC_HTML_ATTR_TYPE, ABC_HTML_FLAG_SHOW},
static html_label_t html_labels[] =
{
- {html_keys, ABC_HTML, abc_number_of(html_attrs), html_attrs, ABC_HTML_FLAG_CLOSE},
- {meta_keys, ABC_HTML_META, abc_number_of(meta_attrs), meta_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE},
+ {html_keys, ABC_HTML, abc_number_of(html_attrs), html_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {meta_keys, ABC_HTML_META, abc_number_of(meta_attrs), meta_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE},
+
+ {title_keys, ABC_HTML_TITLE, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {head_keys, ABC_HTML_HEAD, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {body_keys, ABC_HTML_BODY, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+
+ {div_keys, ABC_HTML_DIV, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
- {title_keys, ABC_HTML_TITLE, 0, NULL, ABC_HTML_FLAG_CLOSE},
- {head_keys, ABC_HTML_HEAD, 0, NULL, ABC_HTML_FLAG_CLOSE},
- {body_keys, ABC_HTML_BODY, 0, NULL, ABC_HTML_FLAG_CLOSE},
+ {h1_keys, ABC_HTML_H1, abc_number_of(h1_attrs), h1_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {h2_keys, ABC_HTML_H2, abc_number_of(h2_attrs), h2_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {h3_keys, ABC_HTML_H3, abc_number_of(h3_attrs), h3_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {h4_keys, ABC_HTML_H4, abc_number_of(h4_attrs), h4_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {h5_keys, ABC_HTML_H5, abc_number_of(h5_attrs), h5_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {h6_keys, ABC_HTML_H6, abc_number_of(h6_attrs), h6_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
- {div_keys, ABC_HTML_DIV, 0, NULL, ABC_HTML_FLAG_CLOSE},
+ {p_keys, ABC_HTML_P, abc_number_of(p_attrs), p_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {br_keys, ABC_HTML_BR, 0, NULL, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW},
+ {hr_keys, ABC_HTML_HR, 0, NULL, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW},
- {h1_keys, ABC_HTML_H1, abc_number_of(h1_attrs), h1_attrs, ABC_HTML_FLAG_CLOSE},
- {h2_keys, ABC_HTML_H2, abc_number_of(h2_attrs), h2_attrs, ABC_HTML_FLAG_CLOSE},
- {h3_keys, ABC_HTML_H3, abc_number_of(h3_attrs), h3_attrs, ABC_HTML_FLAG_CLOSE},
- {h4_keys, ABC_HTML_H4, abc_number_of(h4_attrs), h4_attrs, ABC_HTML_FLAG_CLOSE},
- {h5_keys, ABC_HTML_H5, abc_number_of(h5_attrs), h5_attrs, ABC_HTML_FLAG_CLOSE},
- {h6_keys, ABC_HTML_H6, abc_number_of(h6_attrs), h6_attrs, ABC_HTML_FLAG_CLOSE},
+ {a_keys, ABC_HTML_A, abc_number_of(a_attrs), a_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {img_keys, ABC_HTML_IMG, abc_number_of(img_attrs), img_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE | ABC_HTML_FLAG_SHOW},
- {p_keys, ABC_HTML_P, abc_number_of(p_attrs), p_attrs, ABC_HTML_FLAG_CLOSE},
- {br_keys, ABC_HTML_BR, 0, NULL, ABC_HTML_FLAG_OPEN},
- {hr_keys, ABC_HTML_HR, 0, NULL, ABC_HTML_FLAG_OPEN},
+ {form_keys, ABC_HTML_FORM, abc_number_of(form_attrs), form_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {input_keys, ABC_HTML_INPUT, abc_number_of(input_attrs), input_attrs, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW},
+ {label_keys, ABC_HTML_LABEL, abc_number_of(label_attrs), label_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {center_keys, ABC_HTML_CENTER, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
- {a_keys, ABC_HTML_A, abc_number_of(a_attrs), a_attrs, ABC_HTML_FLAG_CLOSE},
- {img_keys, ABC_HTML_IMG, abc_number_of(img_attrs), img_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SINGLE},
+ {table_keys, ABC_HTML_TABLE, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {tr_keys, ABC_HTML_TR, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {td_keys, ABC_HTML_TD, 0, NULL, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
- {form_keys, ABC_HTML_FORM, abc_number_of(form_attrs), form_attrs, ABC_HTML_FLAG_CLOSE},
- {input_keys, ABC_HTML_INPUT, abc_number_of(input_attrs), input_attrs, ABC_HTML_FLAG_OPEN},
- {label_keys, ABC_HTML_LABEL, abc_number_of(label_attrs), label_attrs, ABC_HTML_FLAG_CLOSE},
- {center_keys, ABC_HTML_CENTER, 0, NULL, ABC_HTML_FLAG_CLOSE},
+ {video_keys, ABC_HTML_VIDEO, abc_number_of(video_attrs), video_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {audio_keys, ABC_HTML_AUDIO, abc_number_of(audio_attrs), audio_attrs, ABC_HTML_FLAG_CLOSE | ABC_HTML_FLAG_SHOW},
+ {source_keys, ABC_HTML_SOURCE, abc_number_of(source_attrs), source_attrs, ABC_HTML_FLAG_OPEN | ABC_HTML_FLAG_SHOW},
- {table_keys, ABC_HTML_TABLE, 0, NULL, ABC_HTML_FLAG_CLOSE},
- {tr_keys, ABC_HTML_TR, 0, NULL, ABC_HTML_FLAG_CLOSE},
- {td_keys, ABC_HTML_TD, 0, NULL, ABC_HTML_FLAG_CLOSE},
+ {play_keys, ABC_HTML_PLAY, 0, NULL, ABC_HTML_FLAG_OPEN},
+ {progress_keys, ABC_HTML_PROGRESS, 0, NULL, ABC_HTML_FLAG_OPEN},
};
static int __html_parse_obj(abc_html_t* html, abc_char_t* c);
return 0;
}
+static int __html_add_controls(abc_obj_t* obj)
+{
+ html_label_t* label;
+ abc_obj_t* play;
+ abc_obj_t* progress;
+
+ play = abc_obj_alloc(obj->file, obj->line, obj->pos, ABC_HTML_PLAY);
+ if (!play)
+ return -ENOMEM;
+
+ label = __html_find_label("play");
+ play->flags = label->flags;
+ play->keys = label->names;
+ play->parent = obj;
+
+ progress = abc_obj_alloc(obj->file, obj->line, obj->pos, ABC_HTML_PROGRESS);
+ if (!progress) {
+ abc_obj_free(play);
+ return -ENOMEM;
+ }
+
+ label = __html_find_label("progress");
+ progress->flags = label->flags;
+ progress->keys = label->names;
+ progress->parent = obj;
+
+ scf_list_add_tail(&obj->childs, &play->list);
+ scf_list_add_tail(&obj->childs, &progress->list);
+ return 0;
+}
+
static int __html_load_attrs(abc_obj_t* obj, html_attr_t* attrs, int n_attrs)
{
scf_list_t* l;
} else
ret = __html_parse_text(html, obj);
+
+ switch (obj->type) {
+
+ case ABC_HTML_VIDEO:
+ html->has_video = 1;
+
+ case ABC_HTML_AUDIO:
+ if (__html_add_controls(obj) < 0)
+ return -1;
+ break;
+ default:
+ break;
+ };
+
html->current = obj->parent;
return ret;
}
int n_lines;
int pos;
- uint8_t download:1;
+ uint8_t download :1;
+ uint8_t has_video:1;
};
int abc_html_open (abc_html_t** pp, const char* path);
if (obj->file)
scf_string_free(obj->file);
+#ifndef ABC_HTML_TEST
+ if (obj->av_filter)
+ abc_filter_close(obj->av_filter, 0);
+#endif
+
scf_list_clear(&obj->attrs, abc_obj_t, list, abc_obj_free);
scf_list_clear(&obj->childs, abc_obj_t, list, abc_obj_free);
if (!obj)
return;
+ if (!(ABC_HTML_FLAG_SHOW & obj->flags))
+ return;
+
if (obj->value)
printf(" %s=\"%s\"", obj->keys[0], obj->value->data);
else if (obj->keys)
if (!obj)
return 0;
+ if (!(ABC_HTML_FLAG_SHOW & obj->flags))
+ return 0;
+
if (obj->value) {
ret = scf_string_cat_cstr(s, " ");
if (ret < 0)
#include"scf_string.h"
#include"scf_list.h"
+#include"abc_ffmpeg.h"
typedef struct abc_obj_s abc_obj_t;
ABC_HTML_TR,
ABC_HTML_TD,
+ ABC_HTML_VIDEO,
+ ABC_HTML_AUDIO,
+ ABC_HTML_SOURCE,
+
+ ABC_HTML_PLAY,
+ ABC_HTML_PROGRESS,
+
ABC_HTML_NB, // total HTML objects
ABC_HTML_ATTR_ID,
ABC_HTML_ATTR_CHARSET,
ABC_HTML_ATTR_ENCTYPE,
+
+ ABC_HTML_ATTR_CONTROLS,
};
struct abc_obj_s
abc_obj_t* parent;
void* gtk_builder;
+ abc_filter_t* av_filter;
+ abc_avio_t* vout;
int type;
CFILES += abc_layout_h1.c
CFILES += abc_layout_a.c
CFILES += abc_layout_img.c
+
CFILES += abc_layout_form.c
CFILES += abc_layout_label.c
CFILES += abc_layout_input.c
CFILES += abc_layout_td.c
+CFILES += abc_layout_video.c
+CFILES += abc_layout_audio.c
+
CFILES += abc_render.c
CFILES += abc_render_html.c
CFILES += abc_render_title.c
-CFILES += abc_render_meta.c
CFILES += abc_render_head.c
CFILES += abc_render_body.c
CFILES += abc_render_div.c
+CFILES += abc_render_empty.c
+
CFILES += abc_render_h1.c
CFILES += abc_render_a.c
CFILES += abc_render_a_href.c
CFILES += abc_render_tr.c
CFILES += abc_render_td.c
+CFILES += abc_render_video.c
+CFILES += abc_render_audio.c
+
CFILES += ../html/abc_html.c
CFILES += ../html/abc_html_util.c
CFILES += ../html/abc_obj.c
CFILES += ../html/abc_io_http.c
CFILES += ../ffmpeg/abc_ffmpeg_img.c
+CFILES += ../ffmpeg/abc_ffmpeg_input.c
+CFILES += ../ffmpeg/abc_ffmpeg_output.c
+CFILES += ../ffmpeg/abc_ffmpeg_alsa.c
+CFILES += ../ffmpeg/abc_vout_fifo.c
+CFILES += ../ffmpeg/abc_avio.c
+CFILES += ../ffmpeg/abc_filter.c
CFILES += ../util/scf_string.c
CFLAGS += -I../util
CFLAGS += `pkg-config --cflags gtk+-3.0`
-LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil
+LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil -lavdevice
LDFLAGS += -lm
LDFLAGS += -lGL
LDFLAGS += `pkg-config --libs gtk+-3.0`
int abc_layout_td (abc_layout_t* layout, abc_obj_t* obj, int width, int height);
+int abc_layout_video(abc_layout_t* layout, abc_obj_t* obj, int width, int height);
+int abc_layout_audio(abc_layout_t* layout, abc_obj_t* obj, int width, int height);
+
int abc_layout_empty(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
{
return 0;
static abc_layout_pt abc_layouts[ABC_HTML_NB] =
{
- [ABC_HTML] = abc_layout_html,
- [ABC_HTML_TITLE] = abc_layout_title,
- [ABC_HTML_HEAD] = abc_layout_head,
- [ABC_HTML_BODY] = abc_layout_body,
- [ABC_HTML_DIV] = abc_layout_div,
-
- [ABC_HTML_META] = abc_layout_empty,
-
- [ABC_HTML_H1] = abc_layout_h1,
- [ABC_HTML_H2] = abc_layout_h1,
- [ABC_HTML_H3] = abc_layout_h1,
- [ABC_HTML_H4] = abc_layout_h1,
- [ABC_HTML_H5] = abc_layout_h1,
- [ABC_HTML_H6] = abc_layout_h1,
-
- [ABC_HTML_P] = abc_layout_h1,
- [ABC_HTML_BR] = abc_layout_h1,
-
- [ABC_HTML_A] = abc_layout_a,
- [ABC_HTML_IMG] = abc_layout_img,
-
- [ABC_HTML_FORM] = abc_layout_form,
- [ABC_HTML_LABEL] = abc_layout_label,
- [ABC_HTML_INPUT] = abc_layout_input,
-
- [ABC_HTML_TABLE] = abc_layout_empty,
- [ABC_HTML_TR] = abc_layout_empty,
- [ABC_HTML_TD] = abc_layout_td,
+ [ABC_HTML] = abc_layout_html,
+ [ABC_HTML_TITLE] = abc_layout_title,
+ [ABC_HTML_HEAD] = abc_layout_head,
+ [ABC_HTML_BODY] = abc_layout_body,
+ [ABC_HTML_DIV] = abc_layout_div,
+
+ [ABC_HTML_META] = abc_layout_empty,
+
+ [ABC_HTML_H1] = abc_layout_h1,
+ [ABC_HTML_H2] = abc_layout_h1,
+ [ABC_HTML_H3] = abc_layout_h1,
+ [ABC_HTML_H4] = abc_layout_h1,
+ [ABC_HTML_H5] = abc_layout_h1,
+ [ABC_HTML_H6] = abc_layout_h1,
+
+ [ABC_HTML_P] = abc_layout_h1,
+ [ABC_HTML_BR] = abc_layout_h1,
+
+ [ABC_HTML_A] = abc_layout_a,
+ [ABC_HTML_IMG] = abc_layout_img,
+
+ [ABC_HTML_FORM] = abc_layout_form,
+ [ABC_HTML_LABEL] = abc_layout_label,
+ [ABC_HTML_INPUT] = abc_layout_input,
+
+ [ABC_HTML_TABLE] = abc_layout_empty,
+ [ABC_HTML_TR] = abc_layout_empty,
+ [ABC_HTML_TD] = abc_layout_td,
+
+ [ABC_HTML_VIDEO] = abc_layout_video,
+ [ABC_HTML_AUDIO] = abc_layout_audio,
+ [ABC_HTML_SOURCE] = abc_layout_empty,
+
+ [ABC_HTML_PLAY] = abc_layout_empty,
+ [ABC_HTML_PROGRESS] = abc_layout_empty,
};
int abc_layout_obj(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
--- /dev/null
+#include"abc.h"
+
+int abc_layout_audio(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ abc_obj_t* attr;
+
+ if (!obj->text)
+ return 0;
+
+// obj->x = 4;
+// obj->y = 4;
+
+ obj->w = (obj->w + 3) & ~0x3;
+ obj->h = (obj->h + 3) & ~0x3;
+
+ scf_logd("%s, w: %d, h: %d, width: %lg, height: %lg\n", obj->text->data, obj->w, obj->h, width, height);
+
+ return 0;
+}
--- /dev/null
+#include"abc.h"
+
+static int __av_filter_init(abc_obj_t* obj)
+{
+ scf_list_t* l;
+ abc_avio_t* avin = NULL;
+ abc_avio_t* vout = NULL;
+ abc_avio_t* aout = NULL;
+ abc_obj_t* attr;
+ abc_obj_t* src;
+
+ int ret = abc_filter_open(&obj->av_filter);
+ if (ret < 0) {
+ scf_loge("open av filter failed, file: %s, line: %d\n", obj->file->data, obj->line);
+ return ret;
+ }
+
+ for (l = scf_list_head(&obj->childs); l != scf_list_sentinel(&obj->childs); l = scf_list_next(l)) {
+ src = scf_list_data(l, abc_obj_t, list);
+
+ if (ABC_HTML_SOURCE != src->type)
+ continue;
+
+ attr = abc_obj_get_attr(src, ABC_HTML_ATTR_SRC);
+ if (!attr || !attr->value)
+ continue;
+
+ ret = abc_avio_open(&avin, "ffmpeg_input", attr->value->data, NULL);
+ if (ret >= 0)
+ break;
+
+ avin = NULL;
+ }
+
+ if (!avin) {
+ scf_loge("don't find source available, file: %s, line: %d\n", obj->file->data, obj->line);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = abc_filter_add_input(obj->av_filter, avin);
+ if (ret < 0) {
+ abc_avio_close(avin);
+ goto error;
+ }
+
+ if (avin->vopen) {
+ ret = abc_avio_open(&vout, "vout_fifo", NULL, "");
+ if (ret < 0)
+ goto error;
+
+ ret = abc_filter_add_output(obj->av_filter, vout);
+ if (ret < 0) {
+ abc_avio_close(vout);
+ goto error;
+ }
+ }
+
+ if (avin->aopen) {
+ ret = abc_avio_open(&aout, "ffmpeg_alsa", NULL, "hw:0");
+ if (ret < 0)
+ goto error;
+
+ ret = abc_filter_add_output(obj->av_filter, aout);
+ if (ret < 0) {
+ abc_avio_close(aout);
+ goto error;
+ }
+ }
+
+ obj->av_filter->speed.num = 1;
+ obj->av_filter->speed.den = 1;
+
+ ret = abc_filter_run(obj->av_filter);
+ if (ret < 0)
+ goto error;
+
+ if (aout) {
+ ret = abc_avio_run(aout);
+ if (ret < 0)
+ goto error;
+ }
+
+ if (vout) {
+ ret = abc_avio_run(vout);
+ if (ret < 0)
+ goto error;
+
+ obj->vout = vout;
+ }
+
+ ret = abc_avio_run(avin);
+ if (ret < 0)
+ goto error;
+
+ if (obj->w <= 0 || obj->h <= 0) {
+ obj->w = avin->width;
+ obj->h = avin->height;
+ }
+
+ return 0;
+
+error:
+ abc_filter_close(obj->av_filter, 0);
+ obj->av_filter = NULL;
+
+ obj->vout = NULL;
+ return ret;
+}
+
+int abc_layout_video(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ abc_obj_t* attr;
+
+ int w = 0;
+ int h = 0;
+
+ if (!obj->av_filter) {
+
+ int ret = __av_filter_init(obj);
+ if (ret < 0) {
+ scf_loge("open video failed\n");
+ return ret;
+ }
+
+ scf_logw("w: %d, h: %d, width: %d, height: %d\n", obj->w, obj->h, width, height);
+ }
+
+ attr = abc_obj_get_attr(obj, ABC_HTML_ATTR_WIDTH);
+ if (attr && attr->value)
+ w = atoi(attr->value->data);
+
+ attr = abc_obj_get_attr(obj, ABC_HTML_ATTR_HEIGHT);
+ if (attr && attr->value)
+ h = atoi(attr->value->data);
+
+ if (w > 0 && h > 0) {
+ obj->w = w;
+ obj->h = h;
+ }
+
+// obj->x = 4;
+// obj->y = 4;
+
+ obj->w = (obj->w + 3) & ~0x3;
+ obj->h = (obj->h + 3) & ~0x3;
+
+ scf_logd("w: %d, h: %d, width: %d, height: %d\n", obj->w, obj->h, width, height);
+ return 0;
+}
extern abc_render_t abc_render_tr;
extern abc_render_t abc_render_td;
+extern abc_render_t abc_render_video;
+extern abc_render_t abc_render_audio;
+
+extern abc_render_t abc_render_empty;
static abc_render_t* abc_renders[ABC_HTML_NB] =
{
- [ABC_HTML] = &abc_render_html,
- [ABC_HTML_TITLE] = &abc_render_title,
- [ABC_HTML_HEAD] = &abc_render_head,
- [ABC_HTML_BODY] = &abc_render_body,
- [ABC_HTML_DIV] = &abc_render_div,
- [ABC_HTML_META] = &abc_render_meta,
-
- [ABC_HTML_H1] = &abc_render_h1,
- [ABC_HTML_H2] = &abc_render_h1,
- [ABC_HTML_H3] = &abc_render_h1,
- [ABC_HTML_H4] = &abc_render_h1,
- [ABC_HTML_H5] = &abc_render_h1,
- [ABC_HTML_H6] = &abc_render_h1,
-
- [ABC_HTML_P] = &abc_render_h1,
- [ABC_HTML_BR] = &abc_render_h1,
-
- [ABC_HTML_A] = &abc_render_a,
- [ABC_HTML_A_HREF] = &abc_render_a_href,
- [ABC_HTML_IMG] = &abc_render_img,
-
- [ABC_HTML_FORM] = &abc_render_form,
- [ABC_HTML_LABEL] = &abc_render_label,
- [ABC_HTML_INPUT] = &abc_render_input,
-
- [ABC_HTML_TABLE] = &abc_render_table,
- [ABC_HTML_TR] = &abc_render_tr,
- [ABC_HTML_TD] = &abc_render_td,
+ [ABC_HTML] = &abc_render_empty,
+ [ABC_HTML_HEAD] = &abc_render_empty,
+ [ABC_HTML_BODY] = &abc_render_empty,
+ [ABC_HTML_META] = &abc_render_empty,
+ [ABC_HTML_TITLE] = &abc_render_title,
+ [ABC_HTML_DIV] = &abc_render_div,
+
+ [ABC_HTML_H1] = &abc_render_h1,
+ [ABC_HTML_H2] = &abc_render_h1,
+ [ABC_HTML_H3] = &abc_render_h1,
+ [ABC_HTML_H4] = &abc_render_h1,
+ [ABC_HTML_H5] = &abc_render_h1,
+ [ABC_HTML_H6] = &abc_render_h1,
+
+ [ABC_HTML_P] = &abc_render_h1,
+ [ABC_HTML_BR] = &abc_render_h1,
+
+ [ABC_HTML_A] = &abc_render_a,
+ [ABC_HTML_A_HREF] = &abc_render_a_href,
+ [ABC_HTML_IMG] = &abc_render_img,
+
+ [ABC_HTML_FORM] = &abc_render_form,
+ [ABC_HTML_LABEL] = &abc_render_label,
+ [ABC_HTML_INPUT] = &abc_render_input,
+
+ [ABC_HTML_TABLE] = &abc_render_table,
+ [ABC_HTML_TR] = &abc_render_tr,
+ [ABC_HTML_TD] = &abc_render_td,
+
+ [ABC_HTML_VIDEO] = &abc_render_video,
+ [ABC_HTML_AUDIO] = &abc_render_audio,
+ [ABC_HTML_SOURCE] = &abc_render_empty,
+
+ [ABC_HTML_PLAY] = &abc_render_empty,
+ [ABC_HTML_PROGRESS] = &abc_render_empty,
};
int abc_renders_fini()
--- /dev/null
+#include"abc.h"
+#include"abc_ffmpeg.h"
+
+static const char* vert_shader =
+ "#version 330 core\n"
+ "layout(location = 0) in vec4 position; \n"
+ "layout(location = 1) in vec2 a_texCoord; \n"
+ "out vec2 v_texCoord; \n"
+ "uniform mat4 mvp; \n"
+ "void main() { \n"
+ "gl_Position = mvp * position; \n"
+ "v_texCoord = a_texCoord; \n"
+ "} \n";
+
+static const char* frag_shader =
+ "#version 330 core\n"
+ "in vec2 v_texCoord; \n"
+ "out vec4 outputColor; \n"
+ "uniform sampler2D tex_rgba; \n"
+ "void main() { \n"
+ " vec2 xy = v_texCoord; \n"
+ " vec4 v = texture2D(tex_rgba, xy).rgba; \n"
+ " outputColor = vec4(v.b, v.g, v.r, 1.0); \n"
+ "} \n";
+
+
+static GLuint program = 0;
+
+static GLuint vao = 0;
+static GLuint buffers[2] = {0};
+static GLuint texture_rgba = 0;
+
+static GLuint uniform_mvp;
+static GLuint uniform_rgba;
+
+
+static int _render_fini_audio(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_audio(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ if (ABC_HTML_ATTR_SRC == attr->type)
+ break;
+ }
+
+ if (l == scf_list_sentinel(&obj->attrs)) {
+ scf_loge("src image of '%s' not found\n", obj->keys[0]);
+ return -1;
+ }
+
+ if (0 == program)
+ __init_program(&program, vert_shader, frag_shader);
+
+ if (0 == vao)
+ __init_buffers(&vao, buffers);
+
+// if (0 == texture_rgba)
+// __init_texture(&texture_rgba, GL_RGBA, audio->width, audio->height, NULL);
+
+ float mvp[16];
+ __compute_mvp(mvp, 0, 0, 0);
+
+ scf_logi("%s, x: %d, y: %d, w: %d, h: %d\n", obj->keys[0], obj->x, obj->y, obj->w, obj->h);
+
+ GLfloat vert_update[] =
+ {
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+ };
+
+ glUseProgram(program);
+ uniform_rgba = glGetUniformLocation(program, "tex_rgba");
+ uniform_mvp = glGetUniformLocation(program, "mvp");
+
+ glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp);
+
+ // board
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_rgba);
+// glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, audio->width, audio->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bgra);
+ glUniform1i(uniform_rgba, 0);
+
+ // draw
+ glBindBuffer (GL_ARRAY_BUFFER, buffers[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update);
+
+ glBindVertexArray(vao);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+// free(bgra);
+ return 0;
+}
+
+abc_render_t abc_render_audio =
+{
+ .type = ABC_HTML_AUDIO,
+
+ .draw = _render_draw_audio,
+ .fini = _render_fini_audio,
+};
--- /dev/null
+#include"abc.h"
+
+static int _render_fini_empty(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_empty(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_empty =
+{
+ .type = ABC_HTML,
+
+ .draw = _render_draw_empty,
+ .fini = _render_fini_empty,
+};
--- /dev/null
+#include"abc.h"
+#include"abc_ffmpeg.h"
+
+static const char* vert_shader =
+ "#version 330 core\n"
+ "layout(location = 0) in vec4 position; \n"
+ "layout(location = 1) in vec2 a_texCoord; \n"
+ "out vec2 v_texCoord; \n"
+ "uniform mat4 mvp; \n"
+ "void main() { \n"
+ "gl_Position = mvp * position; \n"
+ "v_texCoord = a_texCoord; \n"
+ "} \n";
+
+static const char* frag_shader =
+ "#version 330 core\n"
+ "in vec2 v_texCoord; \n"
+ "out vec4 outputColor; \n"
+ "uniform sampler2D tex_y; \n"
+ "uniform sampler2D tex_u; \n"
+ "uniform sampler2D tex_v; \n"
+ "void main() { \n"
+ " vec2 xy = v_texCoord; \n"
+ " float y = texture2D(tex_y, xy).r; \n"
+ " float u = texture2D(tex_u, xy).r - 0.5; \n"
+ " float v = texture2D(tex_v, xy).r - 0.5; \n"
+ " float r = y + 1.4075 * v; \n"
+ " float g = y - 0.3455 * u - 0.7169 * v; \n"
+ " float b = y + 1.779 * u; \n"
+ " outputColor = vec4(r, g, b, 1.0f); \n"
+ "} \n";
+
+
+static GLuint program = 0;
+
+static GLuint vao = 0;
+static GLuint buffers[2] = {0};
+static GLuint texture_y = 0;
+static GLuint texture_u = 0;
+static GLuint texture_v = 0;
+
+static GLuint uniform_mvp;
+static GLuint uniform_y;
+static GLuint uniform_u;
+static GLuint uniform_v;
+
+static int s_width = 0;
+static int s_height = 0;
+
+static int _render_fini_video(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_video(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ scf_list_t* l;
+ abc_frame_t* f;
+
+ if (!obj->av_filter || !obj->vout) {
+ scf_logw("%s, x: %d, y: %d, w: %d, h: %d\n", obj->keys[0], obj->x, obj->y, obj->w, obj->h);
+ return 0;
+ }
+
+ if (0 == program)
+ __init_program(&program, vert_shader, frag_shader);
+
+ if (0 == vao)
+ __init_buffers(&vao, buffers);
+
+ pthread_mutex_lock(&obj->vout->mutex);
+ if (!scf_list_empty(&obj->vout->vout)) {
+ l = scf_list_head(&obj->vout->vout);
+ f = scf_list_data(l, abc_frame_t, list);
+
+ scf_list_del(&f->list);
+ pthread_mutex_unlock(&obj->vout->mutex);
+
+ if (s_width != f->frame->width || s_height != f->frame->height) {
+ s_width = f->frame->width;
+ s_height = f->frame->height;
+
+ if (0 != texture_y) {
+ glDeleteTextures(1, &texture_y);
+ texture_y = 0;
+ }
+
+ if (0 != texture_u) {
+ glDeleteTextures(1, &texture_u);
+ texture_u = 0;
+ }
+
+ if (0 != texture_v) {
+ glDeleteTextures(1, &texture_v);
+ texture_v = 0;
+ }
+
+ __init_texture(&texture_y, GL_RED, f->frame->width, f->frame->height, NULL);
+ __init_texture(&texture_u, GL_RED, f->frame->width / 2, f->frame->height / 2, NULL);
+ __init_texture(&texture_v, GL_RED, f->frame->width / 2, f->frame->height / 2, NULL);
+ }
+ } else {
+ f = NULL;
+ pthread_mutex_unlock(&obj->vout->mutex);
+ }
+
+ float mvp[16];
+ __compute_mvp(mvp, 0, 0, 0);
+
+ scf_logd("%s, x: %d, y: %d, w: %d, h: %d\n", obj->keys[0], obj->x, obj->y, obj->w, obj->h);
+
+ GLfloat vert_update[] =
+ {
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+ };
+
+ glUseProgram(program);
+ uniform_y = glGetUniformLocation(program, "tex_y");
+ uniform_u = glGetUniformLocation(program, "tex_u");
+ uniform_v = glGetUniformLocation(program, "tex_v");
+ uniform_mvp = glGetUniformLocation(program, "mvp");
+
+ glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp);
+
+ int i;
+
+ // y
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_y);
+ if (f) {
+ for (i = 0; i < f->frame->height; i++)
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, f->frame->width, 1, GL_RED, GL_UNSIGNED_BYTE, f->frame->data[0] + i * f->frame->linesize[0]);
+ }
+ glUniform1i(uniform_y, 0);
+
+ // u
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture (GL_TEXTURE_2D, texture_u);
+ if (f) {
+ for (i = 0; i < f->frame->height / 2; i++)
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, f->frame->width / 2, 1, GL_RED, GL_UNSIGNED_BYTE, f->frame->data[1] + i * f->frame->linesize[1]);
+ }
+ glUniform1i(uniform_u, 1);
+
+ // v
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture (GL_TEXTURE_2D, texture_v);
+ if (f) {
+ for (i = 0; i < f->frame->height / 2; i++)
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, f->frame->width / 2, 1, GL_RED, GL_UNSIGNED_BYTE, f->frame->data[2] + i * f->frame->linesize[2]);
+ }
+ glUniform1i(uniform_v, 2);
+
+ // draw
+ glBindBuffer (GL_ARRAY_BUFFER, buffers[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update);
+
+ glBindVertexArray(vao);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ if (f)
+ abc_frame_free(f);
+ return 0;
+}
+
+abc_render_t abc_render_video =
+{
+ .type = ABC_HTML_VIDEO,
+
+ .draw = _render_draw_video,
+ .fini = _render_fini_video,
+};
int width = gtk_widget_get_allocated_width (GTK_WIDGET(self));
int height = gtk_widget_get_allocated_height(GTK_WIDGET(self));
- scf_logd("width: %d, height: %d\n\n", width, height);
+ scf_logd("root: %p, width: %d, height: %d\n\n", root, width, height);
glViewport(0, 0, width, height);
if (obj) {
obj->jiffies++;
gtk_gl_area_queue_render(ctx->gl_area);
+ } else {
+ if (ctx->current->has_video)
+ gtk_gl_area_queue_render(ctx->gl_area);
}
}
return -1;
}
+ avdevice_register_all();
+
gtk_init(&argc, &argv);
builder = gtk_builder_new();
scf_list_add_tail(&ctx.html_list, &html->list);
html->root->gtk_builder = builder;
+ html->current = html->root;
window = gtk_builder_get_object(builder, "main_window");
gl_area = gtk_builder_get_object(builder, "gl_area");
g_signal_connect(window, "key-press-event", G_CALLBACK(key_press_event), &ctx);
g_signal_connect(gl_area, "motion-notify-event", G_CALLBACK(button_move_event), &ctx);
- g_timeout_add(10, timer_handler, &ctx);
+ g_timeout_add(1, timer_handler, &ctx);
ctx.im = gtk_im_multicontext_new();
if (!ctx.im) {