r956: README.BUILD - add more library recommendations
[cinelerra_cv/ct.git] / cinelerra / ffmpeg.C
blob5815acb2c7cd42d97c073e0dcfbf5244b204b8a7
1 #include <string.h>
3 #include "filebase.h"
4 #include "quicktime.h"
5 #include "ffmpeg.h"
6 #include "guicast.h"
8 FFMPEG::FFMPEG(Asset *asset) {
9         this->asset = asset;
10         codec = 0;
11         context = 0;
12         picture = 0;
13         got_picture = 0;
16 int FFMPEG::init(char *codec_string) {
18         avcodec_init();
19         avcodec_register_all();
21         CodecID id = codec_id(codec_string);
22         codec = avcodec_find_decoder(id);
23         if (codec == NULL) {
24                 printf("FFMPEG::init no decoder for '%s'", codec_string);
25                 return 1;
26         }
28         context = avcodec_alloc_context();
30         if (avcodec_open(context, codec)) {
31                 printf("FFMPEG::init avcodec_open() failed\n");
32         }
34         picture = avcodec_alloc_frame();
37         return 0;
40 FFMPEG::~FFMPEG() {
41         avcodec_close(context);
42         free(context);
43         free(picture);
47 CodecID FFMPEG::codec_id(char *codec_string) {
48 #define CODEC_IS(x) (! strncmp(codec_string, x, 4))
50         if (CODEC_IS(QUICKTIME_DV) ||
51             CODEC_IS(QUICKTIME_DVSD)) return CODEC_ID_DVVIDEO;
52         
53         if (CODEC_IS(QUICKTIME_MP4V) ||
54             CODEC_IS(QUICKTIME_DIVX)) return CODEC_ID_MPEG4;
56         return CODEC_ID_NONE;
58 #undef CODEC_IS
61 PixelFormat FFMPEG::color_model_to_pix_fmt(int color_model) {
62         switch (color_model) 
63                 { 
64                 case BC_YUV422: 
65                           return PIX_FMT_YUV422;
66                 case BC_RGB888:
67                         return PIX_FMT_RGB24;
68                 case BC_BGR8888:  // NOTE: order flipped
69                         return PIX_FMT_RGBA32;
70                 case BC_BGR888:
71                         return PIX_FMT_BGR24;
72                 case BC_YUV420P: 
73                         return PIX_FMT_YUV420P;
74                 case BC_YUV422P:
75                         return PIX_FMT_YUV422P;
76                 case BC_YUV444P:
77                         return PIX_FMT_YUV444P;
78                 case BC_YUV411P:
79                         return PIX_FMT_YUV411P;
80                 case BC_RGB565:
81                         return PIX_FMT_RGB565;
82                 };
84         return PIX_FMT_NB;
87 int FFMPEG::pix_fmt_to_color_model(PixelFormat pix_fmt) {
88         switch (pix_fmt) 
89                 { 
90                 case PIX_FMT_YUV422:
91                         return BC_YUV422;
92                 case PIX_FMT_RGB24:
93                         return BC_RGB888;
94                 case PIX_FMT_RGBA32:
95                         return BC_BGR8888;
96                 case PIX_FMT_BGR24:
97                         return BC_BGR888;
98                 case PIX_FMT_YUV420P:
99                         return BC_YUV420P;
100                 case PIX_FMT_YUV422P:
101                         return BC_YUV422P;
102                 case PIX_FMT_YUV444P:
103                         return BC_YUV444P;
104                 case PIX_FMT_YUV411P:
105                         return BC_YUV411P;
106                 case PIX_FMT_RGB565:
107                         return BC_RGB565;
108                 };
110         return BC_TRANSPARENCY;
113 int FFMPEG::init_picture_from_frame(AVPicture *picture, VFrame *frame) {
114         int cmodel = frame->get_color_model();
115         PixelFormat pix_fmt = color_model_to_pix_fmt(cmodel);
117         int size = avpicture_fill(picture, frame->get_data(), pix_fmt, 
118                                   frame->get_w(), frame->get_h());
120         if (size < 0) {
121                 printf("FFMPEG::init_picture failed\n");
122                 return 1;
123         }
125         if (cmodel_is_planar(frame->get_color_model())) {
126                 // override avpicture_fill() for planar types
127                 picture->data[0] = frame->get_y();
128                 picture->data[1] = frame->get_u();
129                 picture->data[2] = frame->get_v();
130         }
132         return size;
136 int FFMPEG::convert_cmodel(VFrame *frame_in,  VFrame *frame_out) {
137   
138         PixelFormat pix_fmt_in = 
139                 color_model_to_pix_fmt(frame_in->get_color_model());
140         PixelFormat pix_fmt_out = 
141                 color_model_to_pix_fmt(frame_out->get_color_model());
143         // do conversion within libavcodec if possible
144         if (pix_fmt_in != PIX_FMT_NB && pix_fmt_out != PIX_FMT_NB) {
145                 // set up a temporary pictures from frame_in and frame_out
146                 AVPicture picture_in, picture_out;
147                 init_picture_from_frame(&picture_in, frame_in);
148                 init_picture_from_frame(&picture_out, frame_out);
150                 int result = img_convert(&picture_out,
151                                          pix_fmt_out,
152                                          &picture_in,
153                                          pix_fmt_in,
154                                          frame_in->get_w(),
155                                          frame_out->get_h());
156                 if (result) {
157                         printf("FFMPEG::convert_cmodel img_convert() failed\n");
158                 }
159                 return result;
160         }
163         // failing the fast method, use the failsafe cmodel_transfer()
164         return convert_cmodel_transfer(frame_in, frame_out);
165 }  
167 int FFMPEG::convert_cmodel_transfer(VFrame *frame_in, VFrame *frame_out) {
168         
169         // WARNING: cmodel_transfer is said to be broken with BC_YUV411P
170         cmodel_transfer
171                 (// Packed data out 
172                  frame_out->get_rows(), 
173                  // Packed data in
174                  frame_in->get_rows(),
176                  // Planar data out
177                  frame_out->get_y(), frame_out->get_u(), frame_out->get_v(),
178                  // Planar data in
179                  frame_in->get_y(), frame_in->get_u(), frame_in->get_v(),
181                  // Dimensions in
182                  0, 0, frame_in->get_w(), frame_in->get_h(),
183                  // Dimensions out
184                  0, 0, frame_out->get_w(), frame_out->get_h(),
186                  // Color models
187                  frame_in->get_color_model(), frame_out->get_color_model(),
189                  // Background color
190                  0,
191                  
192                  // Rowspans (of luma for YUV)
193                  frame_in->get_w(), frame_out->get_w()
194                  
195                  );
197         return 0;
201 int FFMPEG::convert_cmodel(AVPicture *picture_in, PixelFormat pix_fmt_in,
202                           int width_in, int height_in, VFrame *frame_out) {
204         // set up a temporary picture_out from frame_out
205         AVPicture picture_out;
206         init_picture_from_frame(&picture_out, frame_out);
207         int cmodel_out = frame_out->get_color_model();
208         PixelFormat pix_fmt_out = color_model_to_pix_fmt(cmodel_out);
210         // do conversion within libavcodec if possible
211         if (pix_fmt_out != PIX_FMT_NB) {
212                 int result = img_convert(&picture_out,
213                                          pix_fmt_out,
214                                          picture_in,
215                                          pix_fmt_in,
216                                          width_in,
217                                          height_in);
218                 if (result) {
219                         printf("FFMPEG::convert_cmodel img_convert() failed\n");
220                 }
221                 return result;
222         }
223         
224         // make an intermediate temp frame only if necessary
225         int cmodel_in = pix_fmt_to_color_model(pix_fmt_in);
226         if (cmodel_in == BC_TRANSPARENCY) {
227                 if (pix_fmt_in == PIX_FMT_RGBA32) {
228                         // avoid infinite recursion if things are broken
229                         printf("FFMPEG::convert_cmodel pix_fmt_in broken!\n");
230                         return 1;
231                 }
233                 // NOTE: choose RGBA8888 as a hopefully non-lossy colormodel
234                 VFrame *temp_frame = new VFrame(0, width_in, height_in, 
235                                                 BC_RGBA8888);
236                 if (convert_cmodel(picture_in, pix_fmt_in,
237                                   width_in, height_in, temp_frame)) {
238                         delete temp_frame;
239                         return 1;  // recursed call will print error message
240                 }
241                 
242                 int result = convert_cmodel(temp_frame, frame_out);
243                 delete temp_frame;
244                 return result;
245         }
247         
248         // NOTE: no scaling possible in img_convert() so none possible here
249         if (frame_out->get_w() != width_in ||
250             frame_out->get_h() != height_in) {
251                 printf("scaling from %sx%s to %sx%x not allowed\n",
252                        width_in, height_in, 
253                        frame_out->get_w(), frame_out->get_h());
254                 return 1;
255         }
258         // if we reach here we know that cmodel_transfer() will work
259         uint8_t *yuv_in[3] = {0,0,0};
260         uint8_t *row_pointers_in[height_in];
261         if (cmodel_is_planar(cmodel_in)) {
262                 yuv_in[0] = picture_in->data[0];
263                 yuv_in[1] = picture_in->data[1];
264                 yuv_in[2] = picture_in->data[2];
265         }
266         else {
267                 // set row pointers for picture_in 
268                 uint8_t *data = picture_in->data[0];
269                 int bytes_per_line = 
270                         cmodel_calculate_pixelsize(cmodel_in) * height_in;
271                 for (int i = 0; i < height_in; i++) {
272                         row_pointers_in[i] = data + i * bytes_per_line;
273                 }
274         }
276         cmodel_transfer
277                 (// Packed data out 
278                  frame_out->get_rows(), 
279                  // Packed data in
280                  row_pointers_in,
282                  // Planar data out
283                  frame_out->get_y(), frame_out->get_u(), frame_out->get_v(),
284                  // Planar data in
285                  yuv_in[0], yuv_in[1], yuv_in[2],
287                  // Dimensions in
288                  0, 0, width_in, height_in,  // NOTE: dimensions are same
289                  // Dimensions out
290                  0, 0, width_in, height_in,
292                  // Color model in, color model out
293                  cmodel_in, cmodel_out,
295                  // Background color
296                  0,
297                  
298                  // Rowspans in, out (of luma for YUV)
299                  width_in, width_in
300                  
301                  );
303         return 0;
306 int FFMPEG::decode(uint8_t *data, long data_size, VFrame *frame_out) { 
308         // NOTE: frame must already have data space allocated
309         
310         got_picture = 0;
311         int length = avcodec_decode_video(context,
312                                           picture,
313                                           &got_picture,
314                                           data,
315                                           data_size);
316         
317         if (length < 0) {
318                 printf("FFMPEG::decode error decoding frame\n");
319                 return 1;
320         }
322         if (! got_picture) {
323                 // signal the caller there is no picture yet
324                 return FFMPEG_LATENCY;
325         }
326         
327         int result = convert_cmodel((AVPicture *)picture, 
328                                     context->pix_fmt,
329                                     asset->width, 
330                                     asset->height, 
331                                     frame_out);
332         
333         return result;