support HTML label <video> & <audio>, update to ffmpeg-7.1.1
authoryu.dongliang <18588496441@163.com>
Wed, 9 Jul 2025 15:35:08 +0000 (23:35 +0800)
committeryu.dongliang <18588496441@163.com>
Wed, 9 Jul 2025 15:35:08 +0000 (23:35 +0800)
26 files changed:
examples/video.html [new file with mode: 0644]
ffmpeg/Makefile
ffmpeg/abc_avio.c [new file with mode: 0644]
ffmpeg/abc_ffmpeg.h
ffmpeg/abc_ffmpeg_alsa.c [new file with mode: 0644]
ffmpeg/abc_ffmpeg_img.c
ffmpeg/abc_ffmpeg_input.c [new file with mode: 0644]
ffmpeg/abc_ffmpeg_output.c [new file with mode: 0644]
ffmpeg/abc_filter.c [new file with mode: 0644]
ffmpeg/abc_vout_fifo.c [new file with mode: 0644]
ffmpeg/main_img.c [new file with mode: 0644]
ffmpeg/main_player.c [new file with mode: 0644]
html/Makefile
html/abc_html.c
html/abc_html.h
html/abc_obj.c
html/abc_obj.h
ui/Makefile
ui/abc_layout.c
ui/abc_layout_audio.c [new file with mode: 0644]
ui/abc_layout_video.c [new file with mode: 0644]
ui/abc_render.c
ui/abc_render_audio.c [new file with mode: 0644]
ui/abc_render_empty.c [new file with mode: 0644]
ui/abc_render_video.c [new file with mode: 0644]
ui/main.c

diff --git a/examples/video.html b/examples/video.html
new file mode 100644 (file)
index 0000000..15aa245
--- /dev/null
@@ -0,0 +1,15 @@
+<!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>
index 1b8f12a00d57e3eb9a8db80910f2841346257126..417406053cb1390152408b5750c47e772f7ff384 100644 (file)
@@ -1,14 +1,21 @@
 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)
diff --git a/ffmpeg/abc_avio.c b/ffmpeg/abc_avio.c
new file mode 100644 (file)
index 0000000..7874fcc
--- /dev/null
@@ -0,0 +1,123 @@
+#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;
+}
index 60c8620db7a2b8af8e6bd963ce0e0bafe64fad64..577020f466326218f832f103499e2d86c0782021 100644 (file)
 #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
diff --git a/ffmpeg/abc_ffmpeg_alsa.c b/ffmpeg/abc_ffmpeg_alsa.c
new file mode 100644 (file)
index 0000000..47671a6
--- /dev/null
@@ -0,0 +1,605 @@
+#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,
+};
index a9bf794f3fbd16951be40d4bb9c764f9b564ce2e..39380c880ad62aa68febfcd919b9003f9636a705 100644 (file)
@@ -15,8 +15,6 @@ static int __ffmpeg_init(abc_img_t* img, const char* filename)
        }
 
        AVStream* s = NULL;
-       AVCodec*  c = NULL;
-
        int i;
        for (i = 0; i < img->fmt_ctx->nb_streams; i++) {
                s  =        img->fmt_ctx->streams[i];
@@ -32,7 +30,7 @@ static int __ffmpeg_init(abc_img_t* img, const char* filename)
                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;
diff --git a/ffmpeg/abc_ffmpeg_input.c b/ffmpeg/abc_ffmpeg_input.c
new file mode 100644 (file)
index 0000000..2dd1ce2
--- /dev/null
@@ -0,0 +1,387 @@
+#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,
+};
diff --git a/ffmpeg/abc_ffmpeg_output.c b/ffmpeg/abc_ffmpeg_output.c
new file mode 100644 (file)
index 0000000..c83c680
--- /dev/null
@@ -0,0 +1,498 @@
+#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,
+};
diff --git a/ffmpeg/abc_filter.c b/ffmpeg/abc_filter.c
new file mode 100644 (file)
index 0000000..15023da
--- /dev/null
@@ -0,0 +1,610 @@
+#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;
+}
diff --git a/ffmpeg/abc_vout_fifo.c b/ffmpeg/abc_vout_fifo.c
new file mode 100644 (file)
index 0000000..4bae227
--- /dev/null
@@ -0,0 +1,34 @@
+#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,
+};
diff --git a/ffmpeg/main_img.c b/ffmpeg/main_img.c
new file mode 100644 (file)
index 0000000..92093b4
--- /dev/null
@@ -0,0 +1,49 @@
+#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;
+}
diff --git a/ffmpeg/main_player.c b/ffmpeg/main_player.c
new file mode 100644 (file)
index 0000000..5b631de
--- /dev/null
@@ -0,0 +1,80 @@
+#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;
+}
index fe589e279f434bd5c5e25829309bbc41615073b5..417f8accd3ee1b209fba811125704274dc31e07d 100644 (file)
@@ -7,8 +7,9 @@ CFILES += abc_html_util.c
 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
 
index 0dd2cd48b8360cc5e7386a4bac467a72326a5768..60da5b0ab16a8e058e7d6859916dff131075f59a 100644 (file)
@@ -47,6 +47,7 @@ static char* id_keys[]         = {"id",         "编号",     NULL};
 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};
@@ -58,6 +59,7 @@ static char* charset_keys[]    = {"charset",    "字符集",   NULL};
 
 static char* enctype_keys[]    = {"enctype",    NULL};
 
+
 // HTML labels
 static char* html_keys[]       = {"html",       "网页",     NULL};
 static char* head_keys[]       = {"head",       "头部",     NULL};
@@ -73,6 +75,10 @@ static char* title_keys[]      = {"title",      "标题",     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};
@@ -89,6 +95,10 @@ static char* h6_keys[]         = {"h6",         "标题6",    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},
@@ -160,6 +170,24 @@ static html_attr_t  img_attrs[] =
        {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},
@@ -192,37 +220,44 @@ static html_attr_t  form_attrs[] =
 
 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);
@@ -268,6 +303,37 @@ static int __html_add_attr(abc_obj_t* obj, int type, char** names, const char* v
        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;
@@ -885,6 +951,20 @@ static int __html_parse_obj(abc_html_t* html, abc_char_t* c)
        } 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;
 }
index f235163c50d8daf0df27b3a0da07f66e096b1946..20896a53355314e8b0d11be837db745f384dc6b3 100644 (file)
@@ -52,7 +52,8 @@ struct abc_html_s
        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);
index 8c493289dc59c562e9bb5dd14b4f5cf21e6f3345..613e0e8b783b9965f45a245d907246d9cdb3e5a3 100644 (file)
@@ -39,6 +39,11 @@ void abc_obj_free(abc_obj_t* obj)
                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);
 
@@ -131,6 +136,9 @@ void abc_obj_print(abc_obj_t* obj)
        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)
@@ -177,6 +185,9 @@ static int __abc_obj_to_string(abc_obj_t* obj, scf_string_t* s)
        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)
index 4780ca0f47555acbe5cbce31292486f569854be9..de20d3eeff57d7a57b463f1ff440949ea07436cd 100644 (file)
@@ -3,6 +3,7 @@
 
 #include"scf_string.h"
 #include"scf_list.h"
+#include"abc_ffmpeg.h"
 
 typedef struct abc_obj_s   abc_obj_t;
 
@@ -46,6 +47,13 @@ enum abc_objs
        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,
@@ -77,6 +85,8 @@ enum abc_objs
        ABC_HTML_ATTR_CHARSET,
 
        ABC_HTML_ATTR_ENCTYPE,
+
+       ABC_HTML_ATTR_CONTROLS,
 };
 
 struct abc_obj_s
@@ -89,6 +99,8 @@ struct abc_obj_s
        abc_obj_t*      parent;
 
        void*           gtk_builder;
+       abc_filter_t*   av_filter;
+       abc_avio_t*     vout;
 
        int             type;
 
index cc9fd71ede2f7101d3c7c9be6d26f3e9a2beb17b..578244e31e91a7d1c775cb334d7307f2856e3a02 100644 (file)
@@ -9,19 +9,24 @@ CFILES += abc_layout_div.c
 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
@@ -34,6 +39,9 @@ CFILES += abc_render_table.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
@@ -41,6 +49,12 @@ CFILES += ../html/abc_io_file.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
 
@@ -51,7 +65,7 @@ CFLAGS += -I../ffmpeg
 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`
index 4411a10d135246c2ae0efd383a93efbcf9d499af..cef3d3263a843f8b06258fb2416b3abdd4ff6589 100644 (file)
@@ -16,6 +16,9 @@ int abc_layout_input(abc_layout_t* layout, abc_obj_t* obj, int width, int height
 
 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;
@@ -23,34 +26,41 @@ int abc_layout_empty(abc_layout_t* layout, abc_obj_t* obj, int width, int height
 
 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)
diff --git a/ui/abc_layout_audio.c b/ui/abc_layout_audio.c
new file mode 100644 (file)
index 0000000..b8b7f75
--- /dev/null
@@ -0,0 +1,19 @@
+#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;
+}
diff --git a/ui/abc_layout_video.c b/ui/abc_layout_video.c
new file mode 100644 (file)
index 0000000..c7caa07
--- /dev/null
@@ -0,0 +1,150 @@
+#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;
+}
index 81d433418d76c17ecd0f54c7572c154b67977b07..5124e85b8d424b13135f1c12f80c7df52030b62d 100644 (file)
@@ -21,37 +21,48 @@ extern abc_render_t  abc_render_table;
 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()
diff --git a/ui/abc_render_audio.c b/ui/abc_render_audio.c
new file mode 100644 (file)
index 0000000..a6106c1
--- /dev/null
@@ -0,0 +1,121 @@
+#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,
+};
diff --git a/ui/abc_render_empty.c b/ui/abc_render_empty.c
new file mode 100644 (file)
index 0000000..7677b9d
--- /dev/null
@@ -0,0 +1,19 @@
+#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,
+};
diff --git a/ui/abc_render_video.c b/ui/abc_render_video.c
new file mode 100644 (file)
index 0000000..877abca
--- /dev/null
@@ -0,0 +1,186 @@
+#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,
+};
index 8738b0403f6002f89e63bfde009505580b658223..6556e516746338eeea00f791a7f0cbd757dba9a5 100644 (file)
--- a/ui/main.c
+++ b/ui/main.c
@@ -37,7 +37,7 @@ static gboolean render(GtkGLArea* self, GdkGLContext* context, gpointer user_dat
        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);
 
@@ -397,6 +397,9 @@ static gboolean timer_handler(gpointer user_data)
                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);
                }
        }
 
@@ -473,6 +476,8 @@ int main(int argc, char *argv[])
                return -1;
        }
 
+       avdevice_register_all();
+
        gtk_init(&argc, &argv);
 
        builder = gtk_builder_new();
@@ -501,6 +506,7 @@ int main(int argc, char *argv[])
        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");
@@ -533,7 +539,7 @@ int main(int argc, char *argv[])
        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) {