summaryrefslogtreecommitdiff
path: root/miavd/ffoutput.cc
diff options
context:
space:
mode:
Diffstat (limited to 'miavd/ffoutput.cc')
-rw-r--r--miavd/ffoutput.cc400
1 files changed, 400 insertions, 0 deletions
diff --git a/miavd/ffoutput.cc b/miavd/ffoutput.cc
new file mode 100644
index 0000000..963e694
--- /dev/null
+++ b/miavd/ffoutput.cc
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * ffoutput.cc
+ *
+ * Fri Dec 28 18:09:18 CET 2007
+ * Copyright 2007 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of MIaV.
+ *
+ * MIaV is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MIaV 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MIaV; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "ffoutput.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "miav_config.h"
+
+#define INDEX_PACKET_HEADER_SIZE 8192
+
+/*
+ * add a video output stream
+ */
+AVStream *FFOutput::add_video_stream(AVFormatContext *oc, CodecID codec_id)
+{
+ AVCodecContext *c;
+ AVStream *st;
+
+ int stream_id = 0;//av_find_default_stream_index(oc);
+ st = av_new_stream(oc, stream_id);
+ if (!st) {
+ MIaV::info->error("Could not alloc video stream");
+ }
+
+ AVCodec *codec = avcodec_find_encoder(codec_id);
+ if(!codec) {
+ MIaV::info->error("Could not find video encoder codec.");
+ }
+
+ AVFormatParameters parms;
+ parms.mpeg2ts_raw = 1;
+ parms.mpeg2ts_compute_pcr = 1;
+ if (av_set_parameters(oc, NULL) < 0) {
+ MIaV::info->error("Invalid output format parameters.");
+ }
+
+ // Get config values
+ int bitrate = config->readInt("video_bitrate");
+ int num_threads = config->readInt("encoding_threads");
+
+
+ c = st->codec;
+ c->codec_id = codec_id;
+ c->bit_rate = bitrate * 1000;
+ // c->width = 480; c->height = 320;
+ c->width = 720; c->height = 576;
+ c->time_base= (AVRational){1,25};
+ //c->time_base= (AVRational){2,25};
+ c->gop_size = 12; // Emit one intra frame every twelve frames at most.
+ c->pix_fmt = PIX_FMT_YUV420P; //NONE
+ c->codec_type = CODEC_TYPE_VIDEO;
+ c->max_b_frames = 0;
+ // c->flags2 |= CODEC_FLAG2_8X8DCT;
+ c->strict_std_compliance = FF_COMPLIANCE_VERY_STRICT;
+
+ c->debug = 0;
+ c->debug_mv = 0;
+
+ c->rtp_payload_size = 1500;
+ c->rtp_mode = 1;
+
+ c->thread_count = num_threads;
+
+ if(avcodec_open(c, codec) < 0) {
+ MIaV::info->error("Could not open video encoder codec.");
+ }
+
+ if(c->thread_count > 1) avcodec_thread_init(c, c->thread_count);
+
+ /* // Later version of ffmpeg
+ AVProgram *p = av_new_program(oc, stream_id);
+ p->provider_name = "Aasimon.org";
+ p->name = "MIaV";
+ */
+
+
+ snprintf(oc->title, 512, "MIaV");
+ snprintf(oc->author, 512, "Aasimon.org");
+ /*
+ char copyright[512];
+ char comment[512];
+ char album[512];
+ int year;
+ int track;
+ char genre[32];
+*/
+ return st;
+}
+
+
+/*
+ * add an audio output stream
+ */
+AVStream *FFOutput::add_audio_stream(AVFormatContext *oc, CodecID codec_id)
+{
+ AVCodecContext *c;
+ AVStream *st;
+
+ int stream_id = 0;//av_find_default_stream_index(oc);
+ st = av_new_stream(oc, stream_id + 1);
+ if (!st) {
+ MIaV::info->error("Could not alloc audio stream");
+ }
+
+ AVCodec *codec = avcodec_find_encoder(codec_id);
+ if(!codec) {
+ MIaV::info->error("Could not find audio encoder codec.");
+ }
+
+ // Get config values
+ int bitrate = config->readInt("audio_bitrate");
+
+ c = st->codec;
+ c->codec_id = codec_id;
+ c->codec_type = CODEC_TYPE_AUDIO;
+ c->bit_rate = bitrate * 1000;
+ c->sample_rate = 48000;
+ c->channels = 2;
+ c->sample_fmt = SAMPLE_FMT_S16;
+
+ c->debug = 0;
+
+ c->rtp_payload_size = 1500;
+ c->rtp_mode = 1;
+
+ if(avcodec_open(st->codec, codec) < 0) {
+ MIaV::info->error("Could not open audio encoder codec.");
+ }
+
+ return st;
+}
+
+FFOutput::FFOutput(const char *cpr, const char *clientip)
+{
+ avcodec_init();
+ av_register_all();
+
+ // Get filename
+ // Create path and filename
+ char fname[256];
+ string *server_root;
+ char birthmonth[3];
+ char date[32];
+
+ // Get server root
+ server_root = config->readString("server_movie_root");
+
+ // Copy the bytes representing the birth month from the cpr
+ // [dd][mm][yy]-[nn][nn]
+ strncpy(birthmonth, &cpr[2], 2);
+ birthmonth[2] = 0;
+
+ // Create date (today) in [yyyy][mm][dd]
+ struct tm *ltime;
+ time_t t = time(NULL);
+ ltime = localtime(&t);
+ sprintf(date, "%.4d%.2d%.2d",
+ ltime->tm_year + 1900,
+ ltime->tm_mon + 1, // Ranging from 0 to 11
+ ltime->tm_mday);
+
+ sprintf(fname, "%s/%s/%s/%s-%s-", server_root->c_str(), birthmonth, cpr, cpr, date);
+
+ file = new File(fname, "avi", MIaV::info);
+
+ open();
+
+ maxfilesize = -1;//10000000;
+}
+
+FFOutput::~FFOutput()
+{
+ MIaV::info->info("~FFOutput...");
+
+ close();
+
+ if(video_st->codec->thread_count > 1) avcodec_thread_free(video_st->codec);
+ if(net_video_st->codec->thread_count > 1) avcodec_thread_free(net_video_st->codec);
+
+ delete file;
+
+ MIaV::info->info("~FFOutput...done");
+}
+
+void FFOutput::open()
+{
+ enum CodecID vcodecid = CODEC_ID_H264;
+ enum CodecID acodecid = CODEC_ID_MP3;
+
+ //
+ // Output file
+ //
+
+ filename = file->Open();
+
+ oc = av_alloc_format_context();
+
+ oc->oformat = guess_format(NULL, filename.c_str(), NULL);
+ if (!oc->oformat) {
+ MIaV::info->error("guess_format failed on %s", filename.c_str());
+ }
+
+ video_st = add_video_stream(oc, vcodecid);
+ audio_st = add_audio_stream(oc, acodecid);
+
+ oc->flags |= AVFMT_FLAG_GENPTS;
+
+ // some formats want stream headers to be seperate
+ if(!strcmp(oc->oformat->name, "mp4")) {
+ // || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))
+ video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ }
+
+ // dump_format(oc, 0, filename.c_str(), 1);
+
+ // open the output file, if needed
+ if (!(oc->oformat->flags & AVFMT_NOFILE)) {
+ if (url_fopen(&(oc->pb), filename.c_str(), URL_WRONLY) < 0) {
+ MIaV::info->error("Could not open '%s': %s", filename.c_str(), strerror(errno));
+ }
+ }
+
+ av_write_header(oc);
+ keyframes = 0;
+ if(maxfilesize != -1) maxfilesize = maxfilesize - oc->pb.pos;
+
+
+ //
+ // RTSP
+ //
+
+ stream_oc = av_alloc_format_context();
+
+ char streamformat[] = "mpegts";//mpegts";//"m4v"; // "mpegts"; "rm";
+ stream_oc->oformat = guess_stream_format(streamformat, NULL, NULL);
+ if (!stream_oc->oformat) {
+ MIaV::info->error("guess_format failed on %s", streamformat);
+ }
+
+ net_video_st = add_video_stream(stream_oc, vcodecid);
+ net_audio_st = add_audio_stream(stream_oc, acodecid);
+
+ stream_oc->flags |= AVFMT_FLAG_GENPTS;
+
+ // open the output file, if needed
+ if (!(stream_oc->oformat->flags & AVFMT_NOFILE)) {
+ char streamname[] = "rtp://224.0.0.1:1234";
+ if (url_fopen(&(stream_oc->pb), streamname, URL_WRONLY) < 0) {
+ MIaV::info->error("Could not open '%s': %s", streamname, strerror(errno));
+ }
+ }
+ av_write_header(stream_oc);
+
+
+}
+
+void FFOutput::close()
+{
+ av_write_trailer(oc);
+ if (!(oc->oformat->flags & AVFMT_NOFILE)) {
+ url_fclose(&(oc->pb));
+ }
+
+ avcodec_close(audio_st->codec);
+ avcodec_close(video_st->codec);
+ av_free(oc);
+
+ av_write_trailer(stream_oc);
+ if (!(stream_oc->oformat->flags & AVFMT_NOFILE)) {
+ url_fclose(&(stream_oc->pb));
+ }
+
+ avcodec_close(net_audio_st->codec);
+ avcodec_close(net_video_st->codec);
+ av_free(stream_oc);
+}
+
+#ifndef INT64_C
+#define INT64_C(c) (c ## LL)
+#endif
+
+void FFOutput::writeFrame(FFFrame *frame)
+{
+ // return;
+ bit_buffer_size = 720*576*4;//video_st->codec->bit_rate * 1024 * 10;
+ bit_buffer = (uint8_t*)malloc(bit_buffer_size);
+
+ if( maxfilesize != -1 &&
+ oc->pb.pos + (keyframes * 170) + INDEX_PACKET_HEADER_SIZE > maxfilesize) { // Too big?
+ close();
+ open();
+ }
+
+ if(frame == NULL) return;
+ int ret = 0;
+ int stream_index = -1;
+ int64_t pts;
+ int64_t dts;
+ int pos = 0;
+
+
+ // printf("Now: %lld\n", av_gettime());
+ if(frame->pts == AV_NOPTS_VALUE) frame->pts = av_gettime();
+ if(frame->pts == 0) frame->pts = av_gettime();
+
+ switch(frame->codec_context->codec->type) {
+ case CODEC_TYPE_VIDEO:
+ stream_index = 0;
+ ret = avcodec_encode_video(net_video_st->codec,
+ bit_buffer,
+ bit_buffer_size,
+ frame->avframe);
+ pts = net_video_st->codec->coded_frame->pts;
+ //pts = vpts;
+ //vpts++;
+ break;
+
+ case CODEC_TYPE_AUDIO:
+ stream_index = 1;
+ ret = avcodec_encode_audio(net_audio_st->codec,
+ bit_buffer,
+ bit_buffer_size,
+ frame->pcmdata);
+ pts = net_audio_st->codec->coded_frame->pts;
+ // apts += 48000/25;
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+
+ // printf("Now: %lld\n", av_gettime());
+ if(pts == AV_NOPTS_VALUE) pts = av_gettime();
+ if(pts == 0) pts = av_gettime();
+
+ if(ret > 0){
+ AVPacket *pkt = (AVPacket*)malloc(sizeof(AVPacket));
+ av_init_packet(pkt);
+
+ // dts = pts;
+ // dts++;
+
+ pkt->pts = pts;
+ pkt->dts = 0;//AV_NOPTS_VALUE;
+ pkt->stream_index = stream_index;
+ if(stream_index == 1) pkt->flags |= PKT_FLAG_KEY;
+
+ pkt->data = bit_buffer;
+ pkt->size = ret;
+
+ if(video_st->codec->coded_frame && video_st->codec->coded_frame->key_frame) {
+ pkt->flags |= PKT_FLAG_KEY;
+ keyframes++;
+ }
+
+ if (!(oc->oformat->flags & AVFMT_NOFILE)) {
+ av_interleaved_write_frame(oc, pkt);
+ //av_write_frame(oc, pkt);
+ }
+ if (!(stream_oc->oformat->flags & AVFMT_NOFILE)) {
+ av_interleaved_write_frame(stream_oc, pkt);
+ //av_write_frame(oc, pkt);
+ }
+ free(pkt->data);
+ free(pkt);
+ }
+}
+
+void FFOutput::setSaveState(n_savestate savestate)
+{
+ file->setSaveState(savestate);
+}