r499: This commit was manufactured by cvs2svn to create tag 'r1_2_1-last'.
[cinelerra_cv/mob.git] / hvirtual / cinelerra / fileyuv.C
blob5e3a050bcd4e9bc2f33cdb3fa6251a05752d2a52
1 #include "fileyuv.h"
2 #include "asset.h"
3 #include "file.h"
4 #include "guicast.h"
5 #include "mwindow.h"
6 #include "defaults.h"
7 #include "vframe.h"
8 #include "edit.h"
9 #include "quicktime.h"
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <errno.h>
17 FileYUV::FileYUV(Asset *asset, File *file)
18         : FileBase(asset, file)
20         if (asset->format == FILE_UNKNOWN) asset->format = FILE_YUV;
21         asset->byte_order = 0; // FUTURE: is this always correct?
22         temp = 0;
23         ffmpeg = 0;
24         stream = new YUVStream();
25         pipe_latency = 0;
28 FileYUV::~FileYUV()
30         // NOTE: close_file() is already called
31         delete stream;
33         
34 int FileYUV::open_file(int should_read, int should_write)
36         int result;
38         if (should_read) {
40                 result = stream->open_read(asset->path);
41                 if (result) return result;
43                 // NOTE: no easy way to defer setting video_length
44                 asset->video_length = stream->frame_count;
45                 
46                 asset->width = stream->get_width();
47                 asset->height = stream->get_height();
48                 if (asset->width * asset->height <= 0) {
49                         printf("illegal frame size '%d x %d'\n", 
50                                asset->width, asset->height);
51                         return 1;
52                 }
53                 
54                 asset->layers = 1;
55                 asset->video_data = 1;
56                 asset->audio_data = 0;
57                 
58                 asset->frame_rate = stream->get_frame_rate();
59                 asset->aspect_ratio = stream->get_aspect_ratio();
61                 return 0;
62         }
64         if (should_write) {
65                 if (asset->use_pipe) {
66                         result = stream->open_write(asset->path, asset->pipe);
67                 }
68                 else {
69                         result = stream->open_write(asset->path, NULL);
70                 }
71                 if (result) return result;
73                 stream->set_width(asset->width);
74                 stream->set_height(asset->height);
75                 stream->set_frame_rate(asset->frame_rate);
76                 stream->set_aspect_ratio(asset->aspect_ratio);
78                 result = stream->write_header();
79                 if (result) return result;
80                 
81                 return 0;
82         }
83         
84         // no action given
85         return 1;
88 int FileYUV::close_file() {
89         if (pipe_latency && ffmpeg && stream) {
90                 // deal with last frame still in the pipe
91                 ensure_temp(incoming_asset->width,
92                             incoming_asset->height); 
93                 if (ffmpeg->decode(NULL, 0, temp) == 0) {
94                         uint8_t *yuv[3];
95                         yuv[0] = temp->get_y();
96                         yuv[1] = temp->get_u();
97                         yuv[2] = temp->get_v();
98                         stream->write_frame(yuv);
99                 }
100                 pipe_latency = 0;
101         }
102         stream->close_fd();
103         if (ffmpeg) delete ffmpeg;
104         ffmpeg = 0;
105         return 0;
108 // NOTE: set_video_position() called every time a frame is read
109 int FileYUV::set_video_position(int64_t frame_number) {
110         return stream->seek_frame(frame_number);
113 int FileYUV::read_frame(VFrame *frame)
115         int result;
116         VFrame *input = frame;
118         // short cut for direct copy routines
119         if (frame->get_color_model() == BC_COMPRESSED) {
120                 long frame_size = (long) // w*h + w*h/4 + w*h/4
121                         (stream->get_height() * stream->get_width() * 1.5); 
122                 frame->allocate_compressed_data(frame_size);
123                 frame->set_compressed_size(frame_size);
124                 return stream->read_frame_raw(frame->get_data(), frame_size);
125         }
126         
128         // process through a temp frame if necessary
129         if (! cmodel_is_planar(frame->get_color_model()) ||
130             (frame->get_w() != stream->get_width()) ||
131             (frame->get_h() != stream->get_height())) {
132                 ensure_temp(stream->get_width(),
133                             stream->get_height());
134                 input = temp;
135         }
137         uint8_t *yuv[3];
138         yuv[0] = input->get_y();
139         yuv[1] = input->get_u();
140         yuv[2] = input->get_v();
141         result = stream->read_frame(yuv);
142         if (result) return result;
144         // transfer from the temp frame to the real one
145         if (input != frame) {
146                 FFMPEG::convert_cmodel(input, frame);
147         }
148         
149         return 0;
152 int FileYUV::write_frames(VFrame ***layers, int len)
154         int result;
156         // only one layer supported
157         VFrame **frames = layers[0];
158         VFrame *frame;
160         for (int n = 0; n < len; n++) {
162                 frame = frames[n];
164                 // short cut for direct copy routines
165                 if (frame->get_color_model() == BC_COMPRESSED) {
166                         long frame_size = frame->get_compressed_size();
167                         if (incoming_asset->format == FILE_YUV) {
168                                 return stream->write_frame_raw
169                                         (frame->get_data(), frame_size);
170                         }
172                         // decode and write an encoded frame
173                         if (FFMPEG::codec_id(incoming_asset->vcodec) != CODEC_ID_NONE) {
174                                 if (! ffmpeg) {
175                                         ffmpeg = new FFMPEG(incoming_asset);
176                                         ffmpeg->init(incoming_asset->vcodec);
177                                 }
178                                 
179                                 ensure_temp(incoming_asset->width,
180                                             incoming_asset->height); 
181                                 int result = ffmpeg->decode(frame->get_data(),
182                                                             frame_size, temp);
184                                 // some formats are decoded one frame later
185                                 if (result == FFMPEG_LATENCY) {
186                                         // remember to write the last frame
187                                         pipe_latency++;
188                                         return 0;
189                                 }
191                                 if (result) {
192                                         delete ffmpeg;
193                                         ffmpeg = 0;
194                                         return 1;
195                                 }
198                                 uint8_t *yuv[3];
199                                 yuv[0] = temp->get_y();
200                                 yuv[1] = temp->get_u();
201                                 yuv[2] = temp->get_v();
202                                 return stream->write_frame(yuv);
203                         }
205                 }
207                 // process through a temp frame only if necessary
208                 if (! cmodel_is_planar(frame->get_color_model()) ||
209                     (frame->get_w() != stream->get_width()) ||
210                     (frame->get_h() != stream->get_height())) {
211                         ensure_temp(asset->width, asset->height);
212                         FFMPEG::convert_cmodel(frame, temp);
213                         frame = temp;
214                 }
216                 uint8_t *yuv[3];
217                 yuv[0] = frame->get_y();
218                 yuv[1] = frame->get_u();
219                 yuv[2] = frame->get_v();
220                 result = stream->write_frame(yuv);
221                 if (result) return result;
224         }
226         return 0;
230 void FileYUV::get_parameters(BC_WindowBase *parent_window, 
231                              Asset *asset, 
232                              BC_WindowBase* &format_window,
233                              int video_options,
234                              FormatTools *format)
236         if (! video_options) return;
238         YUVConfigVideo *config =  
239                 new YUVConfigVideo(parent_window, asset, format);
240         format_window = config;
241         config->create_objects();
242         if (config->run_window() == 0) {
243                 // save the new path and pipe to the asset
244                 strcpy(asset->path, config->path_textbox->get_text());
245                 strcpy(asset->pipe, config->pipe_config->textbox->get_text());
246                 // are we using the pipe (if there is one)
247                 asset->use_pipe = config->pipe_config->checkbox->get_value();
248                 // update the path textbox in the render window
249                 format->path_textbox->update(asset->path);
250                 // set the pipe status in the render window
251                 format->pipe_status->set_status(asset);
252                 // and add the new path and pipe to the defaults list
253                 const char *prefix = FILE_FORMAT_PREFIX(asset->format);
254                 config->path_recent->add_item(prefix, asset->path);
255                 config->pipe_config->recent->add_item(prefix, asset->pipe);
256         }
257         delete config;
260 int FileYUV::check_sig(Asset *asset)
262         char temp[9];
263         FILE *f = fopen(asset->path, "rb");
265         // check for starting with "YUV4MPEG2"
266         fread(&temp, 9, 1, f);
267         fclose(f);
268         if (strncmp(temp, "YUV4MPEG2", 9) == 0) return 1;
270         return 0;
273 // NOTE: this is called on the write stream, not the read stream!
274 //       as such, I have no idea what one is supposed to do with position.
275 int FileYUV::can_copy_from(Edit *edit, int64_t position)
276 {       // NOTE: width and height already checked in file.C
278         // FUTURE: is the incoming asset already available somewhere?
279         incoming_asset = edit->asset;
281         if (edit->asset->format == FILE_YUV) return 1;
283         // if FFMPEG can decode it, we'll accept it
284         if (FFMPEG::codec_id(edit->asset->vcodec) != CODEC_ID_NONE) {
285                 return 1;
286         }
288         incoming_asset = 0;
290         return 0;
293 int FileYUV::get_best_colormodel(Asset *asset, int driver) 
295         // FUTURE: is there a reason to try to accept anything else?  
296         return BC_YUV420P;
300 int FileYUV::colormodel_supported(int color_model) 
302         // we convert internally to any color model proposed
303         return color_model;
304         // NOTE: file.C does not convert from YUV, so we have to do it.  
308 /*  
309     Other member functions used in other file* modules:
311     write_compressed_frame(): used for record, so probably not needed
312     read_compressed_frame(): perhaps never used?
313     get_video_position: used by record only
314     reset_parameters(): not sure when used or needed
315     reset_parameters_derived(): not sure when used or needed
316     *_audio_*: yuv4mpeg doesn't handle audio
317     
319         
322 void FileYUV::ensure_temp(int width, int height) {
323         
324         // make sure the temp is correct size and type
325         if (temp && (temp->get_w() != width ||
326                      temp->get_h() != height ||
327                      temp->get_color_model() != BC_YUV420P)) {
328                 delete temp;
329                 temp = 0;
330         }
331         
332         // create a correct temp frame if we don't have one
333         if (temp == 0) {
334                 temp = new VFrame(0, width, height, BC_YUV420P);
335         }
339 YUVConfigVideo::YUVConfigVideo(BC_WindowBase *parent_window, Asset *asset, 
340                                FormatTools *format)
341         : BC_Window(PROGRAM_NAME ": YUV4MPEG Stream",
342                     parent_window->get_abs_cursor_x(1),
343                     parent_window->get_abs_cursor_y(1),
344                     500,
345                     150)
347         this->parent_window = parent_window;
348         this->asset = asset;
349         this->format = format;
350         this->defaults = format->mwindow->defaults;
353 YUVConfigVideo::~YUVConfigVideo()
355         delete path_textbox;
356         delete path_recent;
357         delete pipe_config;
358         delete mpeg2enc;
359         delete ffmpeg;
362 int YUVConfigVideo::create_objects()
364         int init_x = 10;
365         int init_y = 10;
366         
367         int x = init_x;
368         int y = init_y;
370         add_subwindow(new BC_Title(x, y, _("Output Path:")));
371         x += 120;
372         path_textbox = new BC_TextBox(x, y, 350, 1, asset->path);
373         add_subwindow(path_textbox);
375         x += 350;
376         path_recent = new BC_RecentList("PATH", defaults, path_textbox, 
377                                         10, x, y, 350, 100);
378         add_subwindow(path_recent);
379         path_recent->load_items(FILE_FORMAT_PREFIX(asset->format));
381         x = init_x;
382         y += 40;
384         pipe_config = new PipeConfig(this, defaults, asset);
385         pipe_config->create_objects(x, y, 350, asset->format);
386         
388         x = init_x;
389         y += 40;
391         add_subwindow(new BC_Title(x, y, _("Presets:")));
392         x += 90;
393         mpeg2enc = new PipePreset(x, y, "mpeg2enc", pipe_config);
394         add_subwindow(mpeg2enc);
395         // NOTE: the '%' character will be replaced by the current path
396         // NOTE: to insert a real '%' double it up: '%%' -> '%'
397         // NOTE: preset items must have a '|' before the actual command
398         mpeg2enc->add_item(new BC_MenuItem ("(DVD) | mpeg2enc -f 8 -o %"));
399         mpeg2enc->add_item(new BC_MenuItem ("(VCD) | mpeg2enc -f 2 -o %"));
401         x += 160;
402         ffmpeg = new PipePreset(x, y, "ffmpeg", pipe_config);
403         add_subwindow(ffmpeg);
404         ffmpeg->add_item(new BC_MenuItem("(DVD) | ffmpeg -f yuv4mpegpipe -i - -y -target dvd %"));
405         ffmpeg->add_item(new BC_MenuItem("(VCD) | ffmpeg -f yuv4mpegpipe -i - -y -target vcd %"));
407         add_subwindow(new BC_OKButton(this));
408         add_subwindow(new BC_CancelButton(this));
409         show_window();
410         return 0;
413 int YUVConfigVideo::close_event()
415         set_done(0);
416         return 1;