diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index 71558181c..2ba1b0a88 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -1377,7 +1377,14 @@ void AudioCaptureInner::onStart() { return; } d->stream->id = d->fmtContext->nb_streams - 1; - d->codecContext = d->stream->codec; + d->codecContext = avcodec_alloc_context3(d->codec); + if (!d->codecContext) { + LOG(("Audio Error: Unable to avcodec_alloc_context3 for capture")); + onStop(false); + emit error(); + return; + } + av_opt_set_int(d->codecContext, "refcounted_frames", 1, 0); d->codecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; @@ -1439,6 +1446,13 @@ void AudioCaptureInner::onStart() { } d->dstSamplesSize = av_samples_get_buffer_size(0, d->codecContext->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0); + if ((res = avcodec_parameters_from_context(d->stream->codecpar, d->codecContext)) < 0) { + LOG(("Audio Error: Unable to avcodec_parameters_from_context for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); + onStop(false); + emit error(); + return; + } + // Write file header if ((res = avformat_write_header(d->fmtContext, 0)) < 0) { LOG(("Audio Error: Unable to avformat_write_header for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); @@ -1486,9 +1500,10 @@ void AudioCaptureInner::onStop(bool needResult) { int32 framesize = d->srcSamples * d->codecContext->channels * sizeof(short), encoded = 0; while (_captured.size() >= encoded + framesize) { - writeFrame(encoded, framesize); + processFrame(encoded, framesize); encoded += framesize; } + writeFrame(nullptr); // drain the codec if (encoded != _captured.size()) { d->fullSamples = 0; d->dataPos = 0; @@ -1545,7 +1560,7 @@ void AudioCaptureInner::onStop(bool needResult) { d->device = nullptr; if (d->codecContext) { - avcodec_close(d->codecContext); + avcodec_free_context(&d->codecContext); d->codecContext = nullptr; } if (d->srcSamplesData) { @@ -1648,7 +1663,7 @@ void AudioCaptureInner::onTimeout() { // Write frames int32 framesize = d->srcSamples * d->codecContext->channels * sizeof(short), encoded = 0; while (uint32(_captured.size()) >= encoded + framesize + fadeSamples * sizeof(short)) { - writeFrame(encoded, framesize); + processFrame(encoded, framesize); encoded += framesize; } @@ -1663,7 +1678,7 @@ void AudioCaptureInner::onTimeout() { } } -void AudioCaptureInner::writeFrame(int32 offset, int32 framesize) { +void AudioCaptureInner::processFrame(int32 offset, int32 framesize) { // Prepare audio frame if (framesize % sizeof(short)) { // in the middle of a sample @@ -1730,33 +1745,93 @@ void AudioCaptureInner::writeFrame(int32 offset, int32 framesize) { // Write audio frame - AVPacket pkt; - memset(&pkt, 0, sizeof(pkt)); // data and size must be 0; AVFrame *frame = av_frame_alloc(); - int gotPacket; - av_init_packet(&pkt); frame->nb_samples = d->dstSamples; + frame->pts = av_rescale_q(d->fullSamples, (AVRational){1, d->codecContext->sample_rate}, d->codecContext->time_base); + avcodec_fill_audio_frame(frame, d->codecContext->channels, d->codecContext->sample_fmt, d->dstSamplesData[0], d->dstSamplesSize, 0); - if ((res = avcodec_encode_audio2(d->codecContext, &pkt, frame, &gotPacket)) < 0) { - LOG(("Audio Error: Unable to avcodec_encode_audio2 for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); + + writeFrame(frame); + + d->fullSamples += samplesCnt; + + av_frame_free(&frame); +} + +void AudioCaptureInner::writeFrame(AVFrame *frame) { + int res = 0; + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + + res = avcodec_send_frame(d->codecContext, frame); + if (res == AVERROR(EAGAIN)) { + int packetsWritten = writePackets(); + if (packetsWritten < 0) { + if (frame && packetsWritten == AVERROR_EOF) { + LOG(("Audio Error: EOF in packets received when EAGAIN was got in avcodec_send_frame()")); + onStop(false); + emit error(); + } + return; + } else if (!packetsWritten) { + LOG(("Audio Error: No packets received when EAGAIN was got in avcodec_send_frame()")); + onStop(false); + emit error(); + return; + } + res = avcodec_send_frame(d->codecContext, frame); + } + if (res < 0) { + LOG(("Audio Error: Unable to avcodec_send_frame for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); onStop(false); emit error(); return; } - if (gotPacket) { + if (!frame) { // drain + if ((res = writePackets()) != AVERROR_EOF) { + LOG(("Audio Error: not EOF in packets received when draining the codec, result %1").arg(res)); + onStop(false); + emit error(); + } + } +} + +int AudioCaptureInner::writePackets() { + AVPacket pkt; + memset(&pkt, 0, sizeof(pkt)); // data and size must be 0; + + int res = 0; + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + + int written = 0; + do { + av_init_packet(&pkt); + if ((res = avcodec_receive_packet(d->codecContext, &pkt)) < 0) { + if (res == AVERROR(EAGAIN)) { + return written; + } else if (res == AVERROR_EOF) { + return res; + } + LOG(("Audio Error: Unable to avcodec_receive_packet for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); + onStop(false); + emit error(); + return res; + } + + av_packet_rescale_ts(&pkt, d->codecContext->time_base, d->stream->time_base); pkt.stream_index = d->stream->index; if ((res = av_interleaved_write_frame(d->fmtContext, &pkt)) < 0) { LOG(("Audio Error: Unable to av_interleaved_write_frame for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); onStop(false); emit error(); - return; + return -1; } - } - d->fullSamples += samplesCnt; - av_frame_free(&frame); + ++written; + av_packet_unref(&pkt); + } while (true); + return written; } class FFMpegAttributesReader : public AbstractFFMpegLoader { diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h index a4fc7c785..87b8cfd08 100644 --- a/Telegram/SourceFiles/media/media_audio.h +++ b/Telegram/SourceFiles/media/media_audio.h @@ -264,6 +264,7 @@ private: }; struct AudioCapturePrivate; +struct AVFrame; class AudioCaptureInner : public QObject { Q_OBJECT @@ -289,7 +290,13 @@ signals: private: - void writeFrame(int32 offset, int32 framesize); + void processFrame(int32 offset, int32 framesize); + + void writeFrame(AVFrame *frame); + + // Writes the packets till EAGAIN is got from av_receive_packet() + // Returns number of packets written or -1 on error + int writePackets(); AudioCapturePrivate *d; QTimer _timer; diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp index b9ef7e020..a8a4227f1 100644 --- a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp @@ -65,7 +65,7 @@ bool AbstractFFMpegLoader::open(qint64 &position) { return false; } - freq = fmtContext->streams[streamId]->codec->sample_rate; + freq = fmtContext->streams[streamId]->codecpar->sample_rate; if (fmtContext->streams[streamId]->duration == AV_NOPTS_VALUE) { len = (fmtContext->duration * freq) / AV_TIME_BASE; } else { @@ -145,15 +145,26 @@ bool FFMpegLoader::open(qint64 &position) { int res = 0; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - // Get a pointer to the codec context for the audio stream - av_opt_set_int(fmtContext->streams[streamId]->codec, "refcounted_frames", 1, 0); - if ((res = avcodec_open2(fmtContext->streams[streamId]->codec, codec, 0)) < 0) { + auto codecParams = fmtContext->streams[streamId]->codecpar; + + codecContext = avcodec_alloc_context3(nullptr); + if (!codecContext) { + LOG(("Audio Error: Unable to avcodec_alloc_context3 for file '%1', data size '%2'").arg(file.name()).arg(data.size())); + return false; + } + if ((res = avcodec_parameters_to_context(codecContext, codecParams)) < 0) { + LOG(("Audio Error: Unable to avcodec_parameters_to_context for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return false; + } + av_codec_set_pkt_timebase(codecContext, fmtContext->streams[streamId]->time_base); + av_opt_set_int(codecContext, "refcounted_frames", 1, 0); + + if ((res = avcodec_open2(codecContext, codec, 0)) < 0) { LOG(("Audio Error: Unable to avcodec_open2 for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } - codecContext = fmtContext->streams[streamId]->codec; - uint64_t layout = codecContext->channel_layout; + uint64_t layout = codecParams->channel_layout; inputFormat = codecContext->sample_fmt; switch (layout) { case AV_CH_LAYOUT_MONO: @@ -232,66 +243,81 @@ bool FFMpegLoader::open(qint64 &position) { AudioPlayerLoader::ReadResult FFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) { int res; + + av_frame_unref(frame); + res = avcodec_receive_frame(codecContext, frame); + if (res >= 0) { + return readFromReadyFrame(result, samplesAdded); + } + + if (res == AVERROR_EOF) { + return ReadResult::EndOfFile; + } else if (res != AVERROR(EAGAIN)) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_receive_frame() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; + } + if ((res = av_read_frame(fmtContext, &avpkt)) < 0) { if (res != AVERROR_EOF) { char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; LOG(("Audio Error: Unable to av_read_frame() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return ReadResult::Error; } - return ReadResult::EndOfFile; + avcodec_send_packet(codecContext, nullptr); // drain + return ReadResult::Ok; } - if (avpkt.stream_index == streamId) { - av_frame_unref(frame); - int got_frame = 0; - if ((res = avcodec_decode_audio4(codecContext, frame, &got_frame, &avpkt)) < 0) { - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + if (avpkt.stream_index == streamId) { + res = avcodec_send_packet(codecContext, &avpkt); + if (res < 0) { av_packet_unref(&avpkt); + + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_send_packet() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); if (res == AVERROR_INVALIDDATA) { return ReadResult::NotYet; // try to skip bad packet } return ReadResult::Error; } - - if (got_frame) { - if (dstSamplesData) { // convert needed - int64_t dstSamples = av_rescale_rnd(swr_get_delay(swrContext, srcRate) + frame->nb_samples, dstRate, srcRate, AV_ROUND_UP); - if (dstSamples > maxResampleSamples) { - maxResampleSamples = dstSamples; - av_free(dstSamplesData[0]); - - if ((res = av_samples_alloc(dstSamplesData, 0, AudioToChannels, maxResampleSamples, AudioToFormat, 1)) < 0) { - dstSamplesData[0] = 0; - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - - av_packet_unref(&avpkt); - return ReadResult::Error; - } - } - if ((res = swr_convert(swrContext, dstSamplesData, dstSamples, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) { - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - - av_packet_unref(&avpkt); - return ReadResult::Error; - } - int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); - result.append((const char*)dstSamplesData[0], resultLen); - samplesAdded += resultLen / sampleSize; - } else { - result.append((const char*)frame->extended_data[0], frame->nb_samples * sampleSize); - samplesAdded += frame->nb_samples; - } - } } av_packet_unref(&avpkt); return ReadResult::Ok; } +AudioPlayerLoader::ReadResult FFMpegLoader::readFromReadyFrame(QByteArray &result, int64 &samplesAdded) { + int res = 0; + + if (dstSamplesData) { // convert needed + int64_t dstSamples = av_rescale_rnd(swr_get_delay(swrContext, srcRate) + frame->nb_samples, dstRate, srcRate, AV_ROUND_UP); + if (dstSamples > maxResampleSamples) { + maxResampleSamples = dstSamples; + av_free(dstSamplesData[0]); + + if ((res = av_samples_alloc(dstSamplesData, 0, AudioToChannels, maxResampleSamples, AudioToFormat, 1)) < 0) { + dstSamplesData[0] = 0; + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; + } + } + if ((res = swr_convert(swrContext, dstSamplesData, dstSamples, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; + } + int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); + result.append((const char*)dstSamplesData[0], resultLen); + samplesAdded += resultLen / sampleSize; + } else { + result.append((const char*)frame->extended_data[0], frame->nb_samples * sampleSize); + samplesAdded += frame->nb_samples; + } + return ReadResult::Ok; +} + FFMpegLoader::~FFMpegLoader() { - if (codecContext) avcodec_close(codecContext); + if (codecContext) avcodec_free_context(&codecContext); if (swrContext) swr_free(&swrContext); if (dstSamplesData) { if (dstSamplesData[0]) { diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h index c856be631..4b9ec0349 100644 --- a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h @@ -86,6 +86,8 @@ protected: int32 sampleSize = 2 * sizeof(uint16); private: + ReadResult readFromReadyFrame(QByteArray &result, int64 &samplesAdded); + int32 fmt = AL_FORMAT_STEREO16; int32 srcRate = AudioVoiceMsgFrequency; int32 dstRate = AudioVoiceMsgFrequency; diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp index 143f9f1c6..8932fcde3 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp @@ -114,65 +114,78 @@ bool ChildFFMpegLoader::open(qint64 &position) { } AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) { + int res; + + av_frame_unref(_frame); + res = avcodec_receive_frame(_parentData->context, _frame); + if (res >= 0) { + return readFromReadyFrame(result, samplesAdded); + } + + if (res == AVERROR_EOF) { + return ReadResult::EndOfFile; + } else if (res != AVERROR(EAGAIN)) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_receive_frame() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; + } + if (_queue.isEmpty()) { return _eofReached ? ReadResult::EndOfFile : ReadResult::Wait; } - av_frame_unref(_frame); - int got_frame = 0; - int res = 0; - AVPacket packet; FFMpeg::packetFromDataWrap(packet, _queue.dequeue()); _eofReached = FFMpeg::isNullPacket(packet); if (_eofReached) { - return ReadResult::EndOfFile; + avcodec_send_packet(_parentData->context, nullptr); // drain + return ReadResult::Ok; } - if ((res = avcodec_decode_audio4(_parentData->context, _frame, &got_frame, &packet)) < 0) { - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - + res = avcodec_send_packet(_parentData->context, &packet); + if (res < 0) { FFMpeg::freePacket(&packet); + + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_send_packet() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); if (res == AVERROR_INVALIDDATA) { return ReadResult::NotYet; // try to skip bad packet } return ReadResult::Error; } + FFMpeg::freePacket(&packet); + return ReadResult::Ok; +} - if (got_frame) { - if (_dstSamplesData) { // convert needed - int64_t dstSamples = av_rescale_rnd(swr_get_delay(_swrContext, _srcRate) + _frame->nb_samples, _dstRate, _srcRate, AV_ROUND_UP); - if (dstSamples > _maxResampleSamples) { - _maxResampleSamples = dstSamples; - av_free(_dstSamplesData[0]); +AudioPlayerLoader::ReadResult ChildFFMpegLoader::readFromReadyFrame(QByteArray &result, int64 &samplesAdded) { + int res = 0; - if ((res = av_samples_alloc(_dstSamplesData, 0, AudioToChannels, _maxResampleSamples, AudioToFormat, 1)) < 0) { - _dstSamplesData[0] = 0; - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + if (_dstSamplesData) { // convert needed + int64_t dstSamples = av_rescale_rnd(swr_get_delay(_swrContext, _srcRate) + _frame->nb_samples, _dstRate, _srcRate, AV_ROUND_UP); + if (dstSamples > _maxResampleSamples) { + _maxResampleSamples = dstSamples; + av_free(_dstSamplesData[0]); - FFMpeg::freePacket(&packet); - return ReadResult::Error; - } - } - if ((res = swr_convert(_swrContext, _dstSamplesData, dstSamples, (const uint8_t**)_frame->extended_data, _frame->nb_samples)) < 0) { + if ((res = av_samples_alloc(_dstSamplesData, 0, AudioToChannels, _maxResampleSamples, AudioToFormat, 1)) < 0) { + _dstSamplesData[0] = 0; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - - FFMpeg::freePacket(&packet); + LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return ReadResult::Error; } - int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); - result.append((const char*)_dstSamplesData[0], resultLen); - samplesAdded += resultLen / _sampleSize; - } else { - result.append((const char*)_frame->extended_data[0], _frame->nb_samples * _sampleSize); - samplesAdded += _frame->nb_samples; } + if ((res = swr_convert(_swrContext, _dstSamplesData, dstSamples, (const uint8_t**)_frame->extended_data, _frame->nb_samples)) < 0) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; + } + int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); + result.append((const char*)_dstSamplesData[0], resultLen); + samplesAdded += resultLen / _sampleSize; + } else { + result.append((const char*)_frame->extended_data[0], _frame->nb_samples * _sampleSize); + samplesAdded += _frame->nb_samples; } - FFMpeg::freePacket(&packet); return ReadResult::Ok; } diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h index ad92459ab..d7542cd12 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h @@ -114,6 +114,8 @@ public: ~ChildFFMpegLoader(); private: + ReadResult readFromReadyFrame(QByteArray &result, int64 &samplesAdded); + bool _eofReached = false; int32 _sampleSize = 2 * sizeof(uint16); diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp index 3104c4d9f..5378fea2c 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp @@ -42,77 +42,14 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { _frameRead = false; } - while (true) { - while (_packetQueue.isEmpty()) { - auto packetResult = readAndProcessPacket(); - if (packetResult == PacketResult::Error) { - return ReadResult::Error; - } else if (packetResult == PacketResult::EndOfFile) { - break; - } - } - bool eofReached = _packetQueue.isEmpty(); - - startPacket(); - - int got_frame = 0; - auto packet = &_packetNull; - AVPacket tempPacket; - if (!_packetQueue.isEmpty()) { - FFMpeg::packetFromDataWrap(tempPacket, _packetQueue.head()); - packet = &tempPacket; - } - int decoded = packet->size; - - int res = 0; - if ((res = avcodec_decode_video2(_codecContext, _frame, &got_frame, packet)) < 0) { - char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Gif Error: Unable to avcodec_decode_video2() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - - if (res == AVERROR_INVALIDDATA) { // try to skip bad packet - finishPacket(); - continue; - } - - eofReached = (res == AVERROR_EOF); - if (!eofReached || !_hadFrame) { // try to skip end of file - return ReadResult::Error; - } - } - if (res > 0) decoded = res; - - if (!_packetQueue.isEmpty()) { - packet->data += decoded; - packet->size -= decoded; - if (packet->size <= 0) { - finishPacket(); - } - } - - if (got_frame) { - int64 duration = av_frame_get_pkt_duration(_frame); - int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts; - int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; - _currentFrameDelay = _nextFrameDelay; - if (_frameMs + _currentFrameDelay < frameMs) { - _currentFrameDelay = int32(frameMs - _frameMs); - } else if (frameMs < _frameMs + _currentFrameDelay) { - frameMs = _frameMs + _currentFrameDelay; - } - - if (duration == AV_NOPTS_VALUE) { - _nextFrameDelay = 0; - } else { - _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; - } - _frameMs = frameMs; - - _hadFrame = _frameRead = true; - _frameTime += _currentFrameDelay; + do { + int res = avcodec_receive_frame(_codecContext, _frame); + if (res >= 0) { + processReadFrame(); return ReadResult::Success; } - if (eofReached) { + if (res == AVERROR_EOF) { clearPacketQueue(); if (_mode == Mode::Normal) { return ReadResult::Eof; @@ -133,12 +70,70 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { _hadFrame = false; _frameMs = 0; _lastReadVideoMs = _lastReadAudioMs = 0; + + continue; + } else if (res != AVERROR(EAGAIN)) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_receive_frame() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return ReadResult::Error; } - } + + while (_packetQueue.isEmpty()) { + auto packetResult = readAndProcessPacket(); + if (packetResult == PacketResult::Error) { + return ReadResult::Error; + } else if (packetResult == PacketResult::EndOfFile) { + break; + } + } + if (_packetQueue.isEmpty()) { + avcodec_send_packet(_codecContext, nullptr); // drain + continue; + } + + startPacket(); + + AVPacket packet; + FFMpeg::packetFromDataWrap(packet, _packetQueue.head()); + res = avcodec_send_packet(_codecContext, &packet); + if (res < 0) { + finishPacket(); + + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; + LOG(("Audio Error: Unable to avcodec_send_packet() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + if (res == AVERROR_INVALIDDATA) { + continue; // try to skip bad packet + } + return ReadResult::Error; + } + finishPacket(); + } while (true); return ReadResult::Error; } +void FFMpegReaderImplementation::processReadFrame() { + int64 duration = av_frame_get_pkt_duration(_frame); + int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts; + int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + _currentFrameDelay = _nextFrameDelay; + if (_frameMs + _currentFrameDelay < frameMs) { + _currentFrameDelay = int32(frameMs - _frameMs); + } else if (frameMs < _frameMs + _currentFrameDelay) { + frameMs = _frameMs + _currentFrameDelay; + } + + if (duration == AV_NOPTS_VALUE) { + _nextFrameDelay = 0; + } else { + _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + } + _frameMs = frameMs; + + _hadFrame = _frameRead = true; + _frameTime += _currentFrameDelay; +} + ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(int64 frameMs, uint64 systemMs) { if (_audioStreamId < 0) { // just keep up if (_frameRead && _frameTime > frameMs) { @@ -291,8 +286,18 @@ bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) { } _packetNull.stream_index = _streamId; - // Get a pointer to the codec context for the video stream - _codecContext = _fmtContext->streams[_streamId]->codec; + _codecContext = avcodec_alloc_context3(nullptr); + if (!_codecContext) { + LOG(("Audio Error: Unable to avcodec_alloc_context3 %1").arg(logData())); + return false; + } + if ((res = avcodec_parameters_to_context(_codecContext, _fmtContext->streams[_streamId]->codecpar)) < 0) { + LOG(("Audio Error: Unable to avcodec_parameters_to_context %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return false; + } + av_codec_set_pkt_timebase(_codecContext, _fmtContext->streams[_streamId]->time_base); + av_opt_set_int(_codecContext, "refcounted_frames", 1, 0); + _codec = avcodec_find_decoder(_codecContext->codec_id); _audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0); @@ -309,7 +314,7 @@ bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) { } else if (_mode == Mode::Silent || !audioPlayer() || !_playId) { _audioStreamId = -1; } - av_opt_set_int(_codecContext, "refcounted_frames", 1, 0); + if ((res = avcodec_open2(_codecContext, _codec, 0)) < 0) { LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; @@ -317,16 +322,19 @@ bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) { std_::unique_ptr soundData; if (_audioStreamId >= 0) { - // Get a pointer to the codec context for the audio stream - auto audioContextOriginal = _fmtContext->streams[_audioStreamId]->codec; - auto audioCodec = avcodec_find_decoder(audioContextOriginal->codec_id); - - AVCodecContext *audioContext = avcodec_alloc_context3(audioCodec); - if ((res = avcodec_copy_context(audioContext, audioContextOriginal)) != 0) { - LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + AVCodecContext *audioContext = avcodec_alloc_context3(nullptr); + if (!audioContext) { + LOG(("Audio Error: Unable to avcodec_alloc_context3 %1").arg(logData())); return false; } + if ((res = avcodec_parameters_to_context(audioContext, _fmtContext->streams[_audioStreamId]->codecpar)) < 0) { + LOG(("Audio Error: Unable to avcodec_parameters_to_context %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + return false; + } + av_codec_set_pkt_timebase(audioContext, _fmtContext->streams[_audioStreamId]->time_base); av_opt_set_int(audioContext, "refcounted_frames", 1, 0); + + auto audioCodec = avcodec_find_decoder(audioContext->codec_id); if ((res = avcodec_open2(audioContext, audioCodec, 0)) < 0) { avcodec_free_context(&audioContext); LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); @@ -334,7 +342,7 @@ bool FFMpegReaderImplementation::start(Mode mode, int64 &positionMs) { } else { soundData = std_::make_unique(); soundData->context = audioContext; - soundData->frequency = audioContextOriginal->sample_rate; + soundData->frequency = _fmtContext->streams[_audioStreamId]->codecpar->sample_rate; if (_fmtContext->streams[_audioStreamId]->duration == AV_NOPTS_VALUE) { soundData->length = (_fmtContext->duration * soundData->frequency) / AV_TIME_BASE; } else { @@ -385,7 +393,7 @@ FFMpegReaderImplementation::~FFMpegReaderImplementation() { av_frame_unref(_frame); _frameRead = false; } - if (_codecContext) avcodec_close(_codecContext); + if (_codecContext) avcodec_free_context(&_codecContext); if (_swsContext) sws_freeContext(_swsContext); if (_opened) { avformat_close_input(&_fmtContext); diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.h b/Telegram/SourceFiles/media/media_clip_ffmpeg.h index e9cf5074c..e805a099d 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.h +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.h @@ -59,6 +59,7 @@ public: private: ReadResult readNextFrame(); + void processReadFrame(); enum class PacketResult { Ok, diff --git a/doc/building-msvc.md b/doc/building-msvc.md index 58865d87e..ec67b58d6 100644 --- a/doc/building-msvc.md +++ b/doc/building-msvc.md @@ -148,7 +148,7 @@ Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **Start Menu > git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg cd ffmpeg - git checkout release/2.8 + git checkout release/3.1 http://msys2.github.io/ > Download [msys2-x86_64-20150512.exe](http://sourceforge.net/projects/msys2/files/Base/x86_64/msys2-x86_64-20150512.exe/download) and install to **D:\\msys64** @@ -169,7 +169,7 @@ Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **Start Menu > PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH" - ./configure --toolchain=msvc --disable-programs --disable-doc --disable-everything --disable-w32threads --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:/d/TBuild/Libraries/opus/win32/VS2010/Win32/Release celt.lib silk_common.lib silk_float.lib" + ./configure --toolchain=msvc --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:/d/TBuild/Libraries/opus/win32/VS2010/Win32/Release celt.lib silk_common.lib silk_float.lib" make make install diff --git a/doc/building-qtcreator.md b/doc/building-qtcreator.md index 18695f128..afb7e60de 100644 --- a/doc/building-qtcreator.md +++ b/doc/building-qtcreator.md @@ -69,13 +69,13 @@ In Terminal go to **/home/user/TBuild/Libraries** and run git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg cd ffmpeg - git checkout release/2.8 + git checkout release/3.1 sudo apt-get update sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev sudo apt-get install yasm - ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-pthreads --disable-mmx --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vdpau --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus + ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vdpau --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus make sudo make install diff --git a/doc/building-xcode-old.md b/doc/building-xcode-old.md index c77daf41d..58d3c8143 100644 --- a/doc/building-xcode-old.md +++ b/doc/building-xcode-old.md @@ -153,7 +153,7 @@ Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg** and run LDFLAGS=`freetype-config --libs` PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig - ./configure --prefix=/usr/local/ffmpeg_old --disable-programs --disable-doc --disable-everything --disable-pthreads --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-cflags="-mmacosx-version-min=10.6" --extra-cxxflags="-mmacosx-version-min=10.6" --extra-ldflags="-mmacosx-version-min=10.6" + ./configure --prefix=/usr/local/ffmpeg_old --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-cflags="-mmacosx-version-min=10.6" --extra-cxxflags="-mmacosx-version-min=10.6" --extra-ldflags="-mmacosx-version-min=10.6" make sudo make install diff --git a/doc/building-xcode.md b/doc/building-xcode.md index dcf0ae122..eb6bcca8a 100644 --- a/doc/building-xcode.md +++ b/doc/building-xcode.md @@ -187,7 +187,7 @@ Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg** and run: LDFLAGS=`freetype-config --libs` PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig - ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-everything --disable-pthreads --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vda --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=mpeg4_videotoolbox --enable-hwaccel=h264_vda --enable-hwaccel=h264_videotoolbox --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-cflags="-mmacosx-version-min=10.8" --extra-cxxflags="-mmacosx-version-min=10.8" --extra-ldflags="-mmacosx-version-min=10.8" + ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vda --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=mpeg4_videotoolbox --enable-hwaccel=h264_vda --enable-hwaccel=h264_videotoolbox --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-cflags="-mmacosx-version-min=10.8" --extra-cxxflags="-mmacosx-version-min=10.8" --extra-ldflags="-mmacosx-version-min=10.8" make sudo make install