/* -*- 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); }