Show how to use the AVIO writing API. --- .gitignore | 1 + configure | 1 + doc/examples/Makefile | 1 + doc/examples/avio_writing.c | 225 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 doc/examples/avio_writing.c
diff --git a/.gitignore b/.gitignore index 793d33a..628ffb1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ /doc/avoptions_format.texi /doc/doxy/html/ /doc/examples/avio_reading +/doc/examples/avio_writing /doc/examples/decoding_encoding /doc/examples/demuxing_decoding /doc/examples/extract_mvs diff --git a/configure b/configure index 570878d..5b3a246 100755 --- a/configure +++ b/configure @@ -1310,6 +1310,7 @@ COMPONENT_LIST=" EXAMPLE_LIST=" avio_reading_example + avio_writing_example decoding_encoding_example demuxing_decoding_example extract_mvs_example diff --git a/doc/examples/Makefile b/doc/examples/Makefile index 07251fe..ad10b5b 100644 --- a/doc/examples/Makefile +++ b/doc/examples/Makefile @@ -12,6 +12,7 @@ CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS) LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS) EXAMPLES= avio_reading \ + avio_writing \ decoding_encoding \ demuxing_decoding \ extract_mvs \ diff --git a/doc/examples/avio_writing.c b/doc/examples/avio_writing.c new file mode 100644 index 0000000..27ef21a --- /dev/null +++ b/doc/examples/avio_writing.c @@ -0,0 +1,225 @@ +/* + * 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 API example. + * + * Make libavformat demuxer access media content through a custom + * AVIOContext write callback. + * @example avio_writing.c + */ + +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +#include <libavutil/file.h> +#include <libavutil/timestamp.h> + +struct buffer_data { + uint8_t *buf; + size_t size; + uint8_t *ptr; + size_t room; ///< size left in the buffer +}; + +static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag) +{ + AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base; + + printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n", + tag, + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base), + pkt->stream_index); +} + +static int write_packet(void *opaque, uint8_t *buf, int buf_size) +{ + struct buffer_data *bd = (struct buffer_data *)opaque; + while (buf_size > bd->room) { + int64_t offset = bd->ptr - bd->buf; + bd->buf = av_realloc_f(bd->buf, 2, bd->size); + if (!bd->buf) + return AVERROR(ENOMEM); + bd->size *= 2; + bd->ptr = bd->buf + offset; + bd->room = bd->size - offset; + } + printf("write packet pkt_size:%d used_buf_size:%zu buf_size:%zu buf_room:%zu\n", buf_size, bd->ptr-bd->buf, bd->size, bd->room); + + /* copy buffer data to buffer_data buffer */ + memcpy(bd->ptr, buf, buf_size); + bd->ptr += buf_size; + bd->room -= buf_size; + + return buf_size; +} + +int main(int argc, char *argv[]) +{ + AVFormatContext *ifmt_ctx = NULL; + AVFormatContext *ofmt_ctx = NULL; + AVIOContext *avio_ctx = NULL; + uint8_t *avio_ctx_buffer = NULL; + size_t avio_ctx_buffer_size = 4096; + char *in_filename = NULL; + char *out_filename = NULL; + int i, ret = 0; + struct buffer_data bd = { 0 }; + const size_t bd_buf_size = 1024; + AVPacket pkt; + + if (argc != 3) { + fprintf(stderr, "usage: %s input_file output_file\n" + "API example program to show how to write to a custom buffer " + "accessed through AVIOContext.\n" + "Remux the content of input_file to output_file, writing to a custom buffer.\n" + "The output file must support the same codec as the input file.\n", argv[0]); + return 1; + } + in_filename = argv[1]; + out_filename = argv[2]; + + av_register_all(); + + if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) { + fprintf(stderr, "Could not open input file '%s'", in_filename); + goto end; + } + + if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { + fprintf(stderr, "Failed to retrieve input stream information"); + goto end; + } + + av_dump_format(ifmt_ctx, 0, in_filename, 0); + + /* fill opaque structure used by the AVIOContext write callback */ + bd.ptr = bd.buf = av_malloc(bd_buf_size); + if (!bd.buf) { + ret = AVERROR(ENOMEM); + goto end; + } + bd.size = bd.room = bd_buf_size; + + 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, + 1, &bd, NULL, &write_packet, NULL); + if (!avio_ctx) { + ret = AVERROR(ENOMEM); + goto end; + } + + ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", NULL); + if (ret < 0) { + fprintf(stderr, "Could not create output context\n"); + goto end; + } + ofmt_ctx->pb = avio_ctx; + + for (i = 0; i < ifmt_ctx->nb_streams; i++) { + AVStream *in_stream = ifmt_ctx->streams[i]; + AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec); + if (!out_stream) { + fprintf(stderr, "Failed creating output stream\n"); + ret = AVERROR_UNKNOWN; + goto end; + } + + ret = avcodec_copy_context(out_stream->codec, in_stream->codec); + if (ret < 0) { + fprintf(stderr, "Failed to copy context from input to output stream codec context\n"); + goto end; + } + out_stream->codec->codec_tag = 0; + if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) + out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + av_dump_format(ofmt_ctx, 0, out_filename, 1); + + ret = avformat_write_header(ofmt_ctx, NULL); + if (ret < 0) { + fprintf(stderr, "Error occurred when opening output file\n"); + goto end; + } + + while (1) { + AVStream *in_stream, *out_stream; + + ret = av_read_frame(ifmt_ctx, &pkt); + if (ret < 0) + break; + + in_stream = ifmt_ctx->streams[pkt.stream_index]; + out_stream = ofmt_ctx->streams[pkt.stream_index]; + log_packet(ifmt_ctx, &pkt, "in"); + + /* copy packet */ + av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base); + pkt.pos = -1; + log_packet(ofmt_ctx, &pkt, "out"); + + ret = av_interleaved_write_frame(ofmt_ctx, &pkt); + if (ret < 0) { + fprintf(stderr, "Error muxing packet\n"); + break; + } + av_free_packet(&pkt); + } + + av_write_trailer(ofmt_ctx); +end: + + avformat_close_input(&ifmt_ctx); + + /* close output */ + avformat_free_context(ofmt_ctx); + av_freep(&avio_ctx->buffer); + av_free(avio_ctx); + + /* write buffer to file */ + { + FILE *out_file = fopen(out_filename, "w"); + if (!out_file) { + fprintf(stderr, "Could not open file '%s'\n", out_filename); + ret = AVERROR(errno); + } else { + fwrite(bd.buf, bd.size, 1, out_file); + fclose(out_file); + } + } + + av_free(bd.buf); + + if (ret < 0 && ret != AVERROR_EOF) { + fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); + return 1; + } + + return 0; +} -- 1.8.3.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel