From 2f10a73cbbe4333e2a41e87a94eda7fb3d442dc5 Mon Sep 17 00:00:00 2001 From: deva Date: Mon, 17 Mar 2008 09:10:42 +0000 Subject: Major code changes... FFMPEG introduced. Project splitup into subfolders. --- miavd/ffoutput.cc | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 miavd/ffoutput.cc (limited to 'miavd/ffoutput.cc') 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 +#include +#include +#include + +#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); +} -- cgit v1.2.3