[FFmpeg-devel] [PATCH 3/4] ffserver: Implement ffserver and add Makefile
--- Makefile | 15 ++ ffserver.c | 477 + 2 files changed, 492 insertions(+) create mode 100644 Makefile create mode 100644 ffserver.c diff --git a/Makefile b/Makefile new file mode 100644 index 000..b077039 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +all: ffserver +LAV_FLAGS = $(shell pkg-config --libs --cflags libavformat libavcodec libavutil) +# LAV_FLAGS = -L/usr/local/lib -lavcodec -lavformat -lavutil + +ffserver: segment.o publisher.o ffserver.c + cc -g -Wall $(LAV_FLAGS) -lpthread -o ffserver segment.o publisher.o ffserver.c + +segment.o: segment.c segment.h + cc -g -Wall $(LAV_FLAGS) -lpthread -c segment.c + +publisher.o: publisher.c publisher.h + cc -g -Wall $(LAV_FLAGS) -lpthread -c publisher.c + +clean: + rm *.o ffserver diff --git a/ffserver.c b/ffserver.c new file mode 100644 index 000..b0ff00e --- /dev/null +++ b/ffserver.c @@ -0,0 +1,477 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * multimedia server based on the FFmpeg libraries + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "segment.h" +#include "publisher.h" + +#define BUFFER_SECS 30 +#define LISTEN_TIMEOUT_MSEC 1000 + +struct ReadInfo { +struct PublisherContext *pub; +AVFormatContext *ifmt_ctx; +char *in_filename; +}; + +struct WriteInfo { +struct PublisherContext *pub; +int thread_id; +}; + +struct AcceptInfo { +struct PublisherContext *pub; +AVFormatContext *ifmt_ctx; +const char *out_uri; +}; + + +void *read_thread(void *arg) +{ +struct ReadInfo *info = (struct ReadInfo*) arg; +AVFormatContext *ifmt_ctx = info->ifmt_ctx; +int ret, i; +int video_idx = -1; +int id = 0; +int64_t pts, now, start; +int64_t *ts; +struct Segment *seg = NULL; +AVPacket pkt; +AVStream *in_stream; +AVRational tb; +tb.num = 1; +tb.den = AV_TIME_BASE; +AVStream *stream; +AVCodecParameters *params; +enum AVMediaType type; + +if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { +av_log(ifmt_ctx, AV_LOG_ERROR, "Could not get input stream info.\n"); +goto end; +} + +av_log(ifmt_ctx, AV_LOG_INFO, "Finding video stream.\n"); +for (i = 0; i < ifmt_ctx->nb_streams; i++) { +av_log(ifmt_ctx, AV_LOG_DEBUG, "Checking stream %d\n", i); +stream = ifmt_ctx->streams[i]; +params = stream->codecpar; +type = params->codec_type; +if (type == AVMEDIA_TYPE_VIDEO) { +video_idx = i; +break; +} +} +if (video_idx == -1) { +av_log(ifmt_ctx, AV_LOG_ERROR, "No video stream found.\n"); +goto end; +} + + +// All information needed to start segmenting the file is gathered now. +// start BUFFER_SECS seconds "in the past" to "catch up" to real-time. Has no effect on streamed sources. +start = av_gettime_relative() - BUFFER_SECS * AV_TIME_BASE; + +// segmenting main-loop + +for (;;) { +ret = av_read_frame(ifmt_ctx, &pkt); +if (ret < 0) +break; + +in_stream = ifmt_ctx->streams[pkt.stream_index]; +if (pkt.pts == AV_NOPTS_VALUE) { +pkt.pts = 0; +} +if (pkt.dts == AV_NOPTS_VALUE) { +pkt.dts = 0; +} + +// current pts +pts = av_rescale_q(pkt.pts, in_stream->time_base, tb); + +// current stream "uptime" +now = av_gettime_relative() - start; + +// simulate real-time reading +while (pts > now) { +usleep(1000); +now = av_gettime_relative() - start; +} + +// keyframe or first Segment +if ((pkt.flags & AV_PKT_FLAG_KEY && pkt.stream_index == video_idx) || !seg) { +if (seg) { +segment_close(seg); +publisher_push_segment(info->pub, seg); +av_log(NULL, AV_LOG_DEBUG, "New segment pushed.\n"); +publish(info->pub); + av_log(NULL, AV_LOG_DEBUG, "Published new segment.\n"); +} +
[FFmpeg-devel] [PATCH 3/4] ffserver: Implement ffserver and add Makefile
--- Makefile | 15 ++ ffserver.c | 451 + 2 files changed, 466 insertions(+) create mode 100644 Makefile create mode 100644 ffserver.c diff --git a/Makefile b/Makefile new file mode 100644 index 000..a57393a --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +all: ffserver +LAV_FLAGS = $(shell pkg-config --libs --cflags libavformat libavcodec libavutil) +# LAV_FLAGS = -L/usr/local/lib -lavcodec -lavformat -lavutil + +ffserver: segment.o publisher.o ffserver.c + cc -g -Wall $(LAV_FLAGS) -lpthread -o ffserver segment.o publisher.o ffserver.c + +segment.o: segment.c segment.h + cc -g -Wall $(LAV_FLAGS) -lpthread -c segment.c + +publisher.o: publisher.c publisher.h + cc -g -Wall $(LAV_FLAGS) -lpthread -c publisher.c + +clean: + rm *.o server diff --git a/ffserver.c b/ffserver.c new file mode 100644 index 000..ecdcc64 --- /dev/null +++ b/ffserver.c @@ -0,0 +1,451 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "segment.h" +#include "publisher.h" + +#define BUFFER_SECS 30 +#define LISTEN_TIMEOUT_MSEC 1000 + +struct ReadInfo { +struct PublisherContext *pub; +AVFormatContext *ifmt_ctx; +char *in_filename; +}; + +struct WriteInfo { +struct PublisherContext *pub; +int thread_id; +}; + +struct AcceptInfo { +struct PublisherContext *pub; +AVFormatContext *ifmt_ctx; +const char *out_uri; +}; + + +void *read_thread(void *arg) +{ +struct ReadInfo *info = (struct ReadInfo*) arg; +AVFormatContext *ifmt_ctx = info->ifmt_ctx; +int ret, i; +int video_idx = -1; +int id = 0; +int64_t pts, now, start; +struct Segment *seg = NULL; +AVPacket pkt; +AVStream *in_stream; +AVRational tb; +tb.num = 1; +tb.den = AV_TIME_BASE; +AVStream *stream; +AVCodecContext *avctx; +AVCodecParameters *params; +enum AVMediaType type; + +if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { +av_log(NULL, AV_LOG_ERROR, "Could not get input stream info.\n"); +goto end; +} + +av_log(NULL, AV_LOG_INFO, "Finding video stream.\n"); +for (i = 0; i < ifmt_ctx->nb_streams; i++) { +av_log(NULL, AV_LOG_DEBUG, "Checking stream %d\n", i); +stream = ifmt_ctx->streams[i]; +avctx = avcodec_alloc_context3(NULL); +if (!avctx) { +av_log(NULL, AV_LOG_ERROR, "Could not allocate AVCodecContext.\n"); +goto end; +} +if ((ret = avcodec_parameters_to_context(avctx, stream->codecpar)) < 0) { +av_log(NULL, AV_LOG_ERROR, "Could not copy codec parameters.\n"); +goto end; +} +params = stream->codecpar; +type = params->codec_type; +if (type == AVMEDIA_TYPE_VIDEO) { +video_idx = i; +break; +} +} +if (video_idx == -1) { +av_log(NULL, AV_LOG_ERROR, "No video stream found.\n"); +goto end; +} + + +// All information needed to start segmenting the file is gathered now. +// start BUFFER_SECS seconds "in the past" to "catch up" to real-time. Has no effect on streamed sources. +start = av_gettime_relative() - BUFFER_SECS * AV_TIME_BASE; + +// segmenting main-loop + +for (;;) { +ret = av_read_frame(ifmt_ctx, &pkt); +if (ret < 0) +break; + +in_stream = ifmt_ctx->streams[pkt.stream_index]; +if (pkt.pts == AV_NOPTS_VALUE) { +pkt.pts = 0; +} +if (pkt.dts == AV_NOPTS_VALUE) { +pkt.dts = 0; +} + +// current pts +pts = av_rescale_q(pkt.pts, in_stream->time_base, tb); + +// current stream "uptime" +now = av_gettime_relative() - start; + +// simulate real-time reading +while (pts > now) { +usleep(1000); +now = av_gettime_relative() - start; +} + +// keyframe or first Segment +if ((pkt.flags & AV_PKT_FLAG_KEY && pkt.stream_index == video_idx) || !seg) { +if (seg) { +segment_close(seg); + +publisher_push_segment(info->pub, seg); +av_log(NULL, AV_LOG_DEBUG, "New segment pushed.\n"); +publish(info->pub); + av_log(NULL, AV_LOG_DEBUG, "Published new segment.\n"); +} +segment_init(&seg, ifmt_ctx); +seg->id = id++; +av_log(NULL, AV_LOG_DEBUG, "Starting new segment, id: %d\n", seg->id); +} + +segment_ts_append(seg, pkt.dts, pkt.pts); +ret = av_write_frame(seg->fmt_ctx, &pkt); +av_packet_unref(&pkt); +if (ret < 0) { +av_log(NULL,AV_LOG_ERROR, "av_write_frame() failed.\n"); +goto end; +} +} + +if (ret