* 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().


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39039 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2010-10-21 10:29:20 +00:00
parent 672b4d7800
commit 657983b8c2
3 changed files with 90 additions and 10 deletions

View File

@ -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 @@ AVCodecEncoder::AVCodecEncoder(uint32 codecID, int bitRateScale)
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 @@ AVCodecEncoder::~AVCodecEncoder()
free(fFrame);
}
free(fContext);
free(fOwnContext);
delete[] fChunkBuffer;
}
@ -157,6 +161,23 @@ AVCodecEncoder::SetUp(const media_format* inputFormat)
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 @@ AVCodecEncoder::SetEncodeParameters(encode_parameters* parameters)
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 @@ AVCodecEncoder::_Setup()
return B_NOT_SUPPORTED;
}
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...
int wantedBitRate = (int)(rawBitRate / fBitRateScale
* fEncodeParameters.quality);
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 @@ AVCodecEncoder::_Setup()
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 @@ AVCodecEncoder::_OpenCodecIfNeeded()
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 @@ AVCodecEncoder::_OpenCodecIfNeeded()
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 @@ AVCodecEncoder::_EncodeAudio(const uint8* buffer, size_t bufferSize,
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)

View File

@ -68,6 +68,7 @@ private:
// TODO: Refactor common base class from AVCodec[De|En]Coder!
CodecID fCodecID;
AVCodec* fCodec;
AVCodecContext* fOwnContext;
AVCodecContext* fContext;
enum {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
* Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de>
* 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 @@ AVFormatWriter::StreamCookie::Init(media_format* format,
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;
}