4
|
1 /**
|
|
2 * Hoofbaby -- http://www.dsource.org/projects/hoofbaby
|
|
3 * Copyright (C) 2009 Robert Fraser
|
|
4 *
|
|
5 * This program is free software; you can redistribute it andor
|
|
6 * modify it under the terms of the GNU General Public License
|
|
7 * as published by the Free Software Foundation; either version 2
|
|
8 * of the License, or (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
|
|
16 module hoofbaby.codec.encoder;
|
|
17
|
|
18 import hoofbaby.codec.libav.avutil;
|
|
19 import hoofbaby.codec.libav.avcodec;
|
|
20 import hoofbaby.codec.libav.avformat;
|
|
21
|
|
22 public final class Encoder
|
|
23 {
|
|
24 // TODO convert asserts to exceptions
|
|
25
|
|
26 private const int OUTBUF_SIZE = 100000;
|
|
27
|
|
28 private AVOutputFormat* format;
|
|
29 private AVFormatContext* formatContext;
|
|
30
|
|
31 private AVCodec* audioCodec;
|
|
32 private AVCodecContext* audioContext;
|
|
33 private AVStream* audioStream;
|
|
34 private char* audioOutbuf;
|
|
35
|
|
36 private AVCodec* videoCodec;
|
|
37 private AVCodecContext* videoContext;
|
|
38 private AVStream* videoStream;
|
|
39 private char* videoOutbuf;
|
|
40
|
|
41 public this()
|
|
42 {
|
|
43 int ret; // Stores return value of functions called within
|
|
44
|
|
45 //--------------------------------------------------------------------------
|
|
46 // Container format
|
|
47
|
|
48 format = guess_format("asf", null, null);
|
|
49 assert(format !is null, "Could not find format");
|
|
50
|
|
51 formatContext = av_alloc_format_context();
|
|
52 assert(formatContext !is null, "Could not allocate format context");
|
|
53 res.add(formatContext, &av_free);
|
|
54 formatContext.oformat = fmt;
|
|
55 //ctx.preload = cast(int) (0.5 * AV_TIME_BASE);
|
|
56 formatContext.max_delay = cast(int) (0.7 * AV_TIME_BASE);
|
|
57 formatContext.loop_output = AVFMT_NOOUTPUTLOOP;
|
|
58 formatContext.flags |= AVFMT_FLAG_NONBLOCK;
|
|
59
|
|
60 AVFormatParameters params;
|
|
61 params.prealloced_context = 1;
|
|
62 params.video_codec_id = CODEC_ID_WMV2;
|
|
63 params.audio_codec_id = CODEC_ID_WMAV2;
|
|
64 params.width = 352; // TEMP
|
|
65 params.height = 288; // TEMP
|
|
66 params.time_base.num = 1; // TEMP
|
|
67 params.time_base.den = 25; // TEMP
|
|
68 params.pix_fmt = PIX_FMT_YUV420P;
|
|
69 params.channels = 2; // NEXTVERSION support >2 channels for devices that can handle it
|
|
70 params.sample_rate = 44100; // TEMP
|
|
71 ret = av_set_parameters(formatContext, null);
|
|
72 assert(ret >= 0, "Could not set parameters");
|
|
73
|
|
74 //--------------------------------------------------------------------------
|
|
75 // Video stream
|
|
76
|
|
77 videoStream = av_new_stream(formatContext, 0);
|
|
78 assert(videoStream !is null, "Could not allocate video stream");
|
|
79 formatContext.streams[0] = vstream;
|
|
80
|
|
81 videoCodec = avcodec_find_encoder(CODEC_ID_WMV2);
|
|
82 assert(videoCodec !is null, "Could not find video codec");
|
|
83
|
|
84 videoContext = videoStream.codec;
|
|
85 videoContext.codec_id = CODEC_ID_WMV2;
|
|
86 videoContext.codec_type = CODEC_TYPE_VIDEO;
|
|
87 videoContext.bit_rate = 400000; //TEMP
|
|
88 videoContext.width = 352; // TEMP
|
|
89 videoContext.height = 288; // TEMP
|
|
90 videoContext.gop_size = 12; // TEMP
|
|
91 videoContext.qmin = 3; // TEMP
|
|
92 videoContext.time_base.den = 25; // TEMP
|
|
93 videoContext.time_base.num = 1; // TEMP
|
|
94 videoContext.pix_fmt = PIX_FMT_YUV420P;
|
|
95 videoContext.flags |= CODEC_FLAG_GLOBAL_HEADER;
|
|
96 ret = avcodec_open(videoContext, videoCodec);
|
|
97 assert(ret >= 0, "Could not open video codec");
|
|
98
|
|
99 videoOutbuf = cast(char*) av_malloc(OUTBUF_SIZE);
|
|
100 assert(videoOutbuf !is null, "Could not allocate video output buffer");
|
|
101
|
|
102 //--------------------------------------------------------------------------
|
|
103 // Audio stream
|
|
104
|
|
105 audioStream = av_new_stream(formatContext, 0);
|
|
106 assert(audioStream !is null, "Could not allocate audio stream");
|
|
107 formatContext.streams[1] = audioStream;
|
|
108
|
|
109 audioCodec = avcodec_find_encoder(CODEC_ID_WMAV2);
|
|
110 assert(audioCodec !is null, "Could not find audio codec");
|
|
111
|
|
112 audioContext = audioStream.codec;
|
|
113 audioContext.codec_id = CODEC_ID_WMAV2;
|
|
114 audioContext.codec_type = CODEC_TYPE_AUDIO;
|
|
115 audioContext.bit_rate = 64000;
|
|
116 audioContext.sample_rate = 44100;
|
|
117 audioContext.channels = 2;
|
|
118 audioContext.flags |= CODEC_FLAG_GLOBAL_HEADER;
|
|
119 ret = avcodec_open(audioContext, audioCodec);
|
|
120 assert(res >= 0, "Could not open audio codec");
|
|
121
|
|
122 audioOutbuf = cast(char*) av_malloc(OUTBUF_SIZE);
|
|
123 assert(audioOutbuf !is null, "Could not allocate audio output buffer");
|
|
124
|
|
125 scope(failure)
|
|
126 {
|
|
127 freeResources();
|
|
128 }
|
|
129 }
|
|
130
|
|
131 public ~this()
|
|
132 {
|
|
133 freeResources();
|
|
134 }
|
|
135
|
|
136 public void writeHeader()
|
|
137 {
|
|
138 int ret = av_write_header(formatContext);
|
|
139 assert(ret >= 0, "Could not write header for output file (incorrect codec paramters?)");
|
|
140 }
|
|
141
|
|
142 public void writeTrailer()
|
|
143 {
|
|
144 int ret = av_write_trailer(ctx);
|
|
145 assert(ret >= 0, "Could not write trailer for output file");
|
|
146 }
|
|
147
|
|
148 public void writeVideoFrame(AVFrame* frame)
|
|
149 {
|
|
150 int outSize = avcodec_encode_video(videoCodec, videoOutbuf, OUTBUF_SIZE, frame);
|
|
151
|
|
152 // if zero size, it means the image was buffered, so don't write the packet
|
|
153 if(outSize > 0)
|
|
154 {
|
|
155 AVPacket pkt;
|
|
156 av_init_packet(&pkt);
|
|
157 pkt.pts = av_rescale_q(videoCodec.coded_frame.pts, videoCodec.time_base, videoStream.time_base);
|
|
158 if(videoCodec.coded_frame.key_frame)
|
|
159 pkt.flags |= PKT_FLAG_KEY;
|
|
160 pkt.stream_index = videoStream.index;
|
|
161 pkt.data = videoOutbuf;
|
|
162 pkt.size = outSize;
|
|
163 int ret = av_write_frame(ctx, &pkt);
|
|
164 assert(!ret, "Error writing video frame");
|
|
165 }
|
|
166 }
|
|
167
|
|
168 public void writeAudioFrame(short* samples)
|
|
169 {
|
|
170 AVPacket pkt;
|
|
171 av_init_packet(&pkt);
|
|
172 pkt.size = avcodec_encode_audio(audioCodec, audioOutbuf, OUTBUF_SIZE, samples);
|
|
173 //pkt.pts = av_rescale_q(acodec.coded_frame.pts, acodec.time_base, acodec.time_base);
|
|
174 pkt.flags |= PKT_FLAG_KEY;
|
|
175 pkt.stream_index = audioStream.index;
|
|
176 pkt.data = audioOutbuf;
|
|
177 int ret = av_write_frame(ctx, &pkt) != 0;
|
|
178 assert(ret == 0, "Error writing audio frame");
|
|
179 }
|
|
180
|
|
181 private void freeResources()
|
|
182 {
|
|
183 if(formatContext) av_free(formatContext);
|
|
184 if(audioOutbuf) av_free(audioOutbuf);
|
|
185 if(videoOutbuf) av_free(videoOutbuf);
|
|
186 }
|
|
187 }
|