Author: stippi Date: 2010-10-21 12:29:20 +0200 (Thu, 21 Oct 2010) New Revision: 39039 Changeset: http://dev.haiku-os.org/changeset/39039 Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp Log: * Introduce some currently disabled code to store the AVCodecContext pointer in the media_format user data section. * In the AVCodecEncoder, optionally use the AVCodecContext pointer from the AVFormatWriter instead of its own instance. The problems I am investigating are not improved by this, but it may be needed anyway. * Map the bitrate for audio to a fixed table. Certain encoders will refuse to use a non-standard bitrate, like the currently enabled AC-3 encoder. * Fixed tracing output in _EncodeAudio(). Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp =================================================================== --- haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp 2010-10-21 09:00:07 UTC (rev 39038) +++ haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp 2010-10-21 10:29:20 UTC (rev 39039) @@ -11,6 +11,9 @@ #include <stdio.h> #include <string.h> +#include <Application.h> +#include <Roster.h> + extern "C" { #include "rational.h" } @@ -39,7 +42,8 @@ fBitRateScale(bitRateScale), fCodecID((enum CodecID)codecID), fCodec(NULL), - fContext(avcodec_alloc_context()), + fOwnContext(avcodec_alloc_context()), + fContext(fOwnContext), fCodecInitStatus(CODEC_INIT_NEEDED), fFrame(avcodec_alloc_frame()), @@ -105,7 +109,7 @@ free(fFrame); } - free(fContext); + free(fOwnContext); delete[] fChunkBuffer; } @@ -157,6 +161,23 @@ fInputFormat = *inputFormat; fFramesWritten = 0; + const uchar* userData = inputFormat->user_data; + if (*(uint32*)userData == 'ffmp') { + userData += sizeof(uint32); + // The Writer plugin used is the FFmpeg plugin. It stores the + // AVCodecContext pointer in the user data section. Use this + // context instead of our own. It requires the Writer living in + // the same team, of course. + app_info appInfo; + if (be_app->GetAppInfo(&appInfo) == B_OK + && *(team_id*)userData == appInfo.team) { + userData += sizeof(team_id); + // Use the AVCodecContext from the Writer. This works better + // than using our own context with some encoders. + fContext = *(AVCodecContext**)userData; + } + } + return _Setup(); } @@ -202,7 +223,11 @@ return B_NOT_SUPPORTED; fEncodeParameters.quality = parameters->quality; - TRACE(" quality: %.1f\n", parameters->quality); + TRACE(" quality: %.5f\n", parameters->quality); + if (fEncodeParameters.quality == 0.0f) { + TRACE(" using default quality (1.0)\n"); + fEncodeParameters.quality = 1.0f; + } // TODO: Auto-bit_rate versus user supplied. See above. // int avgBytesPerSecond = 0; @@ -401,14 +426,38 @@ return B_NOT_SUPPORTED; } + // TODO: Support letting the user overwrite this via + // SetEncodeParameters(). See comments there... int wantedBitRate = (int)(rawBitRate / fBitRateScale * fEncodeParameters.quality); - TRACE(" rawBitRate: %d, wantedBitRate: %d (%.1f)\n", rawBitRate, - wantedBitRate, fEncodeParameters.quality); - // TODO: Support letting the user overwrite this via - // SetEncodeParameters(). See comments there... + if (wantedBitRate == 0) + wantedBitRate = (int)(rawBitRate / fBitRateScale); + fContext->bit_rate = wantedBitRate; + if (fInputFormat.type == B_MEDIA_RAW_AUDIO) { + // Some audio encoders support certain bitrates only. Use the + // closest match to the wantedBitRate. + const int kBitRates[] = { + 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, + 160000, 192000, 224000, 256000, 320000, 384000, 448000, 512000, + 576000, 640000 + }; + int diff = wantedBitRate; + for (int i = 0; i < sizeof(kBitRates) / sizeof(int); i++) { + int currentDiff = abs(wantedBitRate - kBitRates[i]); + if (currentDiff < diff) { + fContext->bit_rate = kBitRates[i]; + diff = currentDiff; + } else + break; + } + } + + TRACE(" rawBitRate: %d, wantedBitRate: %d (%.1f), " + "context bitrate: %d\n", rawBitRate, wantedBitRate, + fEncodeParameters.quality, fContext->bit_rate); + // Add some known fixes from the FFmpeg API example: if (fContext->codec_id == CODEC_ID_MPEG2VIDEO) { // Just for testing, we also add B frames */ @@ -430,6 +479,12 @@ bool AVCodecEncoder::_OpenCodecIfNeeded() { + if (fContext != fOwnContext) { + // We are using the AVCodecContext of the AVFormatWriter plugin, + // and don't maintain it's open/close state. + return true; + } + if (fCodecInitStatus == CODEC_INIT_DONE) return true; @@ -443,7 +498,7 @@ else fCodecInitStatus = CODEC_INIT_FAILED; - TRACE(" avcodec_open(): %d\n", result); + TRACE(" avcodec_open(%p, %p): %d\n", fContext, fCodec, result); return fCodecInitStatus == CODEC_INIT_DONE; @@ -453,6 +508,11 @@ void AVCodecEncoder::_CloseCodecIfNeeded() { + if (fContext != fOwnContext) { + // See _OpenCodecIfNeeded(). + return; + } + if (fCodecInitStatus == CODEC_INIT_DONE) { avcodec_close(fContext); fCodecInitStatus = CODEC_INIT_NEEDED; @@ -536,7 +596,7 @@ bufferSize, reinterpret_cast<const short*>(buffer)); if (usedBytes < 0) { - TRACE(" avcodec_encode_video() failed: %d\n", usedBytes); + TRACE(" avcodec_encode_audio() failed: %d\n", usedBytes); return B_ERROR; } if (usedBytes == 0) Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h =================================================================== --- haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h 2010-10-21 09:00:07 UTC (rev 39038) +++ haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h 2010-10-21 10:29:20 UTC (rev 39039) @@ -68,6 +68,7 @@ // TODO: Refactor common base class from AVCodec[De|En]Coder! CodecID fCodecID; AVCodec* fCodec; + AVCodecContext* fOwnContext; AVCodecContext* fContext; enum { Modified: haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp =================================================================== --- haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp 2010-10-21 09:00:07 UTC (rev 39038) +++ haiku/trunk/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp 2010-10-21 10:29:20 UTC (rev 39039) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Stephan Aßmus <superstippi@xxxxxx> + * Copyright 2009-2010, Stephan Aßmus <superstippi@xxxxxx> * All rights reserved. Distributed under the terms of the GNU L-GPL license. */ @@ -11,12 +11,14 @@ #include <new> +#include <Application.h> #include <AutoDeleter.h> #include <Autolock.h> #include <ByteOrder.h> #include <DataIO.h> #include <MediaDefs.h> #include <MediaFormats.h> +#include <Roster.h> extern "C" { #include "avformat.h" @@ -234,6 +236,23 @@ fStream->time_base.num, fStream->time_base.den, fStream->codec->time_base.num, fStream->codec->time_base.den); +#if 0 + // Write the AVCodecContext pointer to the user data section of the + // media_format. For some encoders, it seems to be necessary to use + // the AVCodecContext of the AVStream in order to successfully encode + // anything and write valid media files. For example some codecs need + // to store meta data or global data in the container. + app_info appInfo; + if (be_app->GetAppInfo(&appInfo) == B_OK) { + uchar* userData = format->user_data; + *(uint32*)userData = 'ffmp'; + userData += sizeof(uint32); + *(team_id*)userData = appInfo.team; + userData += sizeof(team_id); + *(AVCodecContext**)userData = fStream->codec; + } +#endif + return B_OK; }