1 /*
2 	extract_frams: utlize the framebyframe API and mpg123_framedata to extract the MPEG frames out of a stream (strip off anything else).
3 
4 	copyright 2011-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
5 	see COPYING and AUTHORS files in distribution or http://mpg123.org
6 	initially written by Thomas Orgis
7 */
8 
9 #include "config.h"
10 #include "compat.h"
11 #include <mpg123.h>
12 
13 #include "getlopt.h"
14 
15 
16 static struct
17 {
18 	int info;
19 	long icy_interval;
20 	int verbose;
21 } param =
22 {
23 	 TRUE
24 	,0
25 	,0
26 };
27 
28 static const char* progname;
29 
usage(int err)30 static void usage(int err)
31 {
32 	FILE* o = stdout;
33 	if(err)
34 	{
35 		o = stderr;
36 		fprintf(o, "You made some mistake in program usage... let me briefly remind you:\n\n");
37 	}
38 	fprintf(o, "Extract only MPEG frames from a stream using libmpg123 (stdin to stdout)\n");
39 	fprintf(o, "\tversion %s; written and copyright by Thomas Orgis and the mpg123 project\n", PACKAGE_VERSION);
40 	fprintf(o,"\nusage: %s [option(s)] < input > output\n", progname);
41 	fprintf(o,"\noptions:\n");
42 	fprintf(o," -h     --help              give usage help\n");
43 	fprintf(o," -i <n> --icy-interval <n>  stream has ICY metadata present with this interval\n");
44 	fprintf(o," -n     --no-info           also strip info frame at beginning\n");
45 	fprintf(o," -v[*]  --verbose           increase verbosity level\n");
46 	exit(err);
47 }
48 
want_usage(char * bla)49 static void want_usage(char* bla)
50 {
51 	usage(0);
52 }
53 
set_verbose(char * arg)54 static void set_verbose (char *arg)
55 {
56     param.verbose++;
57 }
58 
59 static topt opts[] =
60 {
61 	 {'h', "help", 0, want_usage, 0, 0}
62 	,{'i', "icy-interval", GLO_ARG|GLO_LONG, 0, &param.icy_interval, 0}
63 	,{'n', "no-info", GLO_INT, 0, &param.info, FALSE}
64 	,{'v', "verbose", 0, set_verbose, 0, 0}
65 	,{0, 0, 0, 0, 0, 0}
66 };
67 
68 int do_work(mpg123_handle *m);
69 
main(int argc,char ** argv)70 int main(int argc, char **argv)
71 {
72 	int ret = 0;
73 	mpg123_handle *m;
74 
75 	progname = argv[0];
76 
77 	while ((ret = getlopt(argc, argv, opts)))
78 	switch (ret) {
79 		case GLO_UNKNOWN:
80 			fprintf (stderr, "%s: Unknown option \"%s\".\n",
81 				progname, loptarg);
82 			usage(1);
83 		case GLO_NOARG:
84 			fprintf (stderr, "%s: Missing argument for option \"%s\".\n",
85 				progname, loptarg);
86 			usage(1);
87 	}
88 
89 	mpg123_init();
90 	m = mpg123_new(NULL, &ret);
91 
92 	if(m == NULL)
93 	{
94 		fprintf(stderr, "Cannot create handle: %s", mpg123_plain_strerror(ret));
95 	}
96 	else
97 	{
98 		ret = mpg123_param(m, MPG123_VERBOSE, param.verbose, 0.);
99 		if(ret == MPG123_OK)
100 		{
101 			if(param.verbose)
102 			fprintf(stderr, "Info frame handling: %s\n",
103 				param.info ? "pass-through" : "remove");
104 			ret = param.info /* If info frame is ignored, it is treated as MPEG data. */
105 				? mpg123_param(m, MPG123_ADD_FLAGS, MPG123_IGNORE_INFOFRAME, 0.)
106 				: mpg123_param(m, MPG123_REMOVE_FLAGS, MPG123_IGNORE_INFOFRAME, 0.);
107 		}
108 		if(ret == MPG123_OK && param.icy_interval > 0)
109 		{
110 			if(param.verbose) fprintf(stderr, "ICY interval: %li\n", param.icy_interval);
111 			ret = mpg123_param(m, MPG123_ICY_INTERVAL, param.icy_interval, 0);
112 		}
113 
114 		if(ret == MPG123_OK) ret = do_work(m);
115 
116 		if(ret != MPG123_OK) fprintf(stderr, "Some error occured: %s\n", mpg123_strerror(m));
117 
118 		mpg123_delete(m); /* Closes, too. */
119 	}
120 	mpg123_exit();
121 
122 	return ret;
123 }
124 
do_work(mpg123_handle * m)125 int do_work(mpg123_handle *m)
126 {
127 	int ret;
128 	size_t count = 0;
129 	ret = mpg123_open_fd(m, STDIN_FILENO);
130 	if(ret != MPG123_OK) return ret;
131 
132 	while( (ret = mpg123_framebyframe_next(m)) == MPG123_OK || ret == MPG123_NEW_FORMAT )
133 	{
134 		unsigned long header;
135 		unsigned char *bodydata;
136 		size_t bodybytes;
137 		if(mpg123_framedata(m, &header, &bodydata, &bodybytes) == MPG123_OK)
138 		{
139 			/* Need to extract the 4 header bytes from the native storage in the correct order. */
140 			unsigned char hbuf[4];
141 			int i;
142 			for(i=0; i<4; ++i) hbuf[i] = (unsigned char) ((header >> ((3-i)*8)) & 0xff);
143 
144 			/* Now write out both header and data, fire and forget. */
145 			write(STDOUT_FILENO, hbuf, 4);
146 			write(STDOUT_FILENO, bodydata, bodybytes);
147 			if(param.verbose)
148 			fprintf(stderr, "%"SIZE_P": header 0x%08lx, %"SIZE_P" body bytes\n"
149 			, (size_p)++count, header, (size_p)bodybytes);
150 		}
151 	}
152 
153 	if(ret != MPG123_DONE)
154 	fprintf(stderr, "Some error occured (non-fatal?): %s\n", mpg123_strerror(m));
155 
156 	if(param.verbose) fprintf(stderr, "Done with %"SIZE_P" MPEG frames.\n"
157 	, (size_p)count);
158 
159 	return MPG123_OK;
160 }
161