1cabdff1aSopenharmony_ci#include <stdio.h>
2cabdff1aSopenharmony_ci#include <stdlib.h>
3cabdff1aSopenharmony_ci#include <string.h>
4cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
5cabdff1aSopenharmony_ci#include "libavdevice/avdevice.h"
6cabdff1aSopenharmony_ci#include "libavfilter/avfilter.h"
7cabdff1aSopenharmony_ci#include "libavfilter/buffersink.h"
8cabdff1aSopenharmony_ci#include "libavformat/avformat.h"
9cabdff1aSopenharmony_ci#include "libavcodec/codec_id.h"
10cabdff1aSopenharmony_ci
11cabdff1aSopenharmony_citypedef struct {
12cabdff1aSopenharmony_ci    AVFormatContext *mux;
13cabdff1aSopenharmony_ci    AVStream *stream;
14cabdff1aSopenharmony_ci    AVFilterContext *sink;
15cabdff1aSopenharmony_ci} Stream;
16cabdff1aSopenharmony_ci
17cabdff1aSopenharmony_cistatic int create_sink(Stream *st, AVFilterGraph *graph,
18cabdff1aSopenharmony_ci                       AVFilterContext *f, int idx)
19cabdff1aSopenharmony_ci{
20cabdff1aSopenharmony_ci    enum AVMediaType type = avfilter_pad_get_type(f->output_pads, idx);
21cabdff1aSopenharmony_ci    const char *sink_name;
22cabdff1aSopenharmony_ci    int ret;
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci    switch (type) {
25cabdff1aSopenharmony_ci    case AVMEDIA_TYPE_VIDEO: sink_name =  "buffersink"; break;
26cabdff1aSopenharmony_ci    case AVMEDIA_TYPE_AUDIO: sink_name = "abuffersink"; break;
27cabdff1aSopenharmony_ci    default:
28cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Stream type not supported\n");
29cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
30cabdff1aSopenharmony_ci    }
31cabdff1aSopenharmony_ci    ret = avfilter_graph_create_filter(&st->sink,
32cabdff1aSopenharmony_ci                                       avfilter_get_by_name(sink_name),
33cabdff1aSopenharmony_ci                                       NULL, NULL, NULL, graph);
34cabdff1aSopenharmony_ci    if (ret < 0)
35cabdff1aSopenharmony_ci        return ret;
36cabdff1aSopenharmony_ci    ret = avfilter_link(f, idx, st->sink, 0);
37cabdff1aSopenharmony_ci    if (ret < 0)
38cabdff1aSopenharmony_ci        return ret;
39cabdff1aSopenharmony_ci    return 0;
40cabdff1aSopenharmony_ci}
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_ciint main(int argc, char **argv)
43cabdff1aSopenharmony_ci{
44cabdff1aSopenharmony_ci    char *in_graph_desc, **out_dev_name;
45cabdff1aSopenharmony_ci    int nb_out_dev = 0, nb_streams = 0;
46cabdff1aSopenharmony_ci    AVFilterGraph *in_graph = NULL;
47cabdff1aSopenharmony_ci    Stream *streams = NULL, *st;
48cabdff1aSopenharmony_ci    AVFrame *frame = NULL;
49cabdff1aSopenharmony_ci    int i, j, run = 1, ret;
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_ci    //av_log_set_level(AV_LOG_DEBUG);
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci    if (argc < 3) {
54cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR,
55cabdff1aSopenharmony_ci               "Usage: %s filter_graph dev:out [dev2:out2...]\n\n"
56cabdff1aSopenharmony_ci               "Examples:\n"
57cabdff1aSopenharmony_ci               "%s movie=file.nut:s=v+a xv:- alsa:default\n"
58cabdff1aSopenharmony_ci               "%s movie=file.nut:s=v+a uncodedframecrc:pipe:0\n",
59cabdff1aSopenharmony_ci               argv[0], argv[0], argv[0]);
60cabdff1aSopenharmony_ci        exit(1);
61cabdff1aSopenharmony_ci    }
62cabdff1aSopenharmony_ci    in_graph_desc = argv[1];
63cabdff1aSopenharmony_ci    out_dev_name = argv + 2;
64cabdff1aSopenharmony_ci    nb_out_dev = argc - 2;
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci    avdevice_register_all();
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci    /* Create input graph */
69cabdff1aSopenharmony_ci    if (!(in_graph = avfilter_graph_alloc())) {
70cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
71cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Unable to alloc graph graph: %s\n",
72cabdff1aSopenharmony_ci               av_err2str(ret));
73cabdff1aSopenharmony_ci        goto fail;
74cabdff1aSopenharmony_ci    }
75cabdff1aSopenharmony_ci    ret = avfilter_graph_parse_ptr(in_graph, in_graph_desc, NULL, NULL, NULL);
76cabdff1aSopenharmony_ci    if (ret < 0) {
77cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Unable to parse graph: %s\n",
78cabdff1aSopenharmony_ci               av_err2str(ret));
79cabdff1aSopenharmony_ci        goto fail;
80cabdff1aSopenharmony_ci    }
81cabdff1aSopenharmony_ci    nb_streams = 0;
82cabdff1aSopenharmony_ci    for (i = 0; i < in_graph->nb_filters; i++) {
83cabdff1aSopenharmony_ci        AVFilterContext *f = in_graph->filters[i];
84cabdff1aSopenharmony_ci        for (j = 0; j < f->nb_inputs; j++) {
85cabdff1aSopenharmony_ci            if (!f->inputs[j]) {
86cabdff1aSopenharmony_ci                av_log(NULL, AV_LOG_ERROR, "Graph has unconnected inputs\n");
87cabdff1aSopenharmony_ci                ret = AVERROR(EINVAL);
88cabdff1aSopenharmony_ci                goto fail;
89cabdff1aSopenharmony_ci            }
90cabdff1aSopenharmony_ci        }
91cabdff1aSopenharmony_ci        for (j = 0; j < f->nb_outputs; j++)
92cabdff1aSopenharmony_ci            if (!f->outputs[j])
93cabdff1aSopenharmony_ci                nb_streams++;
94cabdff1aSopenharmony_ci    }
95cabdff1aSopenharmony_ci    if (!nb_streams) {
96cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Graph has no output stream\n");
97cabdff1aSopenharmony_ci        ret = AVERROR(EINVAL);
98cabdff1aSopenharmony_ci        goto fail;
99cabdff1aSopenharmony_ci    }
100cabdff1aSopenharmony_ci    if (nb_out_dev != 1 && nb_out_dev != nb_streams) {
101cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR,
102cabdff1aSopenharmony_ci               "Graph has %d output streams, %d devices given\n",
103cabdff1aSopenharmony_ci               nb_streams, nb_out_dev);
104cabdff1aSopenharmony_ci        ret = AVERROR(EINVAL);
105cabdff1aSopenharmony_ci        goto fail;
106cabdff1aSopenharmony_ci    }
107cabdff1aSopenharmony_ci
108cabdff1aSopenharmony_ci    if (!(streams = av_calloc(nb_streams, sizeof(*streams)))) {
109cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
110cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Could not allocate streams\n");
111cabdff1aSopenharmony_ci    }
112cabdff1aSopenharmony_ci    st = streams;
113cabdff1aSopenharmony_ci    for (i = 0; i < in_graph->nb_filters; i++) {
114cabdff1aSopenharmony_ci        AVFilterContext *f = in_graph->filters[i];
115cabdff1aSopenharmony_ci        for (j = 0; j < f->nb_outputs; j++) {
116cabdff1aSopenharmony_ci            if (!f->outputs[j]) {
117cabdff1aSopenharmony_ci                if ((ret = create_sink(st++, in_graph, f, j)) < 0)
118cabdff1aSopenharmony_ci                    goto fail;
119cabdff1aSopenharmony_ci            }
120cabdff1aSopenharmony_ci        }
121cabdff1aSopenharmony_ci    }
122cabdff1aSopenharmony_ci    av_assert0(st - streams == nb_streams);
123cabdff1aSopenharmony_ci    if ((ret = avfilter_graph_config(in_graph, NULL)) < 0) {
124cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR, "Failed to configure graph\n");
125cabdff1aSopenharmony_ci        goto fail;
126cabdff1aSopenharmony_ci    }
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_ci    /* Create output devices */
129cabdff1aSopenharmony_ci    for (i = 0; i < nb_out_dev; i++) {
130cabdff1aSopenharmony_ci        char *fmt = NULL, *dev = out_dev_name[i];
131cabdff1aSopenharmony_ci        st = &streams[i];
132cabdff1aSopenharmony_ci        if ((dev = strchr(dev, ':'))) {
133cabdff1aSopenharmony_ci            *(dev++) = 0;
134cabdff1aSopenharmony_ci            fmt = out_dev_name[i];
135cabdff1aSopenharmony_ci        }
136cabdff1aSopenharmony_ci        ret = avformat_alloc_output_context2(&st->mux, NULL, fmt, dev);
137cabdff1aSopenharmony_ci        if (ret < 0) {
138cabdff1aSopenharmony_ci            av_log(NULL, AV_LOG_ERROR, "Failed to allocate output: %s\n",
139cabdff1aSopenharmony_ci                   av_err2str(ret));
140cabdff1aSopenharmony_ci            goto fail;
141cabdff1aSopenharmony_ci        }
142cabdff1aSopenharmony_ci        if (!(st->mux->oformat->flags & AVFMT_NOFILE)) {
143cabdff1aSopenharmony_ci            ret = avio_open2(&st->mux->pb, st->mux->url, AVIO_FLAG_WRITE,
144cabdff1aSopenharmony_ci                             NULL, NULL);
145cabdff1aSopenharmony_ci            if (ret < 0) {
146cabdff1aSopenharmony_ci                av_log(st->mux, AV_LOG_ERROR, "Failed to init output: %s\n",
147cabdff1aSopenharmony_ci                       av_err2str(ret));
148cabdff1aSopenharmony_ci                goto fail;
149cabdff1aSopenharmony_ci            }
150cabdff1aSopenharmony_ci        }
151cabdff1aSopenharmony_ci    }
152cabdff1aSopenharmony_ci    for (; i < nb_streams; i++)
153cabdff1aSopenharmony_ci        streams[i].mux = streams[0].mux;
154cabdff1aSopenharmony_ci
155cabdff1aSopenharmony_ci    /* Create output device streams */
156cabdff1aSopenharmony_ci    for (i = 0; i < nb_streams; i++) {
157cabdff1aSopenharmony_ci        st = &streams[i];
158cabdff1aSopenharmony_ci        if (!(st->stream = avformat_new_stream(st->mux, NULL))) {
159cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
160cabdff1aSopenharmony_ci            av_log(NULL, AV_LOG_ERROR, "Failed to create output stream\n");
161cabdff1aSopenharmony_ci            goto fail;
162cabdff1aSopenharmony_ci        }
163cabdff1aSopenharmony_ci        st->stream->codecpar->codec_type = av_buffersink_get_type(st->sink);
164cabdff1aSopenharmony_ci        st->stream->time_base = av_buffersink_get_time_base(st->sink);
165cabdff1aSopenharmony_ci        switch (av_buffersink_get_type(st->sink)) {
166cabdff1aSopenharmony_ci        case AVMEDIA_TYPE_VIDEO:
167cabdff1aSopenharmony_ci            st->stream->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
168cabdff1aSopenharmony_ci            st->stream->avg_frame_rate =
169cabdff1aSopenharmony_ci            st->stream->  r_frame_rate = av_buffersink_get_frame_rate(st->sink);
170cabdff1aSopenharmony_ci            st->stream->codecpar->width               = av_buffersink_get_w(st->sink);
171cabdff1aSopenharmony_ci            st->stream->codecpar->height              = av_buffersink_get_h(st->sink);
172cabdff1aSopenharmony_ci            st->stream->codecpar->sample_aspect_ratio = av_buffersink_get_sample_aspect_ratio(st->sink);
173cabdff1aSopenharmony_ci            st->stream->codecpar->format              = av_buffersink_get_format(st->sink);
174cabdff1aSopenharmony_ci            break;
175cabdff1aSopenharmony_ci        case AVMEDIA_TYPE_AUDIO:
176cabdff1aSopenharmony_ci            ret = av_buffersink_get_ch_layout(st->sink, &st->stream->codecpar->ch_layout);
177cabdff1aSopenharmony_ci            if (ret < 0)
178cabdff1aSopenharmony_ci                goto fail;
179cabdff1aSopenharmony_ci            st->stream->codecpar->sample_rate    = av_buffersink_get_sample_rate(st->sink);
180cabdff1aSopenharmony_ci            st->stream->codecpar->format         = av_buffersink_get_format(st->sink);
181cabdff1aSopenharmony_ci            st->stream->codecpar->codec_id       = av_get_pcm_codec(st->stream->codecpar->format, -1);
182cabdff1aSopenharmony_ci            break;
183cabdff1aSopenharmony_ci        default:
184cabdff1aSopenharmony_ci            av_assert0(!"reached");
185cabdff1aSopenharmony_ci        }
186cabdff1aSopenharmony_ci    }
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci    /* Init output devices */
189cabdff1aSopenharmony_ci    for (i = 0; i < nb_out_dev; i++) {
190cabdff1aSopenharmony_ci        st = &streams[i];
191cabdff1aSopenharmony_ci        if ((ret = avformat_write_header(st->mux, NULL)) < 0) {
192cabdff1aSopenharmony_ci            av_log(st->mux, AV_LOG_ERROR, "Failed to init output: %s\n",
193cabdff1aSopenharmony_ci                   av_err2str(ret));
194cabdff1aSopenharmony_ci            goto fail;
195cabdff1aSopenharmony_ci        }
196cabdff1aSopenharmony_ci    }
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_ci    /* Check output devices */
199cabdff1aSopenharmony_ci    for (i = 0; i < nb_streams; i++) {
200cabdff1aSopenharmony_ci        st = &streams[i];
201cabdff1aSopenharmony_ci        ret = av_write_uncoded_frame_query(st->mux, st->stream->index);
202cabdff1aSopenharmony_ci        if (ret < 0) {
203cabdff1aSopenharmony_ci            av_log(st->mux, AV_LOG_ERROR,
204cabdff1aSopenharmony_ci                   "Uncoded frames not supported on stream #%d: %s\n",
205cabdff1aSopenharmony_ci                   i, av_err2str(ret));
206cabdff1aSopenharmony_ci            goto fail;
207cabdff1aSopenharmony_ci        }
208cabdff1aSopenharmony_ci    }
209cabdff1aSopenharmony_ci
210cabdff1aSopenharmony_ci    while (run) {
211cabdff1aSopenharmony_ci        ret = avfilter_graph_request_oldest(in_graph);
212cabdff1aSopenharmony_ci        if (ret < 0) {
213cabdff1aSopenharmony_ci            if (ret == AVERROR_EOF) {
214cabdff1aSopenharmony_ci                run = 0;
215cabdff1aSopenharmony_ci            } else {
216cabdff1aSopenharmony_ci                av_log(NULL, AV_LOG_ERROR, "Error filtering: %s\n",
217cabdff1aSopenharmony_ci                       av_err2str(ret));
218cabdff1aSopenharmony_ci                break;
219cabdff1aSopenharmony_ci            }
220cabdff1aSopenharmony_ci        }
221cabdff1aSopenharmony_ci        for (i = 0; i < nb_streams; i++) {
222cabdff1aSopenharmony_ci            st = &streams[i];
223cabdff1aSopenharmony_ci            while (1) {
224cabdff1aSopenharmony_ci                if (!frame && !(frame = av_frame_alloc())) {
225cabdff1aSopenharmony_ci                    ret = AVERROR(ENOMEM);
226cabdff1aSopenharmony_ci                    av_log(NULL, AV_LOG_ERROR, "Could not allocate frame\n");
227cabdff1aSopenharmony_ci                    goto fail;
228cabdff1aSopenharmony_ci                }
229cabdff1aSopenharmony_ci                ret = av_buffersink_get_frame_flags(st->sink, frame,
230cabdff1aSopenharmony_ci                                                    AV_BUFFERSINK_FLAG_NO_REQUEST);
231cabdff1aSopenharmony_ci                if (ret < 0) {
232cabdff1aSopenharmony_ci                    if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
233cabdff1aSopenharmony_ci                        av_log(NULL, AV_LOG_WARNING, "Error in sink: %s\n",
234cabdff1aSopenharmony_ci                               av_err2str(ret));
235cabdff1aSopenharmony_ci                    break;
236cabdff1aSopenharmony_ci                }
237cabdff1aSopenharmony_ci                if (frame->pts != AV_NOPTS_VALUE)
238cabdff1aSopenharmony_ci                    frame->pts = av_rescale_q(frame->pts,
239cabdff1aSopenharmony_ci                                              av_buffersink_get_time_base(st->sink),
240cabdff1aSopenharmony_ci                                              st->stream->time_base);
241cabdff1aSopenharmony_ci                ret = av_interleaved_write_uncoded_frame(st->mux,
242cabdff1aSopenharmony_ci                                                         st->stream->index,
243cabdff1aSopenharmony_ci                                                         frame);
244cabdff1aSopenharmony_ci                frame = NULL;
245cabdff1aSopenharmony_ci                if (ret < 0) {
246cabdff1aSopenharmony_ci                    av_log(st->mux, AV_LOG_ERROR,
247cabdff1aSopenharmony_ci                           "Error writing frame: %s\n", av_err2str(ret));
248cabdff1aSopenharmony_ci                    goto fail;
249cabdff1aSopenharmony_ci                }
250cabdff1aSopenharmony_ci            }
251cabdff1aSopenharmony_ci        }
252cabdff1aSopenharmony_ci    }
253cabdff1aSopenharmony_ci    ret = 0;
254cabdff1aSopenharmony_ci
255cabdff1aSopenharmony_ci    for (i = 0; i < nb_out_dev; i++) {
256cabdff1aSopenharmony_ci        st = &streams[i];
257cabdff1aSopenharmony_ci        av_write_trailer(st->mux);
258cabdff1aSopenharmony_ci    }
259cabdff1aSopenharmony_ci
260cabdff1aSopenharmony_cifail:
261cabdff1aSopenharmony_ci    av_frame_free(&frame);
262cabdff1aSopenharmony_ci    avfilter_graph_free(&in_graph);
263cabdff1aSopenharmony_ci    if (streams) {
264cabdff1aSopenharmony_ci        for (i = 0; i < nb_out_dev; i++) {
265cabdff1aSopenharmony_ci            st = &streams[i];
266cabdff1aSopenharmony_ci            if (st->mux) {
267cabdff1aSopenharmony_ci                if (st->mux->pb)
268cabdff1aSopenharmony_ci                    avio_closep(&st->mux->pb);
269cabdff1aSopenharmony_ci                avformat_free_context(st->mux);
270cabdff1aSopenharmony_ci            }
271cabdff1aSopenharmony_ci        }
272cabdff1aSopenharmony_ci    }
273cabdff1aSopenharmony_ci    av_freep(&streams);
274cabdff1aSopenharmony_ci    return ret < 0;
275cabdff1aSopenharmony_ci}
276