--- segment.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ segment.h | 104 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 segment.c create mode 100644 segment.h
diff --git a/segment.c b/segment.c new file mode 100644 index 0000000..0ef58a1 --- /dev/null +++ b/segment.c @@ -0,0 +1,143 @@ +#include <stdio.h> +#include "segment.h" +#include <pthread.h> + +#include <libavutil/opt.h> +#include <libavutil/log.h> + + +void save_segment(struct Segment *seg, const char *filename) +{ + FILE *out = fopen(filename, "w"); + fwrite(seg->buf, seg->size, 1, out); + fclose(out); +} + +void segment_free(struct Segment *seg) +{ + av_log(NULL, AV_LOG_DEBUG, "Freeing segment\n"); + avformat_free_context(seg->fmt_ctx); + av_free(seg->io_ctx->buffer); + av_free(seg->io_ctx); + free(seg->buf); + free(seg->ts); + free(seg); +} + +void segment_ref(struct Segment *seg) +{ + pthread_mutex_lock(&seg->nb_read_lock); + seg->nb_read++; + av_log(NULL, AV_LOG_DEBUG, " ref Readers: %d\n", seg->nb_read); + pthread_mutex_unlock(&seg->nb_read_lock); +} + +void segment_unref(struct Segment *seg) +{ + pthread_mutex_lock(&seg->nb_read_lock); + seg->nb_read--; + pthread_mutex_unlock(&seg->nb_read_lock); + av_log(NULL, AV_LOG_DEBUG, "unref Readers: %d\n", seg->nb_read); + if (seg->nb_read == 0) { + segment_free(seg); + } +} + +void segment_ts_append(struct Segment *seg, int64_t dts, int64_t pts) +{ + seg->ts = (int64_t*) realloc(seg->ts, sizeof(int64_t) * 2 * (seg->ts_len + 2)); + seg->ts[seg->ts_len] = dts; + seg->ts[seg->ts_len + 1] = pts; + seg->ts_len += 2; + return; +} + +int segment_write(void *opaque, unsigned char *buf, int buf_size) +{ + struct Segment *seg = (struct Segment*) opaque; + seg->size += buf_size; + seg->buf = (char*) realloc(seg->buf, seg->size); + memcpy(seg->buf + seg->size - buf_size, buf, buf_size); + return buf_size; +} + +int segment_read(void *opaque, unsigned char *buf, int buf_size) +{ + struct SegmentReadInfo *info = (struct SegmentReadInfo*) opaque; + buf_size = buf_size < info->left ? buf_size : info->left; + + /* copy internal buffer data to buf */ + memcpy(buf, info->buf, buf_size); + info->buf += buf_size; + info->left -= buf_size; + return buf_size ? buf_size : AVERROR_EOF; +} + + +void segment_close(struct Segment *seg) +{ + av_write_trailer(seg->fmt_ctx); +} + +void segment_init(struct Segment **seg_p, AVFormatContext *fmt) +{ + int ret; + int i; + AVStream *in_stream, *out_stream; + AVCodecContext *codec_ctx; + struct Segment *seg = (struct Segment*) malloc(sizeof(struct Segment)); + + seg->ifmt = fmt->iformat; + seg->fmt_ctx = NULL; + seg->nb_read = 0; + seg->size = 0; + seg->ts = NULL; + seg->ts_len = 0; + seg->buf = NULL; + seg->avio_buffer = (unsigned char*) av_malloc(AV_BUFSIZE); + pthread_mutex_init(&seg->nb_read_lock, NULL); + seg->io_ctx = avio_alloc_context(seg->avio_buffer, AV_BUFSIZE, 1, seg, NULL, &segment_write, NULL); + seg->io_ctx->seekable = 0; + avformat_alloc_output_context2(&seg->fmt_ctx, NULL, "matroska", NULL); + if ((ret = av_opt_set_int(seg->fmt_ctx, "flush_packets", 1, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(NULL, AV_LOG_WARNING, "Could not set flush_packets!\n"); + } + + seg->fmt_ctx->flags |= AVFMT_FLAG_GENPTS; + seg->fmt_ctx->oformat->flags &= AVFMT_NOFILE; + + av_log(NULL, AV_LOG_DEBUG, "Initializing segment\n"); + + for (i = 0; i < fmt->nb_streams; i++) { + in_stream = fmt->streams[i]; + codec_ctx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(codec_ctx, in_stream->codecpar); + out_stream = avformat_new_stream(seg->fmt_ctx, codec_ctx->codec); + avcodec_free_context(&codec_ctx); + if (!out_stream) { + av_log(NULL, AV_LOG_WARNING, "Failed allocating output stream\n"); + continue; + } + ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); + if (ret < 0) { + av_log(NULL, AV_LOG_WARNING, "Failed to copy context from input to output stream codec context\n"); + continue; + } + av_dict_copy(&out_stream->metadata, in_stream->metadata, 0); + } + + seg->fmt_ctx->pb = seg->io_ctx; + ret = avformat_write_header(seg->fmt_ctx, NULL); + avio_flush(seg->io_ctx); + if (ret < 0) { + segment_close(seg); + av_log(NULL, AV_LOG_WARNING, "Error occured while writing header: %s\n", av_err2str(ret)); + } + + + av_log(NULL, AV_LOG_DEBUG, "Initialized segment.\n"); + + *seg_p = seg; + + return; +} diff --git a/segment.h b/segment.h new file mode 100644 index 0000000..fecbc66 --- /dev/null +++ b/segment.h @@ -0,0 +1,104 @@ +#ifndef SEGMENT_H +#define SEGMENT_H + +#include <libavformat/avformat.h> + +#define AV_BUFSIZE 4096 + +struct SegmentReadInfo { + char *buf; + int left; +}; + +struct Segment { + char *buf; + AVIOContext *io_ctx; + AVFormatContext *fmt_ctx; + AVInputFormat *ifmt; + size_t size; + int64_t *ts; + size_t ts_len; + //FILE *stream; + int nb_read; + unsigned char *avio_buffer; + int id; + pthread_mutex_t nb_read_lock; +}; + +/** + * Save segment to a file using filename + * + * @param seg pointer to the Segment to save + * @param filename string to use to save the Segment + */ +void save_segment(struct Segment *seg, const char *filename); + +/** + * Free Segment. Automatically called when a Segment's refcount reaches zero. + * + * @param seg pointer to the Segment to free + */ +void segment_free(struct Segment *seg); + +/** + * Increase the reference counter for a Segment. + * + * @param seg pointer to the Segment to increase the refcounter for + */ +void segment_ref(struct Segment *seg); + +/** + * Decrease the reference counter for a Segment. Calls segment_free() if the refcounter reaches zero. + * + * @param seg pointer to the Segment to unref + */ +void segment_unref(struct Segment *seg); + +/** + * Append a dts and pts pair to a Segment. This should be called after each frame is added. + * + * @param seg pointer to the Segment to add timestamps to + * @param dts dts for the last added frame + * @param pts pts for the last added frame + */ +void segment_ts_append(struct Segment *seg, int64_t dts, int64_t pts); + +/** + * Write buf_size bytes from buf to a Segment opaque. + * + * @param opaque void pointer to the Segment to write to + * @param buf pointer to the data to write + * @param buf_size number of bytes to write + * @return number of bytes written. May be less than buf_size. + */ +int segment_write(void *opaque, unsigned char *buf, int buf_size); + +/** + * Read buf_size bytes from a Segment using a SegmentReadInfo struct and store them in buf. + * Using a SegmentReadInfo struct instead of the Segment directly is needed, because there + * are multiple readers for a single Segment and each has to keep its own reading state. + * + * @param opaque void pointer to the SegmentReadInfo struct to use for reading + * @param buf pointer to where to store the read data + * @param buf_size number of bytes to read + * @return number of bytes read. May be less than buf_size. + */ +int segment_read(void *opaque, unsigned char *buf, int buf_size); + +/** + * Write a Segment's trailer + * + * @param seg pointer to the Segment to finalize + */ +void segment_close(struct Segment *seg); + +/** + * Allocate and initialize a new segment given the AVFormatContext fmt + * + * @param seg pointer to a pointer to a Segment to be allocated and initialized + * @param fmt pointer to an AVFormatContext describing the format of the Segment + */ +void segment_init(struct Segment **seg, AVFormatContext *fmt); + + +#endif // SEGMENT_H -- 2.16.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel