r668: Configure.in and autogen.sh cleanup based on ideas by giskard.
[cinelerra_cv.git] / cinelerra / yuvstream.C
blob49f8a9706d2f0c64b38d2aa03c5e6a45b196edab
1 #include <fcntl.h>
2 #include <string.h>
3 #include <errno.h>
5 #include "guicast.h"
6 #include "pipe.h"
7 #include "yuvstream.h"
8 #include "interlacemodes.h"
10 YUVStream::YUVStream() { 
11         y4m_init_stream_info(&stream_info);
12         y4m_init_frame_info(&frame_info);
13         stream_fd = -1;
14         stream_pipe= 0;
15         frame_count = 0;
16         frame_index = 0;
19 YUVStream::~YUVStream() {               
20         y4m_fini_stream_info(&stream_info);
21         y4m_fini_frame_info(&frame_info);
22         if (frame_index) delete frame_index;
23         close_fd();
27 int YUVStream::open_read(char *path) {
28         // NOTE: reading from pipes would be very difficult without temp files
29         stream_fd = open(path, O_RDONLY);
31         if (stream_fd < 0) {
32                 printf("open(%s) failed: %s\n", path, strerror(errno));
33                 return 1;
34         }
36         int result = read_header();
37         if (result != Y4M_OK) {
38                 printf("bad YUV4MPEG2 header: %s\n", y4m_strerr(result));
39                 return 1;
40         }
42         // generate index to frame position if not done yet
43         if (frame_index == 0) {
44                 if (make_index() != 0) {
45                         return 1;
46                 }
47         }
49         return 0;
52 // NOTE: path is opened as a pipe if contains '|'
53 int YUVStream::open_write(char *path, char *pipe) {
54         if (pipe && *pipe) {
55                 // skip over the '|' if present
56                 if (char *p = strchr(path, '|')) {
57                         pipe = p + 1;
58                 }
60                 stream_pipe = new Pipe(pipe, path);
61                 if (stream_pipe->open_write() == 0) {
62                         stream_fd = stream_pipe->fd;
63                         return 0;
64                 }
65                 return 1;
66         }
68         stream_fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
69         if (stream_fd > 0) return 0;
70         
71         printf("open(%s) failed: %s\n", path, strerror(errno));
72         return 1;
76 void YUVStream::close_fd() {
77         if (stream_pipe) {
78                 stream_pipe->close();
79                 stream_pipe = 0;
80                 stream_fd = -1;
81         }
82                 
83         if (stream_fd >= 0) close(stream_fd);
84         stream_fd = -1;
87 int YUVStream::read_frame(uint8_t *yuv[3]) {
88         int result =  y4m_read_frame(stream_fd, &stream_info, 
89                                   &frame_info, yuv);
90         if (result != Y4M_OK) {
91                 if (result != Y4M_ERR_EOF) {
92                         printf("read_frame() failed: %s\n", 
93                                y4m_strerr(result));
94                 }
95                 return 1;
96         }
98         return 0;
101 int YUVStream::read_frame_raw(uint8_t *data, long frame_size) {
102 #if (MJPEGTOOLS_Y4M_WRITE_FRAME_HEADER__3ARGS)
103         int result = y4m_read_frame_header(stream_fd, &stream_info, &frame_info);
104 #else
105         int result = y4m_read_frame_header(stream_fd, &frame_info);
106 #endif
107         if (result != Y4M_OK) {
108                 printf("y4m_read_frame_header() failed: %s\n", 
109                        y4m_strerr(result));
110                 return 1;
111         }
112         result = y4m_read(stream_fd, data, frame_size);
113         if (result != Y4M_OK) {
114                 printf("y4m_read(%d) failed: %s\n", 
115                        frame_size, y4m_strerr(result));
116                 return 1;
117         }
118         return 0;
120                 
121 int YUVStream::write_frame(uint8_t *yuv[3]) {
122         int result = y4m_write_frame(stream_fd, &stream_info, 
123                                      &frame_info, yuv);
124         if (result != Y4M_OK) {
125                 printf("write_frame() failed: %s\n", y4m_strerr(result));
126                 return 1;
127         }
128         return 0;
130 int YUVStream::write_frame_raw(uint8_t *data, long frame_size) {
131 #if (MJPEGTOOLS_Y4M_WRITE_FRAME_HEADER__3ARGS)
132         int result = y4m_write_frame_header(stream_fd, &stream_info, &frame_info);
133 #else
134         int result = y4m_write_frame_header(stream_fd, &frame_info);
135 #endif
136         if (result != Y4M_OK) {
137                 printf("y4m_write_frame_header() failed: %s\n", 
138                        y4m_strerr(result));
139                 return 1;
140         }
141         result = y4m_write(stream_fd, data, frame_size);
142         if (result != Y4M_OK) {
143                 printf("y4m_write(%d) failed: %s\n", 
144                        frame_size, y4m_strerr(result));
145                 return 1;
146         }
147         return 0;
148 }               
150 int YUVStream::make_index() {
151         off_t position;
152         uint8_t *yuv[3];
154         // NOTE: make_index() must be called after read_header().
156         // NOTE: storing frame_index locally means it is destroyed too often.
157         //       make_index() will be called 3 times per file.  If this
158         //       becomes a performance problem, the index should be cached.
159         //       Storing in 'asset' helps some, but still is done twice.
160         if (frame_index) delete frame_index;
161         frame_index = new ArrayList<off_t>;
163         VFrame *frame = new VFrame(0, get_width(), get_height(), BC_YUV420P);
164         yuv[0] = frame->get_y();
165         yuv[1] = frame->get_u();
166         yuv[2] = frame->get_v();
168         // note the start of the first frame
169         position = lseek(stream_fd, 0, SEEK_CUR);
171         // reset the frame count
172         frame_count = 0;
174         while (read_frame(yuv) == 0) {
175                 // index is start position of each frame
176                 frame_index->append(position);
177                 position = lseek(stream_fd, 0, SEEK_CUR);
178                 frame_count++;
179         } 
181         // rewind to the start of the first frame
182         lseek(stream_fd, frame_index->values[0], SEEK_SET);
184         delete frame;
186         return 0;
189 int YUVStream::seek_frame(int64_t frame_number) {
190         if (frame_number > frame_count ||
191             frame_number < 0 ||
192             frame_index == 0) {
193                 printf("seek_frame(%d) failed (frame_count=%d)\n", 
194                        frame_number, frame_count);
195                 return 1;
196         }
198         off_t position = frame_index->values[frame_number];
199         if (position == 0) {
200                 // because of header, position should never be zero
201                 printf("seek_frame(%d): position was zero\n", frame_number);
202         }
204         if (lseek(stream_fd, position, SEEK_SET) < 0) {
205                 printf("lseek(%d) failed: %s\n", position, strerror(errno));
206                 return 1;
207         }
209         return 0;
212 int YUVStream::read_header() {
213         int result = y4m_read_stream_header(stream_fd, &stream_info);
214         if (result != Y4M_OK) {
215                 printf("y4m_read_stream_header() failed: %s\n", 
216                        y4m_strerr(result));
217                 return 1;
218         }
219         return 0;
221 int YUVStream::write_header() {
222         int result = y4m_write_stream_header(stream_fd, &stream_info);
223         if (result != Y4M_OK) {
224                 printf("y4m_write_stream_header() failed: %s\n", 
225                        y4m_strerr(result));
226                 return 1;
227         }
228         return 0;
231 int YUVStream::get_interlace() {
232         return ilace_yuv4mpeg_to_bc(y4m_si_get_interlace(&stream_info));
235 void YUVStream::set_interlace(int imode) {
236         y4m_si_set_interlace(&stream_info, ilace_bc_to_yuv4mpeg(imode));
239 int YUVStream::get_width() {
240         return y4m_si_get_width(&stream_info);
242 void YUVStream::set_width(int width) {
243         y4m_si_set_width(&stream_info, width);
246 int YUVStream::get_height() {
247         return y4m_si_get_height(&stream_info);
249 void YUVStream::set_height(int height) {
250         y4m_si_set_height(&stream_info, height);
253 double YUVStream::get_frame_rate() {
254         y4m_ratio_t ratio = y4m_si_get_framerate(&stream_info);
255         double frame_rate = (double) ratio.n / (double) ratio.d;
256         return frame_rate;
258 void YUVStream::set_frame_rate(double frame_rate) {
259         y4m_ratio_t ratio = mpeg_conform_framerate(frame_rate);
260         y4m_si_set_framerate(&stream_info, ratio);
263 // FUTURE: these seem like a mess, possibly because I don't 
264 //         properly understand "display aspect" vs "pixel aspect"
265 double YUVStream::get_aspect_ratio() {
266         y4m_ratio_t sar = y4m_si_get_sampleaspect(&stream_info);
267         mpeg_aspect_code_t code = 
268                 mpeg_guess_mpeg_aspect_code(2, sar, 
269                                             get_width(),
270                                             get_height());
271         y4m_ratio_t aspect_ratio =  mpeg_framerate(code);
272         if (aspect_ratio.d == 0) return 0;
273         return (double) aspect_ratio.n / (double) aspect_ratio.n;
274 }               
275 void YUVStream::set_aspect_ratio(double aspect_ratio) {
276         y4m_ratio_t ratio;
277         ratio.n = (int)(aspect_ratio * 10000);
278         ratio.d = 10000;
279         y4m_ratio_t sar = y4m_guess_sar(get_width(), get_height(),
280                                         ratio);
281         y4m_si_set_sampleaspect(&stream_info, sar);