Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/soundsourceffmpeg.cpp

Go to the documentation of this file.
00001 /* -*- mode:C++; indent-tabs-mode:t; tab-width:8; c-basic-offset:4; -*- */
00002 /***************************************************************************
00003                           soundsourceffmpeg.cpp -  ffmpeg decoder
00004                              -------------------
00005     copyright            : (C) 2007 by Cedric GESTES
00006     email                : ctaf42@gmail.com
00007 ***************************************************************************/
00008 
00009 /***************************************************************************
00010 *                                                                         *
00011 *   This program is free software; you can redistribute it and/or modify  *
00012 *   it under the terms of the GNU General Public License as published by  *
00013 *   the Free Software Foundation; either version 2 of the License, or     *
00014 *   (at your option) any later version.                                   *
00015 *                                                                         *
00016 ***************************************************************************/
00017 
00018 #include "trackinfoobject.h"
00019 #include "soundsourceffmpeg.h"
00020 //#ifdef __WINDOWS__
00021 //#include <io.h>
00022 //#include <fcntl.h>
00023 //#endif
00024 
00025 // GED's notes - 2007-09-09
00026 // sudo aptitude install libavcodec-dev libavformat-dev liba52-dev libdts-dev
00027 // add '#include <QDebug> to fix qDebug' << "stuff" syntax.
00028 #include <QDebug>
00029 
00030 static QMutex ffmpegmutex;
00031 static bool ffmpeginit = false;
00032 
00033 /* TODO
00034  * - seek doesnt work correctly (seek is not precise for mp3)
00035  * - remove lock except for av_open/av_close
00036  * - do something like in soundbuffermp3 (keep a list of all frame for seeking)
00037  * - make ffmpeginit a singleton
00038  * DTS is not always updated, (maybe only after an av_seek_frame)
00039  * - remove qdebug (it slow down mixxx a lot)
00040  */
00041 
00042 static void FFmpegInit()
00043 {
00044     if (!ffmpeginit) {
00045         qDebug() << "Initialising avcodec/avformat";
00046         av_register_all();
00047         ffmpeginit = true;
00048     }
00049 }
00050 
00051 //TODO: handle error
00052 SoundSourceFFmpeg::SoundSourceFFmpeg(QString qFilename) : SoundSource(qFilename)
00053 {
00054     AVFormatParameters param;
00055     int i;
00056     QByteArray fname;
00057 
00058     packet.data = NULL;
00059     bufferOffset = 0;
00060     bufferSize = 0;
00061     memset(buffer, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE);
00062     fname = qFilename.toLatin1();
00063     FFmpegInit();
00064 
00065     qDebug() << "New SoundSourceFFmpeg :" << fname;
00066 
00067     /* initialize param to something so av_open_input_file works for raw */
00068     memset(&param, 0, sizeof(AVFormatParameters));
00069     param.channels = 2;
00070     param.sample_rate = 44100;
00071 
00072     iformat = av_find_input_format(fname.constData());
00073     // Open audio file
00074     if(av_open_input_file(&pFormatCtx, fname.constData(), iformat, 0, &param)!=0) {
00075         qDebug() << "av_open_input_file: cannot open" << fname;
00076         return;
00077     }
00078 
00079     // Retrieve stream information
00080     if(av_find_stream_info(pFormatCtx)<0) {
00081         qDebug() << "av_find_stream_info: cannot open" << fname;
00082         return;
00083     }
00084     //debug only
00085     dump_format(pFormatCtx, 0, fname.constData(), false);
00086 
00087     qDebug() << "ffmpeg: using the first audio stream available";
00088     // Find the first video stream
00089     audioStream=-1;
00090     for(i=0; i<pFormatCtx->nb_streams; i++)
00091         if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
00092             audioStream=i;
00093             break;
00094         }
00095     if(audioStream==-1) {
00096         qDebug() << "cannot find an audio stream: cannot open" << fname;
00097         return;
00098     }
00099 
00100     // Get a pointer to the codec context for the video stream
00101     pCodecCtx=pFormatCtx->streams[audioStream]->codec;
00102 
00103     // Find the decoder for the audio stream
00104     if(!(pCodec=avcodec_find_decoder(pCodecCtx->codec_id))) {
00105         qDebug() << "cannot find a decoder for" << fname;
00106         return;
00107     }
00108 
00109     qDebug() << "ffmpeg: opening the audio codec";
00110     //avcodec_open is not thread safe
00111     lock();
00112     if(avcodec_open(pCodecCtx, pCodec)<0) {
00113         qDebug() << "avcodec: cannot open" << fname;
00114         return;
00115     }
00116     unlock();
00117 
00118     pFrame=avcodec_alloc_frame();
00119     channels = pCodecCtx->channels;
00120     SRATE = pCodecCtx->sample_rate;
00121 
00122     qDebug() << "Samplerate: " << SRATE << ", Channels: " << channels << "\n";
00123     if(channels > 2){
00124         qDebug() << "ffmpeg: No support for more than 2 channels!";
00125         return;
00126     }
00127     filelength = (long int) ((double)pFormatCtx->duration * 2 / AV_TIME_BASE * SRATE);
00128 
00129     qDebug() << "ffmpeg: filelength: " << filelength << "d -|- duration: " << pFormatCtx->duration << "ld -- starttime: " << pFormatCtx->streams[audioStream]->start_time << "ld -- " << AV_TIME_BASE << " " << pFormatCtx->streams[audioStream]->codec_info_duration << "ld";
00130 }
00131 
00132 SoundSourceFFmpeg::~SoundSourceFFmpeg()
00133 {
00134     av_free(pFrame);
00135     // Close the codec
00136     lock();
00137     avcodec_close(pCodecCtx);
00138     unlock();
00139     // Close the video file
00140     av_close_input_file(pFormatCtx);
00141 };
00142 
00143 void SoundSourceFFmpeg::lock()
00144 {
00145     //  qDebug() << "ffmpeg: Before lock";
00146     ffmpegmutex.lock();
00147     //qDebug() << "ffmpeg: After lock";
00148 }
00149 
00150 void SoundSourceFFmpeg::unlock()
00151 {
00152     //qDebug() << "ffmpeg: Before unlock";
00153     ffmpegmutex.unlock();
00154     //qDebug() << "ffmpeg: After unlock";
00155 }
00156 
00157 long SoundSourceFFmpeg::ffmpeg2mixxx(long pos, const AVRational &time_base) {
00158     return (long)((double)pos / (double)time_base.den * (double)SRATE * (double)2.);
00159 }
00160 
00161 long SoundSourceFFmpeg::mixxx2ffmpeg(long pos, const AVRational &time_base) {
00162     return (long)((double)pos / (double)SRATE / (double)2. * (double)time_base.den);
00163 }
00164 
00165 
00166 /* PLAYGROUND */
00167 /*
00168    REAL:
00169    speedfreak
00170    Debug: file length 28263168
00171    nofreestyle
00172    Debug: file length 23904000
00173 
00174 
00175    speedfreak
00176    ratio 1,014428253
00177    Debug: ffmpeg: filelength: 28427212 -|- duration: 322304000 -- starttime: 0 -- 1000000
00178    Debug: ffmpeg: Seek ERRORRRRRRRRr ret(-1) filepos(28837367).
00179    Debug: file length 28427212
00180    //audiostream=-1
00181    Debug: ffmpeg: filelength: 28427212 -|- duration: 322304000 -- starttime: 0 -- 1000000
00182    time base is 1/90000
00183    Debug: ffmpeg: Seek ERRORRRRRRRRr ret(-1) filepos(320415184).
00184    --
00185    Debug: ffmpeg: filelength: 10342873 -|- duration: 117266144 -- starttime: 0 -- 1000000
00186    Debug: ffmpeg: Seek ERRORRRRRRRRr ret(-1) filepos(10551289).
00187    Debug: file length 10342873
00188    seek to <filepos>
00189 
00190    no free style
00191    Debug: ffmpeg: filelength: 53174016 -|- duration: 602880000 -- starttime: 0 -- 1000000
00192    time base is 1/90000
00193    Debug: ffmpeg: Seek ERRORRRRRRRRr ret(-1) filepos(24389275).
00194    //audistream = -1:
00195    Debug: ffmpeg: filelength: 53174016 -|- duration: 602880000 -- starttime: 0 -- 1000000
00196    time base is 1/90000
00197    Debug: ffmpeg: Seek ERRORRRRRRRRr ret(-1) filepos(270991939).
00198  */
00199 /*
00200    int  avcodec_decode_audio (AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, uint8_t *buf, int buf_size)
00201    int  av_seek_frame (AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
00202    Seek to the key frame at timestamp.
00203    Decode an audio frame.
00204    int  av_seek_frame_binary (AVFormatContext *s, int stream_index, int64_t target_ts, int flags)
00205    Does a binary search using av_index_search_timestamp() and AVCodec.read_timestamp().  */
00206 
00207 //   secs = filepos / SRATE / 2;
00208 //   mins = secs / 60;
00209 //   secs %= 60;
00210 //   hours = mins / 60;
00211 //   mins %= 60;
00212 /*for (fspos = 0; fspos < 602890000; fspos++){
00213    ret = av_seek_frame(pFormatCtx, -1, fspos, 0);
00214    if (ret){
00215    qDebug() << "ffmpeg: Seek ERRORRRRRRRRr ret(" << ret << ") filepos(" << fspos << "d).";
00216    fspos--;
00217    ret = av_seek_frame(pFormatCtx, audioStream, fspos, 0);
00218    readInput();
00219    qDebug() << "ffmpeg: seek2EROR " << bufferOffset << " " << bufferSize << " " << packet.pos << "ld";
00220    return 0;
00221    }
00222    }*/
00223 //  std::cout<< "time base is " << time_base.num << "/" << time_base.den << "\n";
00224 //  fspos = (long)((double)filepos / SRATE / 2 * AV_TIME_BASE);
00225 
00226 //  fspos2 = (long)((double)filepos / SRATE / 2 * time_base.den);
00227 
00228 long SoundSourceFFmpeg::seek(long filepos)
00229 {
00230     int ret = 0;
00231     int hours, mins, secs;
00232     long fspos, diff;
00233     AVRational time_base = pFormatCtx->streams[audioStream]->time_base;
00234 
00235     lock();
00236 
00237     fspos = mixxx2ffmpeg(filepos, time_base);
00238     //  qDebug() << "ffmpeg: seek0.5 " << packet.pos << "ld -- " << packet.duration << " -- " << pFormatCtx->streams[audioStream]->cur_dts << "ld";
00239     qDebug() << "ffmpeg: seek (ffpos " << fspos << "d) (mixxxpos " << filepos << "d)";
00240 
00241     ret = av_seek_frame(pFormatCtx, audioStream, fspos, AVSEEK_FLAG_BACKWARD /*AVSEEK_FLAG_ANY*/);
00242 
00243     if (ret){
00244         qDebug() << "ffmpeg: Seek ERROR ret(" << ret << ") filepos(" << filepos << "d).";
00245         unlock();
00246         return 0;
00247     }
00248 
00249     readInput();
00250     diff = ffmpeg2mixxx(fspos - pFormatCtx->streams[audioStream]->cur_dts, time_base);
00251     qDebug() << "ffmpeg: seeked (dts " << pFormatCtx->streams[audioStream]->cur_dts << ") (diff " << diff << ") (diff " << fspos - pFormatCtx->streams[audioStream]->cur_dts << ")";
00252 
00253     bufferOffset = 0; //diff;
00254     if (bufferOffset > bufferSize) {
00255         qDebug() << "ffmpeg: ERROR BAD OFFFFFFSET, buffsize: " << bufferSize << " offset: " << bufferOffset;
00256         bufferOffset = 0;
00257     }
00258     unlock();
00259     return filepos;
00260 }
00261 
00262 /*
00263  * internal function to only read one paquet
00264  */
00265 bool SoundSourceFFmpeg::readInput(){
00266     char * dst;
00267     unsigned char * src;
00268     int ret = 0;
00269     int readsize = 0;
00270     int inputsize = 0;
00271     int tries = 0;
00272     //DEBUG
00273     bufferSize = 0;
00274     bufferOffset = 0;
00275     memset(buffer, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE);
00276     while (av_read_packet(pFormatCtx, &packet)>0) {
00277         if (packet.stream_index==audioStream){
00278             dst = (char *)buffer;
00279             src = packet.data;
00280             inputsize = 0;
00281             readsize = 0;
00282             //qDebug() << "ffmpeg: before avcodec_decode_audio packet.size(" << packet.size << ")";
00283             tries = 0;
00284             do {
00285                 ret = avcodec_decode_audio(pCodecCtx, (int16_t *)dst, &readsize, src, packet.size - inputsize);
00286                 if (readsize == 0)
00287                 {
00288                     tries++;
00289                     //qDebug() << "ffmpeg: skip frame, decoded readsize = 0";
00290                     break;
00291                 }
00292                 if (ret <= 0)
00293                 {
00294                     tries++;
00295                     //qDebug() << "ffmpeg: skip frame, decoded ret = 0";
00296                     if (tries > 3) break;
00297                     continue;
00298                 }
00299                 dst += readsize;
00300                 bufferSize += readsize;
00301                 src += ret;
00302                 inputsize += ret;
00303                 //qDebug() << "ffmpeg: loop buffersize(" << bufferSize << "), readsize(" << readsize << ") ret(" << ret << ") psize(" << packet.size << ")";
00304             } while (inputsize < packet.size);
00305             //qDebug() << "ffmpeg: after avcodec_decode_audio outsize(" << bufferSize << ") - ret(" << ret << ")";
00306             if (bufferSize != 0)
00307                 return true;
00308 
00309         }
00310         //debug
00311         av_free_packet(&packet);
00312     }
00313     return false;
00314 }
00315 
00316 /*
00317    read <size> samples into <destination>, and return the number of
00318    samples actually read.
00319  */
00320 unsigned SoundSourceFFmpeg::read(unsigned long size, const SAMPLE * destination)
00321 {
00322 
00323     qDebug() << "This code has a bug! It needs fixing before you use it.";
00324     char * dest = (char *) destination;
00325     char * src = NULL;
00326     int index = 0;
00327     int outsize = 0;
00328 
00329     // rryan 2/2009 This is wrong! read()'s semantics are that
00330     // destination has only 'size' free items.
00331     int needed = size*2; //*channels;
00332 
00333     lock();
00334     qDebug() << "ffmpeg: read, requested:(" << needed / 2 << ")  dts:" << pFormatCtx->streams[audioStream]->cur_dts << "ld buffoffset:" << bufferOffset << " buffsize: " << bufferSize << "\n";
00335     //copy previous buffer
00336     src = (char *)buffer;
00337     src += bufferOffset;
00338     while (needed > 0) {
00339         if (bufferOffset < bufferSize) {
00340             index = bufferSize - bufferOffset > needed ? needed : bufferSize - bufferOffset;
00341             //qDebug() << "ffmpeg: copy(" << index << ") needed(" << needed << ")";
00342             memcpy((char *)dest, (char *)(src), index);
00343             src += index;
00344             dest += index; //(SAMPLE *)((char *)(dest) + index);
00345             needed -= index;
00346             bufferOffset += index;
00347             outsize += index;
00348         }
00349         if (needed > 0 && (bufferSize - bufferOffset <= 0)) {
00350             bufferOffset = 0;
00351             readInput();
00352             src = (char *)buffer;
00353             src += bufferOffset;
00354         }
00355     }
00356     // convert into stereo if file is mono
00357     /*if (channels == 1) {
00358        for(int i=index;i>0;i--) {
00359        dest[i*2]     = dest[i];
00360        dest[(i*2)+1] = dest[i];
00361        }
00362        }*/
00363     // return the number of samples in buffer
00364     unlock();
00365     return (outsize/2);
00366 }
00367 
00368 /*
00369    Parse the the file to get metadata
00370  */
00371 
00372 int SoundSourceFFmpeg::ParseHeader( TrackInfoObject * Track )
00373 {
00374     QString location = Track->getLocation();
00375     AVFormatContext * FmtCtx;
00376     AVCodecContext * CodecCtx;
00377     int i, audioStream = -1;
00378     QByteArray fname;
00379 
00380     fname = location.toAscii();
00381     FFmpegInit();
00382     qDebug() << "ffmpeg: pqrsing file:" << fname;
00383     if(av_open_input_file(&FmtCtx, fname.constData(), NULL, 0, NULL)!=0)
00384     {
00385         qDebug() << "av_open_input_file: cannot open" << fname;
00386         return ERR;
00387     }
00388     // Retrieve stream information
00389     if(av_find_stream_info(FmtCtx)<0)
00390     {
00391         qDebug() << "av_find_stream_info: cannot open" << fname;
00392         return ERR;
00393     }
00394     for(i=0; i<FmtCtx->nb_streams; i++)
00395         if(FmtCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO)
00396         {
00397             audioStream=i;
00398             break;
00399         }
00400     if(audioStream==-1)
00401     {
00402         qDebug() << "cannot find an audio stream: cannot open" << location;
00403         return ERR;
00404     }
00405     // Get a pointer to the codec context for the video stream
00406     CodecCtx=FmtCtx->streams[audioStream]->codec;
00407     Track->setType(location.section(".",-1).toLower());
00408     Track->setDuration(FmtCtx->duration / AV_TIME_BASE);
00409     Track->setBitrate((int)(CodecCtx->bit_rate / 1000));
00410     Track->setSampleRate(CodecCtx->sample_rate);
00411     Track->setChannels(CodecCtx->channels);
00412     av_close_input_file(FmtCtx);
00413     return OK;
00414 }
00415 
00416 /*
00417    Return the length of the file in samples.
00418  */
00419 inline long unsigned SoundSourceFFmpeg::length()
00420 {
00421     return filelength;
00422 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines