Probleme mit libmad und dem Open Sound System
-
Hallo!
In einem größeren Projekt müssen u.a. MP3 Daten, die übers Netzwerk kommen, abgespielt werden. Daher setzte ich mich momentan mit libmad und dem Open Sound System auseinander (Alternativen kann ich leider nicht verwenden) und versuche zunächst einen kleinen MP3 Player zu basteln. Ich habe mich dabei an minimad.c (in libmad enthalten) und einem OSS-Tutorial orientiert. Ich kann zwar MP3s abspielen, jedoch viel zu schnell und in einer grausigen Qualität. Außerdem bekomme ich von libmad Unmengen an Decodierungs-Fehler (bad main_data_begin pointer, lost synchronization, forbidden bitrate value, etc.).
Source:
#include <cstdio> #include <cstring> #include <fcntl.h> #include <fstream> #include <iostream> #include <mad.h> #include <sys/soundcard.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> using namespace std; class oss { private: int fdi, fd; protected: /* nothing */ public: oss(int inp, int srate = 44100, int nchannels = 2) { fdi = inp; int format = AFMT_S16_NE; int rate = srate; int channels = nchannels; if((fd = open("/dev/sound/dsp", O_WRONLY)) == -1 && (fd = open("/dev/dsp", O_WRONLY)) == -1) { cout << "Opening sound sevice failed." << endl; exit(-1); } if(ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != AFMT_S16_NE) { cout << "Setting AFMT_S16_NE failed." << endl; exit(-1); } if(ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != nchannels) { cout << "Setting 2 channels failed." << endl; exit(-1); } if(ioctl(fd, SNDCTL_DSP_SPEED, &rate) == -1 || rate < srate-100 || rate > srate+100) { cout << "Setting 44100Hz failed." << endl; exit(-1); } } ~oss() { close(fd); } void run() { const int bufsize = 128*1024; char *buffer = new char[bufsize]; int bytes; while((bytes = read(fdi, buffer, bufsize)) > 0) { if(write(fd, buffer, bytes) != bytes) { perror("write"); exit(-1); } cout << bytes << endl; } if(bytes == -1) { perror("read"); exit(-1); } delete[] buffer; close(fdi); } }; class mad { private: static const int bufsize = 128*1024; struct userp_t { int fdi; int fdo; char *buffer; int bufpos; signed short int *samples; int ssize; } *userp; struct mad_decoder decoder; protected: static enum mad_flow input(void *data, struct mad_stream *stream) { if((char*)stream->next_frame) { memmove(static_cast<userp_t*>(data)->buffer, stream->next_frame, static_cast<userp_t*>(data)->buffer + bufsize - (char*)stream->next_frame); static_cast<userp_t*>(data)->bufpos = static_cast<userp_t*>(data)->buffer + bufsize - (char*)stream->next_frame; } int bytes = read(static_cast<userp_t*>(data)->fdi, static_cast<userp_t*>(data)->buffer + static_cast<userp_t*>(data)->bufpos, bufsize - static_cast<userp_t*>(data)->bufpos); if(bytes <= 0) return MAD_FLOW_STOP; mad_stream_buffer(stream, (unsigned char*)static_cast<userp_t*>(data)->buffer, bytes); return MAD_FLOW_CONTINUE; } static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame) { cerr << "decoding error 0x" << hex << stream->error << " (" << mad_stream_errorstr(stream) << ")" << endl; return MAD_FLOW_CONTINUE; } static inline signed short int scale(mad_fixed_t sample) { sample += (1L << (MAD_F_FRACBITS - 16)); if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE; return sample >> (MAD_F_FRACBITS + 1 - 16); } static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm) { unsigned int nchannels = pcm->channels; unsigned int srate = pcm->samplerate; unsigned int nsamples = pcm->length; mad_fixed_t const *left_ch = pcm->samples[0]; mad_fixed_t const *right_ch = pcm->samples[1]; if(!static_cast<userp_t*>(data)->samples) { static_cast<userp_t*>(data)->samples = new signed short int[nchannels*nsamples]; static_cast<userp_t*>(data)->ssize = nchannels*nsamples; } if(static_cast<userp_t*>(data)->ssize < nchannels*nsamples) { delete[] static_cast<userp_t*>(data)->samples; static_cast<userp_t*>(data)->samples = new signed short int[nchannels*nsamples]; static_cast<userp_t*>(data)->ssize = nchannels*nsamples; } int i = 0; int n = nsamples; while (n--) { static_cast<userp_t*>(data)->samples[++i] = scale(*left_ch++); if (nchannels == 2) static_cast<userp_t*>(data)->samples[++i] = scale(*right_ch++); } write(static_cast<userp_t*>(data)->fdo, static_cast<userp_t*>(data)->samples, nchannels*nsamples); return MAD_FLOW_CONTINUE; } public: mad(int inp, int outp) { userp = new userp_t(); if(!userp) exit(-1); userp->fdi = inp; userp->fdo = outp; userp->buffer = new char[bufsize]; if(!userp->buffer) exit(-1); userp->bufpos = 0; userp->samples = 0; userp->ssize = 0; mad_decoder_init(&decoder, userp, mad::input, 0, 0, mad::output, mad::error, 0); } ~mad() { mad_decoder_finish(&decoder); delete[] userp->buffer; delete[] userp->samples; delete userp; } void run() { mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); close(userp->fdi); close(userp->fdo); } }; void *mad_thread(void *ptr) { static_cast<mad*>(ptr)->run(); } void *oss_thread(void *ptr) { static_cast<oss*>(ptr)->run(); } int main(int argc, char **argv) { if(argc < 2) return 1; int fd = open(argv[1], O_RDWR); int fds[2]; if(pipe(fds) == -1) { perror("pipe"); exit(-1); } mad *mymad = new mad(fd, fds[1]); oss *myoss = new oss(fds[0]); pthread_t thread1, thread2; pthread_create(&thread1, NULL, mad_thread, (void*)mymad); pthread_create(&thread2, NULL, oss_thread, (void*)myoss); pthread_join(thread1, NULL); pthread_join(thread2, NULL); delete mymad; delete myoss; return 0; }
Kann mir jemand helfen?
Danke im Voraus.MfG
Fye
-
Fye schrieb:
Außerdem bekomme ich von libmad Unmengen an Decodierungs-Fehler (bad main_data_begin pointer, lost synchronization, forbidden bitrate value, etc.).
Die kommen vermutlich daher, dass du nicht darauf achtest, MAD mit kompletten MP3 Frames zu füttern.
http://www.mars.org/mailman/public/mad-dev/2003-November/000940.html
-
Danke. Das hat zumindest die Dekodierungs-Fehler beseitig. Die Sound-Ausgabe ist aber immer noch zu schnell und in sehr schlechter Qualität.
Ich habe den Quelltext in meinem ersten Beitrag aktualisiert.Edit: Jetzt hab' ich's: Zeile 172f muss folgendermaßen lauten:
write(static_cast<userp_t*>(data)->fdo, static_cast<userp_t*>(data)->samples, nchannels*nsamples*2);
Ein Sample ist schließlich 16 Bits = 2 Bytes lang.