camogm_jpeg.c 8.19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/** @file camogm_jpeg.c
 * @brief Provides writing to series of individual JPEG files for camogm
 * @copyright Copyright (C) 2016 Elphel, Inc.
 *
 * @par <b>License</b>
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Mikhail Karpenko's avatar
Mikhail Karpenko committed
16
 */
17

18
/** @brief This define is needed to use lseek64 and should be set before includes */
19
#define _LARGEFILE64_SOURCE
20

21 22 23 24 25 26
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <errno.h>
27
#include <sys/stat.h>
28 29 30 31
#include <sys/types.h>
#include <string.h>

#include "camogm_jpeg.h"
32

33
int camogm_init_jpeg(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
34 35
{
	return 0;
36
}
37

Mikhail Karpenko's avatar
Mikhail Karpenko committed
38 39
void camogm_free_jpeg(void)
{
40 41
}

42 43 44 45 46 47 48 49
/**
 * @brief Called every time the JPEG files recording is started.
 *
 * This function checks if the raw device write is initiated and tries to open the device specified. The device
 * will be closed in #camogm_end_jpeg function.
 * @param[in]   state   a pointer to a structure containing current state
 * @return      0 if the device was opened successfully and negative error code otherwise
 */
50
int camogm_start_jpeg(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
51 52 53 54
{
	char * slash;
	int rslt;

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
	if (!state->rawdev_op) {
		strcpy(state->path, state->path_prefix); //!make state->path a directory name (will be replaced when the frames will be written)
		slash = strrchr(state->path, '/');
		D2(fprintf(debug_file, "camogm_start_jpeg\n"));
		if (slash) {
			D3(fprintf(debug_file, "Full path %s\n", state->path));
			slash[0] = '\0'; //! truncate path to the directory name
			D3(fprintf(debug_file, "directory path %s\n", state->path));
			rslt = mkdir(state->path, 0777);
			D3(fprintf(debug_file, "mkdir (%s, 0777) returned %d, errno=%d\n", state->path, rslt, errno));
			if ((rslt < 0) && (errno != EEXIST)) { // already exists is OK
				D0(fprintf(debug_file, "Error creating directory %s, errno=%d\n", state->path, errno));
				return -CAMOGM_FRAME_FILE_ERR;
			}
		}
	} else {
71 72 73 74 75 76 77 78
		if (state->rawdev_op) {
			state->rawdev.rawdev_fd = open(state->rawdev.rawdev_path, O_RDWR);
			if (state->rawdev.rawdev_fd < 0) {
				D0(perror(__func__));
				D0(fprintf(debug_file, "Error opening raw device %s\n", state->rawdev.rawdev_path));
				return -CAMOGM_FRAME_FILE_ERR;
			}
			D3(fprintf(debug_file, "Open raw device %s; start_pos = %llu, end_pos = %llu, curr_pos = %llu\n", state->rawdev.rawdev_path,
79 80
					state->rawdev.start_pos, state->rawdev.end_pos, state->rawdev.curr_pos_w));
			lseek64(state->rawdev.rawdev_fd, state->rawdev.curr_pos_w, SEEK_SET);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
81 82
		}
	}
83

Mikhail Karpenko's avatar
Mikhail Karpenko committed
84
	return 0;
85
}
86

87 88 89 90 91 92 93
/**
 * @brief Write single JPEG frame
 *
 * This function will write single JPEG file
 * @param   state   a pointer to a structure containing current state
 * @return
 */
94
int camogm_frame_jpeg(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
95
{
96
	int i, j, k, split_index;
97
	int chunks_used = state->chunk_index - 1;
98
	ssize_t iovlen, l = 0;
99 100
	struct iovec chunks_iovec[8];
	unsigned char *split_ptr = NULL;
101
	long split_cntr = 0;
102
	int port = state->port_num;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
103

104 105 106 107 108 109 110
	if (!state->rawdev_op) {
		l = 0;
		for (i = 0; i < (state->chunk_index) - 1; i++) {
			chunks_iovec[i].iov_base = state->packetchunks[i + 1].chunk;
			chunks_iovec[i].iov_len = state->packetchunks[i + 1].bytes;
			l += chunks_iovec[i].iov_len;
		}
111
		sprintf(state->path, "%s%010ld_%06ld.jpeg", state->path_prefix, state->this_frame_params[port].timestamp_sec, state->this_frame_params[port].timestamp_usec);
112 113 114 115 116 117 118 119 120 121 122 123 124 125
		if (((state->ivf = open(state->path, O_RDWR | O_CREAT, 0777))) < 0) {
			D0(fprintf(debug_file, "Error opening %s for writing, returned %d, errno=%d\n", state->path, state->ivf, errno));
			return -CAMOGM_FRAME_FILE_ERR;
		}
		iovlen = writev(state->ivf, chunks_iovec, (state->chunk_index) - 1);
		if (iovlen < l) {
			j = errno;
			D0(fprintf(debug_file, "writev error %d (returned %d, expected %d)\n", j, iovlen, l));
			close(state->ivf);
			return -CAMOGM_FRAME_FILE_ERR;
		}
		close(state->ivf);
	} else {
		D0(fprintf(debug_file, "\n%s: current pointers start_pos = %llu, end_pos = %llu, curr_pos = %llu, data in buffer %d\n", __func__,
126
				state->rawdev.start_pos, state->rawdev.end_pos, state->rawdev.curr_pos_w, l));
127 128 129
		split_index = -1;
		for (int i = 0, total_len = 0; i < state->chunk_index - 1; i++) {
			total_len += state->packetchunks[i + 1].bytes;
130
			if (total_len + state->rawdev.curr_pos_w > state->rawdev.end_pos) {
131 132 133 134 135 136
				split_index = i;
				chunks_used++;
				D0(fprintf(debug_file, "\n>>> raw storage roll over detected\n"));
				break;
			}
		}
137 138 139 140
		k = 0;
		l = 0;
		for (int i = 0; i < chunks_used; i++) {
			++k;
141 142 143
			if (i == split_index) {
				// one of the chunks rolls over the end of the raw storage, split it into two segments and
				// use additional chunk in chunks_iovec for this additional segment
144
				split_cntr = state->rawdev.end_pos - (l + state->rawdev.curr_pos_w);
145 146
				split_ptr = state->packetchunks[k].chunk + split_cntr;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
147 148
				D3(fprintf(debug_file, "Splitting chunk #%d: total chunk size = %ld, start address = 0x%p\n",
						i, state->packetchunks[k].bytes, state->packetchunks[k].chunk));
149

150
				// be careful with indexes here
151
				chunks_iovec[i].iov_base = state->packetchunks[k].chunk;
152 153 154
				chunks_iovec[i].iov_len = split_cntr;
				l += chunks_iovec[i].iov_len;
				chunks_iovec[++i].iov_base = split_ptr + 1;
155
				chunks_iovec[i].iov_len = state->packetchunks[k].bytes - split_cntr;
156 157
				l += chunks_iovec[i].iov_len;
			} else {
158 159
				chunks_iovec[i].iov_base = state->packetchunks[k].chunk;
				chunks_iovec[i].iov_len = state->packetchunks[k].bytes;
160 161 162
				l += chunks_iovec[i].iov_len;
			}
		}
163

164 165 166 167 168 169 170 171
		/* debug code follows */
		fprintf(debug_file, "\n=== raw device write, iovec dump ===\n");
		fprintf(debug_file, "split_cntr = %ld; split_ptr = %p; split_index = %d\n", split_cntr, split_ptr, split_index);
		for (int i = 0; i < chunks_used; i++) {
			fprintf(debug_file, "i = %d; iov_base = %p; iov_len = %u\n", i, chunks_iovec[i].iov_base, chunks_iovec[i].iov_len);
		}
		fprintf(debug_file, "total len = %d\n======\n", l);
		/* end of debug code */
172

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
		if (split_index < 0) {
			iovlen = writev(state->rawdev.rawdev_fd, chunks_iovec, chunks_used);
		} else {
			iovlen = writev(state->rawdev.rawdev_fd, chunks_iovec, split_index + 1);
			fprintf(debug_file, "write first part: split_index = %d, %d bytes written\n", split_index, iovlen);
			if (lseek64(state->rawdev.rawdev_fd, state->rawdev.start_pos, SEEK_SET) != state->rawdev.start_pos) {
				perror(__func__);
				D0(fprintf(debug_file, "error positioning file pointer to the beginning of raw device\n"));
				return -CAMOGM_FRAME_FILE_ERR;
			}
			state->rawdev.overrun++;
			iovlen += writev(state->rawdev.rawdev_fd, &chunks_iovec[split_index + 1], chunks_used - split_index);
			fprintf(debug_file, "write second part: split_index + 1 = %d, chunks_used - split_index = %d, %d bytes written in total\n",
					split_index + 1, chunks_used - split_index, iovlen);
		}
188 189
		if (iovlen < l) {
			j = errno;
190
			perror(__func__);
191 192 193
			D0(fprintf(debug_file, "writev error %d (returned %d, expected %d)\n", j, iovlen, l));
			return -CAMOGM_FRAME_FILE_ERR;
		}
194 195 196 197
		state->rawdev.curr_pos_w += l;
		if (state->rawdev.curr_pos_w > state->rawdev.end_pos)
			state->rawdev.curr_pos_w = state->rawdev.curr_pos_w - state->rawdev.end_pos + state->rawdev.start_pos;
		D0(fprintf(debug_file, "%d bytes written, curr_pos = %llu\n", l, state->rawdev.curr_pos_w));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
198
	}
199

Mikhail Karpenko's avatar
Mikhail Karpenko committed
200
	return 0;
201 202
}

203 204 205 206 207 208 209
/**
 * @brief Finish JPEG file write operation
 *
 * This function checks whether raw device write was on and closes raw device file.
 * @param   state   a pointer to a structure containing current state
 * @return  0 if the device was closed successfully and -1 otherwise
 */
210
int camogm_end_jpeg(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
211
{
212 213 214 215 216 217
	int ret = 0;
	if (state->rawdev_op) {
		ret = close(state->rawdev.rawdev_fd);
		D0(fprintf(debug_file, "Closing raw device %s\n", state->rawdev.rawdev_path));
	}
	return ret;
218
}