1 /*
2  *  KCemu -- The emulator for the KC85 homecomputer series and much more.
3  *  Copyright (C) 1997-2010 Torsten Paul
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "kc/config.h"
21 #ifdef HAVE_LIBAVFORMAT
22 
23 #include "kc/system.h"
24 
25 #include "ui/gtk/ffmpeg.h"
26 
FfmpegVideoEncoder(void)27 FfmpegVideoEncoder::FfmpegVideoEncoder(void)
28 {
29   _context = NULL;
30   _stream = NULL;
31   _frame = NULL;
32   _buf = NULL;
33 }
34 
~FfmpegVideoEncoder(void)35 FfmpegVideoEncoder::~FfmpegVideoEncoder(void)
36 {
37   close();
38 }
39 
40 bool
init(const char * filename,int width,int height,int fps_den,double quality)41 FfmpegVideoEncoder::init(const char *filename, int width, int height, int fps_den, double quality)
42 {
43   if (filename == NULL)
44     return false;
45 
46   _width = width;
47   _height = height;
48 
49   av_register_all();
50 
51   AVOutputFormat *fmt = av_guess_format("avi", NULL, "video/x-msvideo");
52   if (fmt == NULL)
53     return false;
54 
55   _context = avformat_alloc_context();
56   if (_context == NULL)
57     return false;
58 
59   _context->oformat = fmt;
60   snprintf(_context->filename, sizeof (_context->filename), "%s", filename);
61 
62   _stream = avformat_new_stream(_context, NULL);
63   if (_stream == NULL)
64     {
65       close();
66       return false;
67     }
68   _stream->id = 0;
69   _stream->codec->codec_id = fmt->video_codec;
70   _stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
71   _stream->codec->codec_tag = MKTAG('D', 'X', '5', '0');
72 
73   _stream->codec->bit_rate = 79000 + 1000 * pow(1.4, quality * 20.0);
74   _stream->codec->width = width;
75   _stream->codec->height = height;
76   _stream->codec->time_base.den = 50;
77   _stream->codec->time_base.num = fps_den;
78   _stream->codec->gop_size = 100 / fps_den;
79   _stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
80 
81   // some formats want stream headers to be separate
82   if (_context->oformat->flags & AVFMT_GLOBALHEADER)
83     _stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
84 
85   AVCodec *codec = avcodec_find_encoder(_stream->codec->codec_id);
86   if (avcodec_open2(_stream->codec, codec, NULL) < 0)
87     {
88       close();
89       return false;
90     }
91 
92   _bufsize = 10 * width * height;
93   _buf = (byte_t *)av_malloc(_bufsize);
94   if (_buf == NULL)
95     {
96       close();
97       return false;
98     }
99 
100   _frame = av_frame_alloc();
101   if (_frame == NULL)
102     {
103       close();
104       return false;
105     }
106 
107   int size = avpicture_get_size(_stream->codec->pix_fmt, width, height);
108   byte_t *buf = (byte_t *) av_malloc(size);
109   if (buf == NULL)
110     {
111       close();
112       return false;
113     }
114 
115   avpicture_fill((AVPicture *) _frame, buf, _stream->codec->pix_fmt, width, height);
116 
117   if (avio_open(&_context->pb, filename, AVIO_FLAG_WRITE) < 0)
118     {
119       close();
120       return false;
121     }
122 
123   //dump_format(_context, 0, filename, 1);
124   avformat_write_header(_context, NULL);
125   return true;
126 }
127 
128 void
allocate_color_rgb(int idx,int r,int g,int b)129 FfmpegVideoEncoder::allocate_color_rgb(int idx, int r, int g, int b)
130 {
131   _col[idx].y =  (0.257 * r) + (0.504 * g) + (0.098 * b) + 16;
132   _col[idx].u = -(0.148 * r) - (0.291 * g) + (0.439 * b) + 128;
133   _col[idx].v =  (0.439 * r) - (0.368 * g) - (0.071 * b) + 128;
134 }
135 
136 bool
encode(byte_t * image,byte_t * dirty)137 FfmpegVideoEncoder::encode(byte_t *image, byte_t *dirty)
138 {
139   for (int y = 0;y < _height;y++)
140     {
141       int z = y * _frame->linesize[0];
142       for (int x = 0;x < _width;x++)
143         {
144           _frame->data[0][z + x] = _col[image[z + x]].y;
145         }
146     }
147   for (int y = 0;y < _height / 2;y++)
148     {
149       int z = 2 * y * _width;
150       for (int x = 0;x < _width;x += 2)
151         {
152           int u = _col[image[z + x]].u + _col[image[z + x + 1]].u + _col[image[z + x + _width]].u + _col[image[z + x + _width + 1]].u;
153           int v = _col[image[z + x]].v + _col[image[z + x + 1]].v + _col[image[z + x + _width]].v + _col[image[z + x + _width + 1]].v;
154           _frame->data[1][y * _frame->linesize[1] + x / 2] = u / 4;
155           _frame->data[2][y * _frame->linesize[2] + x / 2] = v / 4;
156         }
157     }
158 
159   AVPacket pkt;
160   av_init_packet(&pkt);
161   pkt.data = _buf;
162   pkt.size = _bufsize;
163 
164   int got_packet = 0;
165   int ret = avcodec_encode_video2(_stream->codec, &pkt, _frame, &got_packet);
166   if (ret < 0 || !got_packet || pkt.size <= 0)
167     return true;
168 
169   if (_stream->codec->coded_frame->pts != AV_NOPTS_VALUE)
170     pkt.pts = av_rescale_q(_stream->codec->coded_frame->pts, _stream->codec->time_base, _stream->time_base);
171   if (_stream->codec->coded_frame->key_frame)
172     pkt.flags |= AV_PKT_FLAG_KEY;
173   pkt.stream_index = _stream->index;
174 
175   return av_interleaved_write_frame(_context, &pkt) == 0;
176 }
177 
178 void
close(void)179 FfmpegVideoEncoder::close(void)
180 {
181   if (_context == NULL)
182     return;
183 
184   av_write_trailer(_context);
185 
186   avcodec_close(_stream->codec);
187   av_free(_frame->data[0]);
188   av_free(_frame);
189   av_free(_buf);
190 
191   for (unsigned int i = 0;i < _context->nb_streams;i++)
192     {
193       av_freep(&_context->streams[i]->codec);
194       av_freep(&_context->streams[i]);
195     }
196   avio_close(_context->pb);
197 
198   av_free(_context);
199 
200   _context = NULL;
201   _stream = NULL;
202   _frame = NULL;
203   _buf = NULL;
204 }
205 
206 #endif /* HAVE_LIBAVFORMAT */
207