| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| \input texinfo @c -*- texinfo -*- | ||
| @documentencoding UTF-8 | ||
|
|
||
| @settitle Community | ||
| @titlepage | ||
| @center @titlefont{Community} | ||
| @end titlepage | ||
|
|
||
| @top | ||
|
|
||
| @contents | ||
|
|
||
| @anchor{Organisation} | ||
| @chapter Organisation | ||
|
|
||
| The FFmpeg project is organized through a community working on global consensus. | ||
|
|
||
| Decisions are taken by the ensemble of active members, through voting and are aided by two committees. | ||
|
|
||
| @anchor{General Assembly} | ||
| @chapter General Assembly | ||
|
|
||
| The ensemble of active members is called the General Assembly (GA). | ||
|
|
||
| The General Assembly is sovereign and legitimate for all its decisions regarding the FFmpeg project. | ||
|
|
||
| The General Assembly is made up of active contributors. | ||
|
|
||
| Contributors are considered "active contributors" if they have authored more than 20 patches in the last 36 months in the main FFmpeg repository, or if they have been voted in by the GA. | ||
|
|
||
| The list of active contributors is updated twice each year, on 1st January and 1st July, 0:00 UTC. | ||
|
|
||
| Additional members are added to the General Assembly through a vote after proposal by a member of the General Assembly. They are part of the GA for two years, after which they need a confirmation by the GA. | ||
|
|
||
| A script to generate the current members of the general assembly (minus members voted in) can be found in `tools/general_assembly.pl`. | ||
|
|
||
| @anchor{Voting} | ||
| @chapter Voting | ||
|
|
||
| Voting is done using a ranked voting system, currently running on https://vote.ffmpeg.org/ . | ||
|
|
||
| Majority vote means more than 50% of the expressed ballots. | ||
|
|
||
| @anchor{Technical Committee} | ||
| @chapter Technical Committee | ||
|
|
||
| The Technical Committee (TC) is here to arbitrate and make decisions when technical conflicts occur in the project. They will consider the merits of all the positions, judge them and make a decision. | ||
|
|
||
| The TC resolves technical conflicts but is not a technical steering committee. | ||
|
|
||
| Decisions by the TC are binding for all the contributors. | ||
|
|
||
| Decisions made by the TC can be re-opened after 1 year or by a majority vote of the General Assembly, requested by one of the member of the GA. | ||
|
|
||
| The TC is elected by the General Assembly for a duration of 1 year, and is composed of 5 members. Members can be re-elected if they wish. A majority vote in the General Assembly can trigger a new election of the TC. | ||
|
|
||
| The members of the TC can be elected from outside of the GA. Candidates for election can either be suggested or self-nominated. | ||
|
|
||
| The conflict resolution process is detailed in the resolution process document. | ||
|
|
||
| The TC can be contacted at <tc@@ffmpeg>. | ||
|
|
||
| @anchor{Resolution Process} | ||
| @section Resolution Process | ||
|
|
||
| The Technical Committee (TC) is here to arbitrate and make decisions when technical conflicts occur in the project. | ||
|
|
||
| The TC main role is to resolve technical conflicts. It is therefore not a technical steering committee, but it is understood that some decisions might impact the future of the project. | ||
|
|
||
| @subsection Seizing | ||
|
|
||
| The TC can take possession of any technical matter that it sees fit. | ||
|
|
||
| To involve the TC in a matter, email tc@ or CC them on an ongoing discussion. | ||
|
|
||
| As members of TC are developers, they also can email tc@ to raise an issue. | ||
| @subsection Announcement | ||
|
|
||
| The TC, once seized, must announce itself on the main mailing list, with a [TC] tag. | ||
|
|
||
| The TC has 2 modes of operation: a RFC one and an internal one. | ||
|
|
||
| If the TC thinks it needs the input from the larger community, the TC can call for a RFC. Else, it can decide by itself. | ||
|
|
||
| The decision to use a RFC process or an internal discussion is a discretionary decision of the TC. | ||
|
|
||
| The TC can also reject a seizure for a few reasons such as: the matter was not discussed enough previously; it lacks expertise to reach a beneficial decision on the matter; or the matter is too trivial. | ||
| @subsection RFC call | ||
|
|
||
| In the RFC mode, one person from the TC posts on the mailing list the technical question and will request input from the community. | ||
|
|
||
| The mail will have the following specification: | ||
|
|
||
| a precise title | ||
| a specific tag [TC RFC] | ||
| a top-level email | ||
| contain a precise question that does not exceed 100 words and that is answerable by developers | ||
| may have an extra description, or a link to a previous discussion, if deemed necessary, | ||
| contain a precise end date for the answers. | ||
|
|
||
| The answers from the community must be on the main mailing list and must have the following specification: | ||
|
|
||
| keep the tag and the title unchanged | ||
| limited to 400 words | ||
| a first-level, answering directly to the main email | ||
| answering to the question. | ||
|
|
||
| Further replies to answers are permitted, as long as they conform to the community standards of politeness, they are limited to 100 words, and are not nested more than once. (max-depth=2) | ||
|
|
||
| After the end-date, mails on the thread will be ignored. | ||
|
|
||
| Violations of those rules will be escalated through the Community Committee. | ||
|
|
||
| After all the emails are in, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay, that will be notified on the mailing list. | ||
| @subsection Within TC | ||
|
|
||
| In the internal case, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay. | ||
| @subsection Decisions | ||
|
|
||
| The decisions from the TC will be sent on the mailing list, with the [TC] tag. | ||
|
|
||
| Internally, the TC should take decisions with a majority, or using ranked-choice voting. | ||
|
|
||
| Each TC member must vote on such decision according to what is, in their view, best for the project. | ||
|
|
||
| If a TC member feels they are affected by a conflict of interest with regards to the case, they should announce it and recuse themselves from the TC | ||
| discussion and vote. | ||
|
|
||
| A conflict of interest is presumed to occur when a TC member has a personal interest (e.g. financial) in a specific outcome of the case. | ||
|
|
||
| The decision from the TC should be published with a summary of the reasons that lead to this decision. | ||
|
|
||
| The decisions from the TC are final, until the matters are reopened after no less than one year. | ||
|
|
||
| @anchor{Community Committee} | ||
| @chapter Community Committee | ||
|
|
||
| The Community Committee (CC) is here to arbitrage and make decisions when inter-personal conflicts occur in the project. It will decide quickly and take actions, for the sake of the project. | ||
|
|
||
| The CC can remove privileges of offending members, including removal of commit access and temporary ban from the community. | ||
|
|
||
| Decisions made by the CC can be re-opened after 1 year or by a majority vote of the General Assembly. Indefinite bans from the community must be confirmed by the General Assembly, in a majority vote. | ||
|
|
||
| The CC is elected by the General Assembly for a duration of 1 year, and is composed of 5 members. Members can be re-elected if they wish. A majority vote in the General Assembly can trigger a new election of the CC. | ||
|
|
||
| The members of the CC can be elected from outside of the GA. Candidates for election can either be suggested or self-nominated. | ||
|
|
||
| The CC is governed by and responsible for enforcing the Code of Conduct. | ||
|
|
||
| The CC can be contacted at <cc@@ffmpeg>. | ||
|
|
||
| @anchor{Code of Conduct} | ||
| @chapter Code of Conduct | ||
|
|
||
| Be friendly and respectful towards others and third parties. | ||
| Treat others the way you yourself want to be treated. | ||
|
|
||
| Be considerate. Not everyone shares the same viewpoint and priorities as you do. | ||
| Different opinions and interpretations help the project. | ||
| Looking at issues from a different perspective assists development. | ||
|
|
||
| Do not assume malice for things that can be attributed to incompetence. Even if | ||
| it is malice, it's rarely good to start with that as initial assumption. | ||
|
|
||
| Stay friendly even if someone acts contrarily. Everyone has a bad day | ||
| once in a while. | ||
| If you yourself have a bad day or are angry then try to take a break and reply | ||
| once you are calm and without anger if you have to. | ||
|
|
||
| Try to help other team members and cooperate if you can. | ||
|
|
||
| The goal of software development is to create technical excellence, not for any | ||
| individual to be better and "win" against the others. Large software projects | ||
| are only possible and successful through teamwork. | ||
|
|
||
| If someone struggles do not put them down. Give them a helping hand | ||
| instead and point them in the right direction. | ||
|
|
||
| Finally, keep in mind the immortal words of Bill and Ted, | ||
| "Be excellent to each other." | ||
|
|
||
| @bye |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| /* | ||
| * Copyright (c) 2015 Stephan Holljes | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file libavformat multi-client network API usage example | ||
| * @example avio_http_serve_files.c | ||
| * | ||
| * Serve a file without decoding or demuxing it over the HTTP protocol. Multiple | ||
| * clients can connect and will receive the same file. | ||
| */ | ||
|
|
||
| #include <libavformat/avformat.h> | ||
| #include <libavutil/opt.h> | ||
| #include <unistd.h> | ||
|
|
||
| static void process_client(AVIOContext *client, const char *in_uri) | ||
| { | ||
| AVIOContext *input = NULL; | ||
| uint8_t buf[1024]; | ||
| int ret, n, reply_code; | ||
| uint8_t *resource = NULL; | ||
| while ((ret = avio_handshake(client)) > 0) { | ||
| av_opt_get(client, "resource", AV_OPT_SEARCH_CHILDREN, &resource); | ||
| // check for strlen(resource) is necessary, because av_opt_get() | ||
| // may return empty string. | ||
| if (resource && strlen(resource)) | ||
| break; | ||
| av_freep(&resource); | ||
| } | ||
| if (ret < 0) | ||
| goto end; | ||
| av_log(client, AV_LOG_TRACE, "resource=%p\n", resource); | ||
| if (resource && resource[0] == '/' && !strcmp((resource + 1), in_uri)) { | ||
| reply_code = 200; | ||
| } else { | ||
| reply_code = AVERROR_HTTP_NOT_FOUND; | ||
| } | ||
| if ((ret = av_opt_set_int(client, "reply_code", reply_code, AV_OPT_SEARCH_CHILDREN)) < 0) { | ||
| av_log(client, AV_LOG_ERROR, "Failed to set reply_code: %s.\n", av_err2str(ret)); | ||
| goto end; | ||
| } | ||
| av_log(client, AV_LOG_TRACE, "Set reply code to %d\n", reply_code); | ||
|
|
||
| while ((ret = avio_handshake(client)) > 0); | ||
|
|
||
| if (ret < 0) | ||
| goto end; | ||
|
|
||
| fprintf(stderr, "Handshake performed.\n"); | ||
| if (reply_code != 200) | ||
| goto end; | ||
| fprintf(stderr, "Opening input file.\n"); | ||
| if ((ret = avio_open2(&input, in_uri, AVIO_FLAG_READ, NULL, NULL)) < 0) { | ||
| av_log(input, AV_LOG_ERROR, "Failed to open input: %s: %s.\n", in_uri, | ||
| av_err2str(ret)); | ||
| goto end; | ||
| } | ||
| for(;;) { | ||
| n = avio_read(input, buf, sizeof(buf)); | ||
| if (n < 0) { | ||
| if (n == AVERROR_EOF) | ||
| break; | ||
| av_log(input, AV_LOG_ERROR, "Error reading from input: %s.\n", | ||
| av_err2str(n)); | ||
| break; | ||
| } | ||
| avio_write(client, buf, n); | ||
| avio_flush(client); | ||
| } | ||
| end: | ||
| fprintf(stderr, "Flushing client\n"); | ||
| avio_flush(client); | ||
| fprintf(stderr, "Closing client\n"); | ||
| avio_close(client); | ||
| fprintf(stderr, "Closing input\n"); | ||
| avio_close(input); | ||
| av_freep(&resource); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| AVDictionary *options = NULL; | ||
| AVIOContext *client = NULL, *server = NULL; | ||
| const char *in_uri, *out_uri; | ||
| int ret, pid; | ||
| av_log_set_level(AV_LOG_TRACE); | ||
| if (argc < 3) { | ||
| printf("usage: %s input http://hostname[:port]\n" | ||
| "API example program to serve http to multiple clients.\n" | ||
| "\n", argv[0]); | ||
| return 1; | ||
| } | ||
|
|
||
| in_uri = argv[1]; | ||
| out_uri = argv[2]; | ||
|
|
||
| avformat_network_init(); | ||
|
|
||
| if ((ret = av_dict_set(&options, "listen", "2", 0)) < 0) { | ||
| fprintf(stderr, "Failed to set listen mode for server: %s\n", av_err2str(ret)); | ||
| return ret; | ||
| } | ||
| if ((ret = avio_open2(&server, out_uri, AVIO_FLAG_WRITE, NULL, &options)) < 0) { | ||
| fprintf(stderr, "Failed to open server: %s\n", av_err2str(ret)); | ||
| return ret; | ||
| } | ||
| fprintf(stderr, "Entering main loop.\n"); | ||
| for(;;) { | ||
| if ((ret = avio_accept(server, &client)) < 0) | ||
| goto end; | ||
| fprintf(stderr, "Accepted client, forking process.\n"); | ||
| // XXX: Since we don't reap our children and don't ignore signals | ||
| // this produces zombie processes. | ||
| pid = fork(); | ||
| if (pid < 0) { | ||
| perror("Fork failed"); | ||
| ret = AVERROR(errno); | ||
| goto end; | ||
| } | ||
| if (pid == 0) { | ||
| fprintf(stderr, "In child.\n"); | ||
| process_client(client, in_uri); | ||
| avio_close(server); | ||
| exit(0); | ||
| } | ||
| if (pid > 0) | ||
| avio_close(client); | ||
| } | ||
| end: | ||
| avio_close(server); | ||
| if (ret < 0 && ret != AVERROR_EOF) { | ||
| fprintf(stderr, "Some errors occurred: %s\n", av_err2str(ret)); | ||
| return 1; | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| /* | ||
| * Copyright (c) 2014 Lukasz Marek | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file libavformat AVIOContext list directory API usage example | ||
| * @example avio_list_dir.c | ||
| * | ||
| * Show how to list directories through the libavformat AVIOContext API. | ||
| */ | ||
|
|
||
| #include <libavcodec/avcodec.h> | ||
| #include <libavformat/avformat.h> | ||
| #include <libavformat/avio.h> | ||
|
|
||
| static const char *type_string(int type) | ||
| { | ||
| switch (type) { | ||
| case AVIO_ENTRY_DIRECTORY: | ||
| return "<DIR>"; | ||
| case AVIO_ENTRY_FILE: | ||
| return "<FILE>"; | ||
| case AVIO_ENTRY_BLOCK_DEVICE: | ||
| return "<BLOCK DEVICE>"; | ||
| case AVIO_ENTRY_CHARACTER_DEVICE: | ||
| return "<CHARACTER DEVICE>"; | ||
| case AVIO_ENTRY_NAMED_PIPE: | ||
| return "<PIPE>"; | ||
| case AVIO_ENTRY_SYMBOLIC_LINK: | ||
| return "<LINK>"; | ||
| case AVIO_ENTRY_SOCKET: | ||
| return "<SOCKET>"; | ||
| case AVIO_ENTRY_SERVER: | ||
| return "<SERVER>"; | ||
| case AVIO_ENTRY_SHARE: | ||
| return "<SHARE>"; | ||
| case AVIO_ENTRY_WORKGROUP: | ||
| return "<WORKGROUP>"; | ||
| case AVIO_ENTRY_UNKNOWN: | ||
| default: | ||
| break; | ||
| } | ||
| return "<UNKNOWN>"; | ||
| } | ||
|
|
||
| static int list_op(const char *input_dir) | ||
| { | ||
| AVIODirEntry *entry = NULL; | ||
| AVIODirContext *ctx = NULL; | ||
| int cnt, ret; | ||
| char filemode[4], uid_and_gid[20]; | ||
|
|
||
| if ((ret = avio_open_dir(&ctx, input_dir, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot open directory: %s.\n", av_err2str(ret)); | ||
| goto fail; | ||
| } | ||
|
|
||
| cnt = 0; | ||
| for (;;) { | ||
| if ((ret = avio_read_dir(ctx, &entry)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot list directory: %s.\n", av_err2str(ret)); | ||
| goto fail; | ||
| } | ||
| if (!entry) | ||
| break; | ||
| if (entry->filemode == -1) { | ||
| snprintf(filemode, 4, "???"); | ||
| } else { | ||
| snprintf(filemode, 4, "%3"PRIo64, entry->filemode); | ||
| } | ||
| snprintf(uid_and_gid, 20, "%"PRId64"(%"PRId64")", entry->user_id, entry->group_id); | ||
| if (cnt == 0) | ||
| av_log(NULL, AV_LOG_INFO, "%-9s %12s %30s %10s %s %16s %16s %16s\n", | ||
| "TYPE", "SIZE", "NAME", "UID(GID)", "UGO", "MODIFIED", | ||
| "ACCESSED", "STATUS_CHANGED"); | ||
| av_log(NULL, AV_LOG_INFO, "%-9s %12"PRId64" %30s %10s %s %16"PRId64" %16"PRId64" %16"PRId64"\n", | ||
| type_string(entry->type), | ||
| entry->size, | ||
| entry->name, | ||
| uid_and_gid, | ||
| filemode, | ||
| entry->modification_timestamp, | ||
| entry->access_timestamp, | ||
| entry->status_change_timestamp); | ||
| avio_free_directory_entry(&entry); | ||
| cnt++; | ||
| }; | ||
|
|
||
| fail: | ||
| avio_close_dir(&ctx); | ||
| return ret; | ||
| } | ||
|
|
||
| static void usage(const char *program_name) | ||
| { | ||
| fprintf(stderr, "usage: %s input_dir\n" | ||
| "API example program to show how to list files in directory " | ||
| "accessed through AVIOContext.\n", program_name); | ||
| } | ||
|
|
||
| int main(int argc, char *argv[]) | ||
| { | ||
| int ret; | ||
|
|
||
| av_log_set_level(AV_LOG_DEBUG); | ||
|
|
||
| if (argc < 2) { | ||
| usage(argv[0]); | ||
| return 1; | ||
| } | ||
|
|
||
| avformat_network_init(); | ||
|
|
||
| ret = list_op(argv[1]); | ||
|
|
||
| avformat_network_deinit(); | ||
|
|
||
| return ret < 0 ? 1 : 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /* | ||
| * Copyright (c) 2014 Stefano Sabatini | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file libavformat AVIOContext read callback API usage example | ||
| * @example avio_read_callback.c | ||
| * | ||
| * Make libavformat demuxer access media content through a custom | ||
| * AVIOContext read callback. | ||
| */ | ||
|
|
||
| #include <libavcodec/avcodec.h> | ||
| #include <libavformat/avformat.h> | ||
| #include <libavformat/avio.h> | ||
| #include <libavutil/file.h> | ||
| #include <libavutil/mem.h> | ||
|
|
||
| struct buffer_data { | ||
| uint8_t *ptr; | ||
| size_t size; ///< size left in the buffer | ||
| }; | ||
|
|
||
| static int read_packet(void *opaque, uint8_t *buf, int buf_size) | ||
| { | ||
| struct buffer_data *bd = (struct buffer_data *)opaque; | ||
| buf_size = FFMIN(buf_size, bd->size); | ||
|
|
||
| if (!buf_size) | ||
| return AVERROR_EOF; | ||
| printf("ptr:%p size:%zu\n", bd->ptr, bd->size); | ||
|
|
||
| /* copy internal buffer data to buf */ | ||
| memcpy(buf, bd->ptr, buf_size); | ||
| bd->ptr += buf_size; | ||
| bd->size -= buf_size; | ||
|
|
||
| return buf_size; | ||
| } | ||
|
|
||
| int main(int argc, char *argv[]) | ||
| { | ||
| AVFormatContext *fmt_ctx = NULL; | ||
| AVIOContext *avio_ctx = NULL; | ||
| uint8_t *buffer = NULL, *avio_ctx_buffer = NULL; | ||
| size_t buffer_size, avio_ctx_buffer_size = 4096; | ||
| char *input_filename = NULL; | ||
| int ret = 0; | ||
| struct buffer_data bd = { 0 }; | ||
|
|
||
| if (argc != 2) { | ||
| fprintf(stderr, "usage: %s input_file\n" | ||
| "API example program to show how to read from a custom buffer " | ||
| "accessed through AVIOContext.\n", argv[0]); | ||
| return 1; | ||
| } | ||
| input_filename = argv[1]; | ||
|
|
||
| /* slurp file content into buffer */ | ||
| ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL); | ||
| if (ret < 0) | ||
| goto end; | ||
|
|
||
| /* fill opaque structure used by the AVIOContext read callback */ | ||
| bd.ptr = buffer; | ||
| bd.size = buffer_size; | ||
|
|
||
| if (!(fmt_ctx = avformat_alloc_context())) { | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| avio_ctx_buffer = av_malloc(avio_ctx_buffer_size); | ||
| if (!avio_ctx_buffer) { | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
| avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, | ||
| 0, &bd, &read_packet, NULL, NULL); | ||
| if (!avio_ctx) { | ||
| av_freep(&avio_ctx_buffer); | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
| fmt_ctx->pb = avio_ctx; | ||
|
|
||
| ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL); | ||
| if (ret < 0) { | ||
| fprintf(stderr, "Could not open input\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = avformat_find_stream_info(fmt_ctx, NULL); | ||
| if (ret < 0) { | ||
| fprintf(stderr, "Could not find stream information\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| av_dump_format(fmt_ctx, 0, input_filename, 0); | ||
|
|
||
| end: | ||
| avformat_close_input(&fmt_ctx); | ||
|
|
||
| /* note: the internal buffer could have changed, and be != avio_ctx_buffer */ | ||
| if (avio_ctx) | ||
| av_freep(&avio_ctx->buffer); | ||
| avio_context_free(&avio_ctx); | ||
|
|
||
| av_file_unmap(buffer, buffer_size); | ||
|
|
||
| if (ret < 0) { | ||
| fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); | ||
| return 1; | ||
| } | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,319 @@ | ||
| /* | ||
| * Copyright (c) 2010 Nicolas George | ||
| * Copyright (c) 2011 Stefano Sabatini | ||
| * Copyright (c) 2012 Clément Bœsch | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file audio decoding and filtering usage example | ||
| * @example decode_filter_audio.c | ||
| * | ||
| * Demux, decode and filter audio input file, generate a raw audio | ||
| * file to be played with ffplay. | ||
| */ | ||
|
|
||
| #include <libavcodec/avcodec.h> | ||
| #include <libavformat/avformat.h> | ||
| #include <libavfilter/buffersink.h> | ||
| #include <libavfilter/buffersrc.h> | ||
| #include <libavutil/channel_layout.h> | ||
| #include <libavutil/mem.h> | ||
| #include <libavutil/opt.h> | ||
|
|
||
| static const char *filter_descr = "aresample=8000,aformat=sample_fmts=s16:channel_layouts=mono"; | ||
| static const char *player = "ffplay -f s16le -ar 8000 -ac 1 -"; | ||
|
|
||
| static AVFormatContext *fmt_ctx; | ||
| static AVCodecContext *dec_ctx; | ||
| AVFilterContext *buffersink_ctx; | ||
| AVFilterContext *buffersrc_ctx; | ||
| AVFilterGraph *filter_graph; | ||
| static int audio_stream_index = -1; | ||
|
|
||
| static int open_input_file(const char *filename) | ||
| { | ||
| const AVCodec *dec; | ||
| int ret; | ||
|
|
||
| if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| /* select the audio stream */ | ||
| ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot find an audio stream in the input file\n"); | ||
| return ret; | ||
| } | ||
| audio_stream_index = ret; | ||
|
|
||
| /* create decoding context */ | ||
| dec_ctx = avcodec_alloc_context3(dec); | ||
| if (!dec_ctx) | ||
| return AVERROR(ENOMEM); | ||
| avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[audio_stream_index]->codecpar); | ||
|
|
||
| /* init the audio decoder */ | ||
| if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot open audio decoder\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int init_filters(const char *filters_descr) | ||
| { | ||
| char args[512]; | ||
| int ret = 0; | ||
| const AVFilter *abuffersrc = avfilter_get_by_name("abuffer"); | ||
| const AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); | ||
| AVFilterInOut *outputs = avfilter_inout_alloc(); | ||
| AVFilterInOut *inputs = avfilter_inout_alloc(); | ||
| static const int out_sample_rate = 8000; | ||
| const AVFilterLink *outlink; | ||
| AVRational time_base = fmt_ctx->streams[audio_stream_index]->time_base; | ||
|
|
||
| filter_graph = avfilter_graph_alloc(); | ||
| if (!outputs || !inputs || !filter_graph) { | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| /* buffer audio source: the decoded frames from the decoder will be inserted here. */ | ||
| if (dec_ctx->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) | ||
| av_channel_layout_default(&dec_ctx->ch_layout, dec_ctx->ch_layout.nb_channels); | ||
| ret = snprintf(args, sizeof(args), | ||
| "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=", | ||
| time_base.num, time_base.den, dec_ctx->sample_rate, | ||
| av_get_sample_fmt_name(dec_ctx->sample_fmt)); | ||
| av_channel_layout_describe(&dec_ctx->ch_layout, args + ret, sizeof(args) - ret); | ||
| ret = avfilter_graph_create_filter(&buffersrc_ctx, abuffersrc, "in", | ||
| args, NULL, filter_graph); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* buffer audio sink: to terminate the filter chain. */ | ||
| buffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "out"); | ||
| if (!buffersink_ctx) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n"); | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = av_opt_set(buffersink_ctx, "sample_formats", "s16", | ||
| AV_OPT_SEARCH_CHILDREN); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = av_opt_set(buffersink_ctx, "channel_layouts", "mono", | ||
| AV_OPT_SEARCH_CHILDREN); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = av_opt_set_array(buffersink_ctx, "samplerates", AV_OPT_SEARCH_CHILDREN, | ||
| 0, 1, AV_OPT_TYPE_INT, &out_sample_rate); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = avfilter_init_dict(buffersink_ctx, NULL); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot initialize audio buffer sink\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* | ||
| * Set the endpoints for the filter graph. The filter_graph will | ||
| * be linked to the graph described by filters_descr. | ||
| */ | ||
|
|
||
| /* | ||
| * The buffer source output must be connected to the input pad of | ||
| * the first filter described by filters_descr; since the first | ||
| * filter input label is not specified, it is set to "in" by | ||
| * default. | ||
| */ | ||
| outputs->name = av_strdup("in"); | ||
| outputs->filter_ctx = buffersrc_ctx; | ||
| outputs->pad_idx = 0; | ||
| outputs->next = NULL; | ||
|
|
||
| /* | ||
| * The buffer sink input must be connected to the output pad of | ||
| * the last filter described by filters_descr; since the last | ||
| * filter output label is not specified, it is set to "out" by | ||
| * default. | ||
| */ | ||
| inputs->name = av_strdup("out"); | ||
| inputs->filter_ctx = buffersink_ctx; | ||
| inputs->pad_idx = 0; | ||
| inputs->next = NULL; | ||
|
|
||
| if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr, | ||
| &inputs, &outputs, NULL)) < 0) | ||
| goto end; | ||
|
|
||
| if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | ||
| goto end; | ||
|
|
||
| /* Print summary of the sink buffer | ||
| * Note: args buffer is reused to store channel layout string */ | ||
| outlink = buffersink_ctx->inputs[0]; | ||
| av_channel_layout_describe(&outlink->ch_layout, args, sizeof(args)); | ||
| av_log(NULL, AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n", | ||
| (int)outlink->sample_rate, | ||
| (char *)av_x_if_null(av_get_sample_fmt_name(outlink->format), "?"), | ||
| args); | ||
|
|
||
| end: | ||
| avfilter_inout_free(&inputs); | ||
| avfilter_inout_free(&outputs); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static void print_frame(const AVFrame *frame) | ||
| { | ||
| const int n = frame->nb_samples * frame->ch_layout.nb_channels; | ||
| const uint16_t *p = (uint16_t*)frame->data[0]; | ||
| const uint16_t *p_end = p + n; | ||
|
|
||
| while (p < p_end) { | ||
| fputc(*p & 0xff, stdout); | ||
| fputc(*p>>8 & 0xff, stdout); | ||
| p++; | ||
| } | ||
| fflush(stdout); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int ret; | ||
| AVPacket *packet = av_packet_alloc(); | ||
| AVFrame *frame = av_frame_alloc(); | ||
| AVFrame *filt_frame = av_frame_alloc(); | ||
|
|
||
| if (!packet || !frame || !filt_frame) { | ||
| fprintf(stderr, "Could not allocate frame or packet\n"); | ||
| exit(1); | ||
| } | ||
| if (argc != 2) { | ||
| fprintf(stderr, "Usage: %s file | %s\n", argv[0], player); | ||
| exit(1); | ||
| } | ||
|
|
||
| if ((ret = open_input_file(argv[1])) < 0) | ||
| goto end; | ||
| if ((ret = init_filters(filter_descr)) < 0) | ||
| goto end; | ||
|
|
||
| /* read all packets */ | ||
| while (1) { | ||
| if ((ret = av_read_frame(fmt_ctx, packet)) < 0) | ||
| break; | ||
|
|
||
| if (packet->stream_index == audio_stream_index) { | ||
| ret = avcodec_send_packet(dec_ctx, packet); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n"); | ||
| break; | ||
| } | ||
|
|
||
| while (ret >= 0) { | ||
| ret = avcodec_receive_frame(dec_ctx, frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | ||
| break; | ||
| } else if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| if (ret >= 0) { | ||
| /* push the audio data from decoded frame into the filtergraph */ | ||
| if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n"); | ||
| break; | ||
| } | ||
|
|
||
| /* pull filtered audio from the filtergraph */ | ||
| while (1) { | ||
| ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| break; | ||
| if (ret < 0) | ||
| goto end; | ||
| print_frame(filt_frame); | ||
| av_frame_unref(filt_frame); | ||
| } | ||
| av_frame_unref(frame); | ||
| } | ||
| } | ||
| } | ||
| av_packet_unref(packet); | ||
| } | ||
| if (ret == AVERROR_EOF) { | ||
| /* signal EOF to the filtergraph */ | ||
| if (av_buffersrc_add_frame_flags(buffersrc_ctx, NULL, 0) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while closing the filtergraph\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* pull remaining frames from the filtergraph */ | ||
| while (1) { | ||
| ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| break; | ||
| if (ret < 0) | ||
| goto end; | ||
| print_frame(filt_frame); | ||
| av_frame_unref(filt_frame); | ||
| } | ||
| } | ||
|
|
||
| end: | ||
| avfilter_graph_free(&filter_graph); | ||
| avcodec_free_context(&dec_ctx); | ||
| avformat_close_input(&fmt_ctx); | ||
| av_packet_free(&packet); | ||
| av_frame_free(&frame); | ||
| av_frame_free(&filt_frame); | ||
|
|
||
| if (ret < 0 && ret != AVERROR_EOF) { | ||
| fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); | ||
| exit(1); | ||
| } | ||
|
|
||
| exit(0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,317 @@ | ||
| /* | ||
| * Copyright (c) 2010 Nicolas George | ||
| * Copyright (c) 2011 Stefano Sabatini | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file | ||
| * API example for decoding and filtering | ||
| * @example decode_filter_video.c | ||
| */ | ||
|
|
||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
|
|
||
| #include <libavcodec/avcodec.h> | ||
| #include <libavformat/avformat.h> | ||
| #include <libavfilter/buffersink.h> | ||
| #include <libavfilter/buffersrc.h> | ||
| #include <libavutil/mem.h> | ||
| #include <libavutil/opt.h> | ||
| #include <libavutil/time.h> | ||
|
|
||
| const char *filter_descr = "scale=78:24,transpose=cclock"; | ||
| /* other way: | ||
| scale=78:24 [scl]; [scl] transpose=cclock // assumes "[in]" and "[out]" to be input output pads respectively | ||
| */ | ||
|
|
||
| static AVFormatContext *fmt_ctx; | ||
| static AVCodecContext *dec_ctx; | ||
| AVFilterContext *buffersink_ctx; | ||
| AVFilterContext *buffersrc_ctx; | ||
| AVFilterGraph *filter_graph; | ||
| static int video_stream_index = -1; | ||
| static int64_t last_pts = AV_NOPTS_VALUE; | ||
|
|
||
| static int open_input_file(const char *filename) | ||
| { | ||
| const AVCodec *dec; | ||
| int ret; | ||
|
|
||
| if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| /* select the video stream */ | ||
| ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n"); | ||
| return ret; | ||
| } | ||
| video_stream_index = ret; | ||
|
|
||
| /* create decoding context */ | ||
| dec_ctx = avcodec_alloc_context3(dec); | ||
| if (!dec_ctx) | ||
| return AVERROR(ENOMEM); | ||
| avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar); | ||
|
|
||
| /* init the video decoder */ | ||
| if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n"); | ||
| return ret; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int init_filters(const char *filters_descr) | ||
| { | ||
| char args[512]; | ||
| int ret = 0; | ||
| const AVFilter *buffersrc = avfilter_get_by_name("buffer"); | ||
| const AVFilter *buffersink = avfilter_get_by_name("buffersink"); | ||
| AVFilterInOut *outputs = avfilter_inout_alloc(); | ||
| AVFilterInOut *inputs = avfilter_inout_alloc(); | ||
| AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base; | ||
|
|
||
| filter_graph = avfilter_graph_alloc(); | ||
| if (!outputs || !inputs || !filter_graph) { | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| /* buffer video source: the decoded frames from the decoder will be inserted here. */ | ||
| snprintf(args, sizeof(args), | ||
| "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", | ||
| dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, | ||
| time_base.num, time_base.den, | ||
| dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); | ||
|
|
||
| ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | ||
| args, NULL, filter_graph); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* buffer video sink: to terminate the filter chain. */ | ||
| buffersink_ctx = avfilter_graph_alloc_filter(filter_graph, buffersink, "out"); | ||
| if (!buffersink_ctx) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = av_opt_set(buffersink_ctx, "pixel_formats", "gray8", | ||
| AV_OPT_SEARCH_CHILDREN); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| ret = avfilter_init_dict(buffersink_ctx, NULL); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Cannot initialize buffer sink\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* | ||
| * Set the endpoints for the filter graph. The filter_graph will | ||
| * be linked to the graph described by filters_descr. | ||
| */ | ||
|
|
||
| /* | ||
| * The buffer source output must be connected to the input pad of | ||
| * the first filter described by filters_descr; since the first | ||
| * filter input label is not specified, it is set to "in" by | ||
| * default. | ||
| */ | ||
| outputs->name = av_strdup("in"); | ||
| outputs->filter_ctx = buffersrc_ctx; | ||
| outputs->pad_idx = 0; | ||
| outputs->next = NULL; | ||
|
|
||
| /* | ||
| * The buffer sink input must be connected to the output pad of | ||
| * the last filter described by filters_descr; since the last | ||
| * filter output label is not specified, it is set to "out" by | ||
| * default. | ||
| */ | ||
| inputs->name = av_strdup("out"); | ||
| inputs->filter_ctx = buffersink_ctx; | ||
| inputs->pad_idx = 0; | ||
| inputs->next = NULL; | ||
|
|
||
| if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr, | ||
| &inputs, &outputs, NULL)) < 0) | ||
| goto end; | ||
|
|
||
| if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | ||
| goto end; | ||
|
|
||
| end: | ||
| avfilter_inout_free(&inputs); | ||
| avfilter_inout_free(&outputs); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static void display_frame(const AVFrame *frame, AVRational time_base) | ||
| { | ||
| int x, y; | ||
| uint8_t *p0, *p; | ||
| int64_t delay; | ||
|
|
||
| if (frame->pts != AV_NOPTS_VALUE) { | ||
| if (last_pts != AV_NOPTS_VALUE) { | ||
| /* sleep roughly the right amount of time; | ||
| * usleep is in microseconds, just like AV_TIME_BASE. */ | ||
| delay = av_rescale_q(frame->pts - last_pts, | ||
| time_base, AV_TIME_BASE_Q); | ||
| if (delay > 0 && delay < 1000000) | ||
| av_usleep(delay); | ||
| } | ||
| last_pts = frame->pts; | ||
| } | ||
|
|
||
| /* Trivial ASCII grayscale display. */ | ||
| p0 = frame->data[0]; | ||
| puts("\033c"); | ||
| for (y = 0; y < frame->height; y++) { | ||
| p = p0; | ||
| for (x = 0; x < frame->width; x++) | ||
| putchar(" .-+#"[*(p++) / 52]); | ||
| putchar('\n'); | ||
| p0 += frame->linesize[0]; | ||
| } | ||
| fflush(stdout); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| int ret; | ||
| AVPacket *packet; | ||
| AVFrame *frame; | ||
| AVFrame *filt_frame; | ||
|
|
||
| if (argc != 2) { | ||
| fprintf(stderr, "Usage: %s file\n", argv[0]); | ||
| exit(1); | ||
| } | ||
|
|
||
| frame = av_frame_alloc(); | ||
| filt_frame = av_frame_alloc(); | ||
| packet = av_packet_alloc(); | ||
| if (!frame || !filt_frame || !packet) { | ||
| fprintf(stderr, "Could not allocate frame or packet\n"); | ||
| exit(1); | ||
| } | ||
|
|
||
| if ((ret = open_input_file(argv[1])) < 0) | ||
| goto end; | ||
| if ((ret = init_filters(filter_descr)) < 0) | ||
| goto end; | ||
|
|
||
| /* read all packets */ | ||
| while (1) { | ||
| if ((ret = av_read_frame(fmt_ctx, packet)) < 0) | ||
| break; | ||
|
|
||
| if (packet->stream_index == video_stream_index) { | ||
| ret = avcodec_send_packet(dec_ctx, packet); | ||
| if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n"); | ||
| break; | ||
| } | ||
|
|
||
| while (ret >= 0) { | ||
| ret = avcodec_receive_frame(dec_ctx, frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | ||
| break; | ||
| } else if (ret < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| frame->pts = frame->best_effort_timestamp; | ||
|
|
||
| /* push the decoded frame into the filtergraph */ | ||
| if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); | ||
| break; | ||
| } | ||
|
|
||
| /* pull filtered frames from the filtergraph */ | ||
| while (1) { | ||
| ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| break; | ||
| if (ret < 0) | ||
| goto end; | ||
| display_frame(filt_frame, buffersink_ctx->inputs[0]->time_base); | ||
| av_frame_unref(filt_frame); | ||
| } | ||
| av_frame_unref(frame); | ||
| } | ||
| } | ||
| av_packet_unref(packet); | ||
| } | ||
| if (ret == AVERROR_EOF) { | ||
| /* signal EOF to the filtergraph */ | ||
| if (av_buffersrc_add_frame_flags(buffersrc_ctx, NULL, 0) < 0) { | ||
| av_log(NULL, AV_LOG_ERROR, "Error while closing the filtergraph\n"); | ||
| goto end; | ||
| } | ||
|
|
||
| /* pull remaining frames from the filtergraph */ | ||
| while (1) { | ||
| ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); | ||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | ||
| break; | ||
| if (ret < 0) | ||
| goto end; | ||
| display_frame(filt_frame, buffersink_ctx->inputs[0]->time_base); | ||
| av_frame_unref(filt_frame); | ||
| } | ||
| } | ||
|
|
||
| end: | ||
| avfilter_graph_free(&filter_graph); | ||
| avcodec_free_context(&dec_ctx); | ||
| avformat_close_input(&fmt_ctx); | ||
| av_frame_free(&frame); | ||
| av_frame_free(&filt_frame); | ||
| av_packet_free(&packet); | ||
|
|
||
| if (ret < 0 && ret != AVERROR_EOF) { | ||
| fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); | ||
| exit(1); | ||
| } | ||
|
|
||
| exit(0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,380 @@ | ||
| /* | ||
| * Copyright (c) 2012 Stefano Sabatini | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
|
|
||
| /** | ||
| * @file libavformat and libavcodec demuxing and decoding API usage example | ||
| * @example demux_decode.c | ||
| * | ||
| * Show how to use the libavformat and libavcodec API to demux and decode audio | ||
| * and video data. Write the output as raw audio and input files to be played by | ||
| * ffplay. | ||
| */ | ||
|
|
||
| #include <libavutil/imgutils.h> | ||
| #include <libavutil/samplefmt.h> | ||
| #include <libavutil/timestamp.h> | ||
| #include <libavcodec/avcodec.h> | ||
| #include <libavformat/avformat.h> | ||
|
|
||
| static AVFormatContext *fmt_ctx = NULL; | ||
| static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx; | ||
| static int width, height; | ||
| static enum AVPixelFormat pix_fmt; | ||
| static AVStream *video_stream = NULL, *audio_stream = NULL; | ||
| static const char *src_filename = NULL; | ||
| static const char *video_dst_filename = NULL; | ||
| static const char *audio_dst_filename = NULL; | ||
| static FILE *video_dst_file = NULL; | ||
| static FILE *audio_dst_file = NULL; | ||
|
|
||
| static uint8_t *video_dst_data[4] = {NULL}; | ||
| static int video_dst_linesize[4]; | ||
| static int video_dst_bufsize; | ||
|
|
||
| static int video_stream_idx = -1, audio_stream_idx = -1; | ||
| static AVFrame *frame = NULL; | ||
| static AVPacket *pkt = NULL; | ||
| static int video_frame_count = 0; | ||
| static int audio_frame_count = 0; | ||
|
|
||
| static int output_video_frame(AVFrame *frame) | ||
| { | ||
| if (frame->width != width || frame->height != height || | ||
| frame->format != pix_fmt) { | ||
| /* To handle this change, one could call av_image_alloc again and | ||
| * decode the following frames into another rawvideo file. */ | ||
| fprintf(stderr, "Error: Width, height and pixel format have to be " | ||
| "constant in a rawvideo file, but the width, height or " | ||
| "pixel format of the input video changed:\n" | ||
| "old: width = %d, height = %d, format = %s\n" | ||
| "new: width = %d, height = %d, format = %s\n", | ||
| width, height, av_get_pix_fmt_name(pix_fmt), | ||
| frame->width, frame->height, | ||
| av_get_pix_fmt_name(frame->format)); | ||
| return -1; | ||
| } | ||
|
|
||
| printf("video_frame n:%d\n", | ||
| video_frame_count++); | ||
|
|
||
| /* copy decoded frame to destination buffer: | ||
| * this is required since rawvideo expects non aligned data */ | ||
| av_image_copy2(video_dst_data, video_dst_linesize, | ||
| frame->data, frame->linesize, | ||
| pix_fmt, width, height); | ||
|
|
||
| /* write to rawvideo file */ | ||
| fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file); | ||
| return 0; | ||
| } | ||
|
|
||
| static int output_audio_frame(AVFrame *frame) | ||
| { | ||
| size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format); | ||
| printf("audio_frame n:%d nb_samples:%d pts:%s\n", | ||
| audio_frame_count++, frame->nb_samples, | ||
| av_ts2timestr(frame->pts, &audio_dec_ctx->time_base)); | ||
|
|
||
| /* Write the raw audio data samples of the first plane. This works | ||
| * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, | ||
| * most audio decoders output planar audio, which uses a separate | ||
| * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). | ||
| * In other words, this code will write only the first audio channel | ||
| * in these cases. | ||
| * You should use libswresample or libavfilter to convert the frame | ||
| * to packed data. */ | ||
| fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int decode_packet(AVCodecContext *dec, const AVPacket *pkt) | ||
| { | ||
| int ret = 0; | ||
|
|
||
| // submit the packet to the decoder | ||
| ret = avcodec_send_packet(dec, pkt); | ||
| if (ret < 0) { | ||
| fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret)); | ||
| return ret; | ||
| } | ||
|
|
||
| // get all the available frames from the decoder | ||
| while (ret >= 0) { | ||
| ret = avcodec_receive_frame(dec, frame); | ||
| if (ret < 0) { | ||
| // those two return values are special and mean there is no output | ||
| // frame available, but there were no errors during decoding | ||
| if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) | ||
| return 0; | ||
|
|
||
| fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret)); | ||
| return ret; | ||
| } | ||
|
|
||
| // write the frame data to output file | ||
| if (dec->codec->type == AVMEDIA_TYPE_VIDEO) | ||
| ret = output_video_frame(frame); | ||
| else | ||
| ret = output_audio_frame(frame); | ||
|
|
||
| av_frame_unref(frame); | ||
| } | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static int open_codec_context(int *stream_idx, | ||
| AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) | ||
| { | ||
| int ret, stream_index; | ||
| AVStream *st; | ||
| const AVCodec *dec = NULL; | ||
|
|
||
| ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); | ||
| if (ret < 0) { | ||
| fprintf(stderr, "Could not find %s stream in input file '%s'\n", | ||
| av_get_media_type_string(type), src_filename); | ||
| return ret; | ||
| } else { | ||
| stream_index = ret; | ||
| st = fmt_ctx->streams[stream_index]; | ||
|
|
||
| /* find decoder for the stream */ | ||
| dec = avcodec_find_decoder(st->codecpar->codec_id); | ||
| if (!dec) { | ||
| fprintf(stderr, "Failed to find %s codec\n", | ||
| av_get_media_type_string(type)); | ||
| return AVERROR(EINVAL); | ||
| } | ||
|
|
||
| /* Allocate a codec context for the decoder */ | ||
| *dec_ctx = avcodec_alloc_context3(dec); | ||
| if (!*dec_ctx) { | ||
| fprintf(stderr, "Failed to allocate the %s codec context\n", | ||
| av_get_media_type_string(type)); | ||
| return AVERROR(ENOMEM); | ||
| } | ||
|
|
||
| /* Copy codec parameters from input stream to output codec context */ | ||
| if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) { | ||
| fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n", | ||
| av_get_media_type_string(type)); | ||
| return ret; | ||
| } | ||
|
|
||
| /* Init the decoders */ | ||
| if ((ret = avcodec_open2(*dec_ctx, dec, NULL)) < 0) { | ||
| fprintf(stderr, "Failed to open %s codec\n", | ||
| av_get_media_type_string(type)); | ||
| return ret; | ||
| } | ||
| *stream_idx = stream_index; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int get_format_from_sample_fmt(const char **fmt, | ||
| enum AVSampleFormat sample_fmt) | ||
| { | ||
| int i; | ||
| struct sample_fmt_entry { | ||
| enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; | ||
| } sample_fmt_entries[] = { | ||
| { AV_SAMPLE_FMT_U8, "u8", "u8" }, | ||
| { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, | ||
| { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, | ||
| { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, | ||
| { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, | ||
| }; | ||
| *fmt = NULL; | ||
|
|
||
| for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { | ||
| struct sample_fmt_entry *entry = &sample_fmt_entries[i]; | ||
| if (sample_fmt == entry->sample_fmt) { | ||
| *fmt = AV_NE(entry->fmt_be, entry->fmt_le); | ||
| return 0; | ||
| } | ||
| } | ||
|
|
||
| fprintf(stderr, | ||
| "sample format %s is not supported as output format\n", | ||
| av_get_sample_fmt_name(sample_fmt)); | ||
| return -1; | ||
| } | ||
|
|
||
| int main (int argc, char **argv) | ||
| { | ||
| int ret = 0; | ||
|
|
||
| if (argc != 4) { | ||
| fprintf(stderr, "usage: %s input_file video_output_file audio_output_file\n" | ||
| "API example program to show how to read frames from an input file.\n" | ||
| "This program reads frames from a file, decodes them, and writes decoded\n" | ||
| "video frames to a rawvideo file named video_output_file, and decoded\n" | ||
| "audio frames to a rawaudio file named audio_output_file.\n", | ||
| argv[0]); | ||
| exit(1); | ||
| } | ||
| src_filename = argv[1]; | ||
| video_dst_filename = argv[2]; | ||
| audio_dst_filename = argv[3]; | ||
|
|
||
| /* open input file, and allocate format context */ | ||
| if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) { | ||
| fprintf(stderr, "Could not open source file %s\n", src_filename); | ||
| exit(1); | ||
| } | ||
|
|
||
| /* retrieve stream information */ | ||
| if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { | ||
| fprintf(stderr, "Could not find stream information\n"); | ||
| exit(1); | ||
| } | ||
|
|
||
| if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) { | ||
| video_stream = fmt_ctx->streams[video_stream_idx]; | ||
|
|
||
| video_dst_file = fopen(video_dst_filename, "wb"); | ||
| if (!video_dst_file) { | ||
| fprintf(stderr, "Could not open destination file %s\n", video_dst_filename); | ||
| ret = 1; | ||
| goto end; | ||
| } | ||
|
|
||
| /* allocate image where the decoded image will be put */ | ||
| width = video_dec_ctx->width; | ||
| height = video_dec_ctx->height; | ||
| pix_fmt = video_dec_ctx->pix_fmt; | ||
| ret = av_image_alloc(video_dst_data, video_dst_linesize, | ||
| width, height, pix_fmt, 1); | ||
| if (ret < 0) { | ||
| fprintf(stderr, "Could not allocate raw video buffer\n"); | ||
| goto end; | ||
| } | ||
| video_dst_bufsize = ret; | ||
| } | ||
|
|
||
| if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) { | ||
| audio_stream = fmt_ctx->streams[audio_stream_idx]; | ||
| audio_dst_file = fopen(audio_dst_filename, "wb"); | ||
| if (!audio_dst_file) { | ||
| fprintf(stderr, "Could not open destination file %s\n", audio_dst_filename); | ||
| ret = 1; | ||
| goto end; | ||
| } | ||
| } | ||
|
|
||
| /* dump input information to stderr */ | ||
| av_dump_format(fmt_ctx, 0, src_filename, 0); | ||
|
|
||
| if (!audio_stream && !video_stream) { | ||
| fprintf(stderr, "Could not find audio or video stream in the input, aborting\n"); | ||
| ret = 1; | ||
| goto end; | ||
| } | ||
|
|
||
| frame = av_frame_alloc(); | ||
| if (!frame) { | ||
| fprintf(stderr, "Could not allocate frame\n"); | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| pkt = av_packet_alloc(); | ||
| if (!pkt) { | ||
| fprintf(stderr, "Could not allocate packet\n"); | ||
| ret = AVERROR(ENOMEM); | ||
| goto end; | ||
| } | ||
|
|
||
| if (video_stream) | ||
| printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename); | ||
| if (audio_stream) | ||
| printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename); | ||
|
|
||
| /* read frames from the file */ | ||
| while (av_read_frame(fmt_ctx, pkt) >= 0) { | ||
| // check if the packet belongs to a stream we are interested in, otherwise | ||
| // skip it | ||
| if (pkt->stream_index == video_stream_idx) | ||
| ret = decode_packet(video_dec_ctx, pkt); | ||
| else if (pkt->stream_index == audio_stream_idx) | ||
| ret = decode_packet(audio_dec_ctx, pkt); | ||
| av_packet_unref(pkt); | ||
| if (ret < 0) | ||
| break; | ||
| } | ||
|
|
||
| /* flush the decoders */ | ||
| if (video_dec_ctx) | ||
| decode_packet(video_dec_ctx, NULL); | ||
| if (audio_dec_ctx) | ||
| decode_packet(audio_dec_ctx, NULL); | ||
|
|
||
| printf("Demuxing succeeded.\n"); | ||
|
|
||
| if (video_stream) { | ||
| printf("Play the output video file with the command:\n" | ||
| "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", | ||
| av_get_pix_fmt_name(pix_fmt), width, height, | ||
| video_dst_filename); | ||
| } | ||
|
|
||
| if (audio_stream) { | ||
| enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt; | ||
| int n_channels = audio_dec_ctx->ch_layout.nb_channels; | ||
| const char *fmt; | ||
|
|
||
| if (av_sample_fmt_is_planar(sfmt)) { | ||
| const char *packed = av_get_sample_fmt_name(sfmt); | ||
| printf("Warning: the sample format the decoder produced is planar " | ||
| "(%s). This example will output the first channel only.\n", | ||
| packed ? packed : "?"); | ||
| sfmt = av_get_packed_sample_fmt(sfmt); | ||
| n_channels = 1; | ||
| } | ||
|
|
||
| if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) | ||
| goto end; | ||
|
|
||
| printf("Play the output audio file with the command:\n" | ||
| "ffplay -f %s -ac %d -ar %d %s\n", | ||
| fmt, n_channels, audio_dec_ctx->sample_rate, | ||
| audio_dst_filename); | ||
| } | ||
|
|
||
| end: | ||
| avcodec_free_context(&video_dec_ctx); | ||
| avcodec_free_context(&audio_dec_ctx); | ||
| avformat_close_input(&fmt_ctx); | ||
| if (video_dst_file) | ||
| fclose(video_dst_file); | ||
| if (audio_dst_file) | ||
| fclose(audio_dst_file); | ||
| av_packet_free(&pkt); | ||
| av_frame_free(&frame); | ||
| av_free(video_dst_data[0]); | ||
|
|
||
| return ret < 0; | ||
| } |