summaryrefslogtreecommitdiff
path: root/transcode.c
blob: 636f8d738c5d96fb1a3e1ac125fe7c6190f2a17a (plain)
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
#include "transcode.h"
#include "resample.h"

#include <glib.h>

void transcode(GInputStream *input, const struct decoder_plugin *_decoder,
		GOutputStream *output, const struct encoder_plugin *_encoder) {
	GError *error = NULL;

	gboolean decode_done = FALSE, encode_done = FALSE;
	struct decoder *decoder = g_new0(struct decoder, 1);
	struct encoder *encoder = g_new0(struct encoder, 1);
	decoder->decoder = _decoder;
	encoder->encoder = _encoder;
	decoder_init(decoder);
	encoder_init(encoder);

	/* holds decoded audio passed to encoder */
	GInputStream *decoded_in = g_memory_input_stream_new();

	SRC_STATE *state = NULL;

	while(decode_done == FALSE || encode_done == FALSE) {
		/* holds decoded audio from decoder */
		GOutputStream *decoded_out = g_memory_output_stream_new(NULL, 0, g_realloc, g_free);

		if(decode_done == FALSE && decoder_decode(decoder, input, decoded_out, &error) <= 0 && error != NULL) {
			if(error->code == DECODER_CODE_DONE) {
				decode_done = TRUE;
			} else {
				g_warning(error->message);
				g_object_unref(decoded_out);
				break;
			}
			g_error_free(error);
			error = NULL;
		}

		/* temp variables */
		gpointer data = g_memory_output_stream_get_data((GMemoryOutputStream*)decoded_out);
		gsize size = g_memory_output_stream_get_data_size((GMemoryOutputStream*)decoded_out);

		if(size > 0) {
			/* TODO: allow custom sample rate */
			if(decoder->rate != 44100) {
				if(state == NULL) {
					state = resample_init(decoder->channels, &error);
					/* state should be NULL in case of errors */
					if(error) {
						g_warning(error->message);
						break;
					}
				}
				gpointer out_data;
				gsize out_size;
				resample(state, decoder->rate, 44100, decoder->channels, data, size, &out_data, &out_size, decode_done, &error);
				if(error) {
					g_warning(error->message);
					g_error_free(error);
					error = NULL;
				}
				/* data is owned by decoded_out, don't free */
				data = out_data;
				size = out_size;
			} else {
				data = g_memdup(data, size);
			}
			g_memory_input_stream_add_data((GMemoryInputStream*)decoded_in, data, size, g_free);
		}

		g_object_unref(decoded_out);

		if(encode_done == FALSE && encoder_encode(encoder, decoded_in, output, &error) <= 0 && error != NULL) {
			if(error->code == ENCODER_CODE_DONE) {
				/* assume the encoder doesn't have enough data to continue
				 * if the decoder isn't done yet
				 */
				encode_done = decode_done;
				if(encode_done == TRUE) {
					encoder_flush(encoder, output, &error);
				}
			} else {
				g_warning(error->message);
				break;
			}
			g_error_free(error);
			error = NULL;
		}
	}

	g_debug("transcoding done");

	/* error cleanup */
	if(error) {
		g_error_free(error);
	}

	/* resample cleanup */
	if(state) {
		resample_free(state);
	}

	g_object_unref(decoded_in);

	decoder_close(decoder);
	encoder_close(encoder);
	g_free(decoder);
	g_free(encoder);
}