Skip to contentSkip to conversation

crec: Initial version of a compressed capture utility

PATCH Superseded Charles Keepax 3 participants 8 messages Dec 3, 2013 – Dec 11, 2013

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/2 crec: Initial version of a compressed capture utility

This version only supports capture of PCM streams over a compressed device and saves these as WAV files.

Change-Id I79b0f22598b3ea3b8e99865804799dd013e9d02b

---

Changes since v1:
- Use memset rather than manually clear snd_codec fields
- Change fopen to "wb" rather than "w+b"

I haven't changed the control switch letters, but if there are strong feelings on which letter to use I am happy to update this.

Thanks,
Charles

 Android.mk     |   10 ++
 crec.c         |  417 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 makefile.linux |   20 ++-
 3 files changed, 440 insertions(+), 7 deletions(-)
 create mode 100644 crec.c
Android.mk +10 -0
index 104dfef..3cdd3d0 100644
@@ -20,3 +20,13 @@ LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include
+LOCAL_SRC_FILES:= crec.c
+LOCAL_MODULE := crec
+LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinycompress
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
+
crec.c new file +417 -0

417 lines not on this page · raw message

makefile.linux +13 -7
index 677878d..81782d0 100644
@@ -1,10 +1,13 @@
 LIB = libtinycompress
-BIN = cplay
 VER = 0.1
 LIBSRC = compress.c
-SRC = cplay.c utils.c
+PLAYBIN = cplay
+RECBIN = crec
+PLAYSRC = cplay.c utils.c
+RECSRC = crec.c utils.c
 LIBOBJ = ${LIBSRC:.c=.o}
-OBJ = ${SRC:.c=.o}
+PLAYOBJ = ${PLAYSRC:.c=.o}
+RECOBJ = ${RECSRC:.c=.o}
 
 CC = gcc
 CROSS_COMPILE =
@@ -13,23 +16,26 @@ CFLAGS += -std=c99 -Wall -Wextra -Wunused -DVERSION=\"${VER}\" -I./include
 LDFLAGS += -L. -ltinycompress
 LIBLDFLAGS = -lasound
 
-all: libtinycompress cplay
+all: libtinycompress cplay crec
 
 $(LIB): ${LIBOBJ}
 	@echo "  LD	"$@
 	@${CROSS_COMPILE}${CC} ${CFLAGS} -shared -Wl,-soname,[email protected] -o [email protected] ${LIBOBJ} ${LIBLDFLAGS}
 
-$(BIN): ${OBJ} ${LIB}
+$(PLAYBIN): ${PLAYOBJ} ${LIB}
 	@echo "  LD	"$@
-	@${CROSS_COMPILE}${CC} ${CFLAGS} -o $@ ${OBJ} ${LDFLAGS}
+	@${CROSS_COMPILE}${CC} ${CFLAGS} -o $@ ${PLAYOBJ} ${LDFLAGS}
 
+$(RECBIN): ${RECOBJ} ${LIB}
+	@echo "  LD	"$@
+	@${CROSS_COMPILE}${CC} ${CFLAGS} -o $@ ${RECOBJ} ${LDFLAGS}
 
 %.o: %.c
 	@echo "  CC	"$<
 	@${CROSS_COMPILE}${CC} ${CFLAGS} -c -fPIC -o $@ $<
 
 clean:
-	@rm -rf ${BIN} ${OBJ} ${LIB}.so ${LIBOBJ}
+	@rm -rf ${BIN} ${PLAYOBJ} ${RECOBJ} ${LIB}.so ${LIBOBJ}
 
 
 .PHONY: all clean
Signature · 1 line
1.7.2.5
#1 lore raw

PATCH v2 2/2 crec: Add primitive exception handling

Add very primitive signal handling, we will not attempt to drain any remaining data etc. simply save out what we have to a file.

Change-Id I01aee72d0ce9445e9dbe0f7ea42a8752ba71ccbd

---

Changes since v1:
- Set fopen parameter to "w+b", this was originally in the last patch but makes more sense here as this patch is the one that requires it, so that it can read back the header from the file on close.

Thanks,
Charles

 crec.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 78 insertions(+), 14 deletions(-)
crec.c +78 -14
index 2976df3..b761f7c 100644
@@ -74,6 +74,7 @@
 #include "tinycompress/tinycompress.h"
 
 static int verbose;
+static FILE *file;
 
 static const unsigned int DEFAULT_CHANNELS = 1;
 static const unsigned int DEFAULT_RATE = 44100;
@@ -183,6 +184,62 @@ static int print_time(struct compress *compress)
 	return 0;
 }
 
+static int finish_record()
+{
+	struct wave_header header;
+	int ret;
+	size_t read, written;
+
+	if (!file)
+		return -ENOENT;
+
+	/* Get amount of data written to file */
+	ret = fseek(file, 0, SEEK_END);
+	if (ret < 0)
+		goto seek_error;
+	ret = ftell(file);
+	if (ret < 0) {
+		fprintf(stderr, "Error reading file position: %s\n",
+			strerror(errno));
+		return errno;
+	}
+	written = ret;
+	if (written < sizeof(header)) {
+		fprintf(stderr, "No data recorded!\n");
+		return -ENOENT;
+	}
+	written -= sizeof(header);
+
+	/* Sync file header from file */
+	ret = fseek(file, 0, SEEK_SET);
+	if (ret < 0)
+		goto seek_error;
+	read = fread(&header, sizeof(header), 1, file);
+	if (read != 1) {
+		ret = ferror(file);
+		fprintf(stderr, "Error reading output file header: %d\n", ret);
+		return ret;
+	}
+
+	/* Update file header */
+	ret = fseek(file, 0, SEEK_SET);
+	if (ret < 0)
+		goto seek_error;
+	size_wave_header(&header, written);
+	written = fwrite(&header, sizeof(header), 1, file);
+	if (written != 1) {
+		ret = ferror(file);
+		fprintf(stderr, "Error updating output file header: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+
+seek_error:
+	fprintf(stderr, "Error seeking: %s\n", strerror(errno));
+	return errno;
+}
+
 void capture_samples(char *name, unsigned int card, unsigned int device,
 		     unsigned long buffer_size, unsigned int frag,
 		     unsigned int length, unsigned int rate,
@@ -192,7 +249,6 @@ void capture_samples(char *name, unsigned int card, unsigned int device,
 	struct snd_codec codec;
 	struct compress *compress;
 	struct wave_header header;
-	FILE *file;
 	char *buffer;
 	size_t written;
 	int read, ret;
@@ -214,7 +270,7 @@ void capture_samples(char *name, unsigned int card, unsigned int device,
 	if (verbose)
 		printf("%s: entry, reading %u bytes\n", __func__, length);
 
-	file = fopen(name, "wb");
+	file = fopen(name, "w+b");
 	if (!file) {
 		fprintf(stderr, "Unable to open file '%s'\n", name);
 		exit(EXIT_FAILURE);
@@ -310,25 +366,16 @@ void capture_samples(char *name, unsigned int card, unsigned int device,
 		fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
 	}
 
-	/* Update file header now we know file size */
-	size_wave_header(&header, total_read);
-	ret = fseek(file, 0, SEEK_SET);
-	if (ret < 0) {
-		fprintf(stderr, "Error seeking: %s\n", stderror(errno));
+	ret = finish_record();
+	if (ret < 0)
 		goto buf_exit;
-	}
-	written = fwrite(&header, sizeof(header), 1, file);
-	if (written != 1) {
-		fprintf(stderr, "Error updating output file header: %d\n",
-			ferror(file));
-		goto buf_exit;
-	}
 
 	if (verbose)
 		printf("%s: exit success\n", __func__);
 
 	free(buffer);
 	fclose(file);
+	file = NULL;
 
 	compress_close(compress);
 
@@ -346,6 +393,18 @@ file_exit:
 	exit(EXIT_FAILURE);
 }
 
+static void sig_handler(int signum __attribute__ ((unused)))
+{
+	printf("Interrupted, saving what we have!\n");
+
+	finish_record();
+
+	if (file)
+		fclose(file);
+
+	_exit(EXIT_FAILURE);
+}
+
 int main(int argc, char **argv)
 {
 	char *file;
@@ -355,6 +414,11 @@ int main(int argc, char **argv)
 	unsigned int rate = DEFAULT_RATE, channels = DEFAULT_CHANNELS;
 	unsigned int format = DEFAULT_FORMAT;
 
+	if (signal(SIGINT, sig_handler) == SIG_ERR) {
+		fprintf(stderr, "Error registering signal handler\n");
+		exit(EXIT_FAILURE);
+	}
+
 	if (argc < 2)
 		usage();
 
Signature · 1 line
1.7.2.5
#2 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

crec.c file_exit: · line 406

Quoted 11 lines · Charles Keepax
crec.c +11 -0
+static void sig_handler(int signum __attribute__ ((unused)))
+{
+	printf("Interrupted, saving what we have!\n");
+
+	finish_record();
+
+	if (file)
+		fclose(file);
+
+	_exit(EXIT_FAILURE);
+}

You can't safely do most of this such as calling fclose() from within a signal handler, the set of functions that are guaranteed safe is quite limited - see signal(7).

#3 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

Quoted 3 lines · Mark Brown

You can't safely do most of this such as calling fclose() from within a signal handler, the set of functions that are guaranteed safe is quite limited - see signal(7).

I should also have mentioned that it looks like you're probably looking for setvbuf() with _IONBF.

#4 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

Quoted 8 lines · Mark Brown
Quoted 3 lines · Mark Brown

You can't safely do most of this such as calling fclose() from within a signal handler, the set of functions that are guaranteed safe is quite limited - see signal(7).

I should also have mentioned that it looks like you're probably looking for setvbuf() with _IONBF.

All good points, it is probably worth just merging the first patch in the series for now and I can have a more detailed look at the second patch.

Tricky thing is making 100% sure we don't enter a poll after we have received the signal, especially if we are just using the standard pattern of communicating the signal using a sig_atomic_t.

Thanks,
Charles

#5 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

Quoted 4 lines · Charles Keepax

Tricky thing is making 100% sure we don't enter a poll after we have received the signal, especially if we are just using the standard pattern of communicating the signal using a sig_atomic_t.

If you don't buffer and use Unix I/O rather than stdio for any I/O in the handler you can exit happily from the handler IIRC, the big problem is stdio.

#6 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

Quoted 10 lines · Mark Brown
Quoted 4 lines · Charles Keepax

Tricky thing is making 100% sure we don't enter a poll after we have received the signal, especially if we are just using the standard pattern of communicating the signal using a sig_atomic_t.

If you don't buffer and use Unix I/O rather than stdio for any I/O in the handler you can exit happily from the handler IIRC, the big problem is stdio.

Cool, I will have a look at this in more detail and respin the series.

Thanks,
Charles

#7 lore raw

Re: [PATCH 2/2 v2] crec: Add primitive exception handling

In reply to Mark Brown
Quoted 17 lines · Mark Brown

crec.c file_exit: · line 406

Quoted 11 lines · Charles Keepax
crec.c +11 -0
+static void sig_handler(int signum __attribute__ ((unused)))
+{
+	printf("Interrupted, saving what we have!\n");
+
+	finish_record();
+
+	if (file)
+		fclose(file);
+
+	_exit(EXIT_FAILURE);
+}

You can't safely do most of this such as calling fclose() from within a signal handler, the set of functions that are guaranteed safe is quite limited - see signal(7).

A cool thing would be to use signalfd(2) to serialize the handling of the signal and getting rid of the traditional signal handler.

Quoted 1 line

From a quick look at bionic it doesn't seem to define a syscall wrapper for that

so if one really wanted to use it, it would be through syscall(2) but that's a bit ugly for this purpose.

Thanks,
Dimitris