aboutsummaryrefslogtreecommitdiffstats
path: root/src/blind-rewrite-head.c
blob: 9111485dbb1cc2cb5b4c2873cb1499cf1b4c0203 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* See LICENSE file for copyright and license details. */
#include "stream.h"
#include "util.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>

USAGE("[-h] file [(frames | 'auto') [(width | 'same') (height | 'same') [format | 'same']]]")

static void
rewrite(struct stream *stream, int frames_auto)
{
	char head[STREAM_HEAD_MAX];
	ssize_t headlen;
	size_t frame_size, frame_count, length;
	struct stat st;
	char *data;

	echeck_frame_size(stream->width, stream->height, stream->pixel_size, 0, stream->file);
	frame_size = stream->width * stream->height * stream->pixel_size;

	if (fstat(stream->fd, &st))
		eprintf("fstat %s:", stream->file);
	if (!S_ISREG(st.st_mode))
		eprintf("%s: not a regular file\n", stream->file);

	frame_count = (size_t)(st.st_size) / frame_size;
	if (frame_count * frame_size != (size_t)(st.st_size))
		eprintf("%s: given the select width and height, "
			"the file has an incomplete frame\n", stream->file);
	if (frames_auto)
		stream->frames = frame_count;
	else if (stream->frames != frame_count)
		eprintf("%s: frame count mismatch\n", stream->file);

	SPRINTF_HEAD_ZN(head, stream->frames, stream->width, stream->height, stream->pixfmt, &headlen);

	length = stream->frames * frame_size;
	if (length > (size_t)SSIZE_MAX || (size_t)headlen > (size_t)SSIZE_MAX - length)
		eprintf("%s: video is too long\n", stream->file);

	if ((size_t)headlen > stream->headlen)
		if (ftruncate(stream->fd, length + headlen))
			eprintf("ftruncate %s:", stream->file);

	data = mmap(0, length + (size_t)headlen, PROT_READ | PROT_WRITE, MAP_SHARED, stream->fd, 0);
	if (data == MAP_FAILED)
		eprintf("mmap %s:", stream->file);
	if ((size_t)headlen != stream->headlen)
		memmove(data + headlen, data + stream->headlen, length);
	memcpy(data, head, (size_t)headlen);
	munmap(data, length + (size_t)headlen);

	if ((size_t)headlen < stream->headlen)
		if (ftruncate(stream->fd, length + headlen))
			eprintf("ftruncate %s:", stream->file);
}

int
main(int argc, char *argv[])
{
	struct stream stream;
	int headless = 0, frames_auto = 0;

	ARGBEGIN {
	case 'h':
		headless = 1;
		break;
	default:
		usage();
	} ARGEND;


	if (headless) {
		if (argc != 5)
			eprintf("all positional arguments are mandatory unless -h is used\n");
	} else if (argc != 1 && argc != 2 && argc != 4 && argc != 5) {
		usage();
	}


	memset(&stream, 0, sizeof(stream));
	stream.file = argv[0];
	stream.fd = eopen(stream.file, O_RDWR);
	if (!headless)
		einit_stream(&stream);


	if (argc < 2 || !strcmp(argv[1], "auto"))
		frames_auto = 1;
	else
		stream.frames = etozu_arg("the frame count", argv[1], 0, SIZE_MAX);

	if (argc < 4);
	else if (strcmp(argv[2], "same"))
		stream.width = etozu_arg("the width",  argv[2], 1, SIZE_MAX);
	else if (headless)
		eprintf("cannot use both 'same' and -h\n");

	if (argc < 4);
	else if (strcmp(argv[3], "same"))
		stream.height = etozu_arg("the height", argv[3], 1, SIZE_MAX);
	else if (headless)
		eprintf("cannot use both 'same' and -h\n");

	if (argc < 5);
	else if (strcmp(argv[4], "same")) {
		if (strlen(argv[4]) >= sizeof(stream.pixfmt))
			eprintf("choosen pixel format is unsupported\n");
		strcpy(stream.pixfmt, argv[5]);
		if (set_pixel_size(&stream))
			eprintf("choosen pixel format is unsupported\n");
	} else if (headless) {
		eprintf("cannot use both 'same' and -h\n");
	} else if (argc > 1) {
		usage();
	}


	rewrite(&stream, frames_auto);
	close(stream.fd);
	return 0;
}