* Also pass the media_codec_info to the Writer::AllocateCookie(), since that

info is not part of the media_format otherwise.
* Finished enough in the AVFormatWriter and AVCodecEncoder that we can now
  actually create AVIs and MPGs and encode MPEG1, MPEG2 and MPEG4 video.
  But no audio as of yet. Also, there is no bit-rate/quality setup, so it seems
  libavformat is using the least possible bit-rate/quality.
* Enable some more muxers and encoders in the FFmpeg libs.
* Uses pixel format conversion from libswsscale, need to read the documentation
  again, but I think it makes the plugin GPL.
* Fixed includes in libswscale/swscale.h, this is now an unmodified FFmpeg 0.5
  header again (AFAICT).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32043 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2009-08-01 01:16:12 +00:00
parent a0bfe3ab9d
commit 54897d5c06
13 changed files with 224 additions and 45 deletions

View File

@ -21,7 +21,8 @@ public:
virtual status_t Close() = 0;
virtual status_t AllocateCookie(void** cookie,
const media_format* format) = 0;
const media_format* format,
const media_codec_info* codecInfo) = 0;
virtual status_t FreeCookie(void* cookie) = 0;
virtual status_t SetCopyright(void* cookie,

View File

@ -24,7 +24,7 @@ extern "C" {
#endif
static const size_t kDefaultChunkBufferSize = FF_MIN_BUFFER_SIZE;
static const size_t kDefaultChunkBufferSize = 2 * 1024 * 1024;
AVCodecEncoder::AVCodecEncoder(uint32 codecID)
@ -32,8 +32,8 @@ AVCodecEncoder::AVCodecEncoder(uint32 codecID)
Encoder(),
fCodec(NULL),
fContext(avcodec_alloc_context()),
fInputPicture(avcodec_alloc_frame()),
// fOutputPicture(avcodec_alloc_frame()),
fFrame(avcodec_alloc_frame()),
fSwsContext(NULL),
fCodecInitDone(false),
fChunkBuffer(new(std::nothrow) uint8[kDefaultChunkBufferSize])
{
@ -53,8 +53,25 @@ AVCodecEncoder::~AVCodecEncoder()
if (fCodecInitDone)
avcodec_close(fContext);
// free(fOutputPicture);
free(fInputPicture);
sws_freeContext(fSwsContext);
avpicture_free(&fDstFrame);
// NOTE: Do not use avpicture_free() on fSrcFrame!! We fill the picture
// data on the file with the media buffer data passed to Encode().
if (fFrame != NULL) {
fFrame->data[0] = NULL;
fFrame->data[1] = NULL;
fFrame->data[2] = NULL;
fFrame->data[3] = NULL;
fFrame->linesize[0] = 0;
fFrame->linesize[1] = 0;
fFrame->linesize[2] = 0;
fFrame->linesize[3] = 0;
free(fFrame);
}
free(fContext);
delete[] fChunkBuffer;
@ -94,10 +111,15 @@ AVCodecEncoder::SetUp(const media_format* inputFormat)
fInputFormat = *inputFormat;
if (fInputFormat.type == B_MEDIA_RAW_VIDEO) {
// frame rate
fContext->time_base.den = (int)fInputFormat.u.raw_video.field_rate;
fContext->time_base.num = 1;
// video size
fContext->width = fInputFormat.u.raw_video.display.line_width;
fContext->height = fInputFormat.u.raw_video.display.line_count;
// fContext->gop_size = 12;
fContext->pix_fmt = PIX_FMT_BGR32;
// TODO: Fix pixel format or setup conversion method...
fContext->pix_fmt = PIX_FMT_YUV420P;
// fContext->rate_emu = 0;
// TODO: Setup rate control:
// fContext->rc_eq = NULL;
@ -111,11 +133,38 @@ AVCodecEncoder::SetUp(const media_format* inputFormat)
|| fContext->sample_aspect_ratio.den == 0) {
av_reduce(&fContext->sample_aspect_ratio.num,
&fContext->sample_aspect_ratio.den, fContext->width,
fContext->height, 256);
fContext->height, 255);
}
// TODO: This should already happen in AcceptFormat()
fInputFormat.u.raw_video.display.bytes_per_row = fContext->width * 4;
if (fInputFormat.u.raw_video.display.bytes_per_row == 0) {
fInputFormat.u.raw_video.display.bytes_per_row
= fContext->width * 4;
}
fFrame->pts = 0;
// Allocate space for colorspace converted AVPicture
// TODO: Check allocations...
avpicture_alloc(&fDstFrame, fContext->pix_fmt, fContext->width,
fContext->height);
// Make the frame point to the data in the converted AVPicture
fFrame->data[0] = fDstFrame.data[0];
fFrame->data[1] = fDstFrame.data[1];
fFrame->data[2] = fDstFrame.data[2];
fFrame->data[3] = fDstFrame.data[3];
fFrame->linesize[0] = fDstFrame.linesize[0];
fFrame->linesize[1] = fDstFrame.linesize[1];
fFrame->linesize[2] = fDstFrame.linesize[2];
fFrame->linesize[3] = fDstFrame.linesize[3];
// TODO: Use actual pixel format from media_format!
fSwsContext = sws_getContext(fContext->width, fContext->height,
PIX_FMT_RGB32, fContext->width, fContext->height,
fContext->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
} else {
return B_NOT_SUPPORTED;
}
@ -154,6 +203,9 @@ AVCodecEncoder::Encode(const void* buffer, int64 frameCount,
{
TRACE("AVCodecEncoder::Encode(%p, %lld, %p)\n", buffer, frameCount, info);
if (!fCodecInitDone)
return B_NO_INIT;
if (fInputFormat.type == B_MEDIA_RAW_AUDIO)
return _EncodeAudio(buffer, frameCount, info);
else if (fInputFormat.type == B_MEDIA_RAW_VIDEO)
@ -192,12 +244,23 @@ AVCodecEncoder::_EncodeVideo(const void* buffer, int64 frameCount,
while (frameCount > 0) {
size_t bpr = fInputFormat.u.raw_video.display.bytes_per_row;
size_t bufferSize = fInputFormat.u.raw_video.display.line_count * bpr;
TRACE(" bytes per row: %ld, buffer size: %ld\n", bpr, bufferSize);
fInputPicture->data[0] = (uint8_t*)buffer;
fInputPicture->linesize[0] = bpr;
// We should always get chunky bitmaps, so this code should be safe.
fSrcFrame.data[0] = (uint8_t*)buffer;
fSrcFrame.linesize[0] = bpr;
// Run the pixel format conversion
sws_scale(fSwsContext, fSrcFrame.data, fSrcFrame.linesize, 0,
fInputFormat.u.raw_video.display.line_count, fDstFrame.data,
fDstFrame.linesize);
// TODO: Look into this... avcodec.h says we need to set it.
fFrame->pts++;
// Encode one video chunk/frame.
int usedBytes = avcodec_encode_video(fContext, fChunkBuffer,
kDefaultChunkBufferSize, fInputPicture);
kDefaultChunkBufferSize, fFrame);
if (usedBytes < 0) {
TRACE(" avcodec_encode_video() failed: %d\n", usedBytes);

View File

@ -10,6 +10,7 @@
extern "C" {
#include "avcodec.h"
#include "swscale.h"
}
#include "EncoderPlugin.h"
@ -51,8 +52,10 @@ private:
// TODO: Refactor common base class from AVCodec[De|En]Coder!
AVCodec* fCodec;
AVCodecContext* fContext;
AVFrame* fInputPicture;
// AVFrame* fOutputPicture;
AVPicture fSrcFrame;
AVPicture fDstFrame;
AVFrame* fFrame;
SwsContext* fSwsContext;
uint32 fAVCodecID;

View File

@ -26,7 +26,7 @@ extern "C" {
#include "gfx_util.h"
#define TRACE_AVFORMAT_READER
//#define TRACE_AVFORMAT_READER
#ifdef TRACE_AVFORMAT_READER
# define TRACE printf
# define TRACE_IO(a...)

View File

@ -58,7 +58,8 @@ public:
BLocker* streamLock);
virtual ~StreamCookie();
status_t Init(const media_format* format);
status_t Init(const media_format* format,
const media_codec_info* codecInfo);
status_t WriteChunk(const void* chunkBuffer,
size_t chunkSize,
@ -70,7 +71,8 @@ public:
private:
AVFormatContext* fContext;
AVStream* fStream;
// Since different threads may read from the source,
AVPacket fPacket;
// Since different threads may write to the target,
// we need to protect the file position and I/O by a lock.
BLocker* fStreamLock;
};
@ -84,29 +86,64 @@ AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context,
fStream(NULL),
fStreamLock(streamLock)
{
av_new_packet(&fPacket, 0);
}
AVFormatWriter::StreamCookie::~StreamCookie()
{
av_free_packet(&fPacket);
}
status_t
AVFormatWriter::StreamCookie::Init(const media_format* format)
AVFormatWriter::StreamCookie::Init(const media_format* format,
const media_codec_info* codecInfo)
{
TRACE("AVFormatWriter::StreamCookie::Init()\n");
BAutolock _(fStreamLock);
fStream = av_new_stream(fContext, fContext->nb_streams);
fPacket.stream_index = fContext->nb_streams;
fStream = av_new_stream(fContext, fPacket.stream_index);
if (fStream == NULL) {
TRACE(" failed to add new stream\n");
return B_ERROR;
}
// TODO: Setup the stream according to the media format...
// Setup the stream according to the media format...
if (format->type == B_MEDIA_RAW_VIDEO) {
avcodec_get_context_defaults2(fStream->codec, CODEC_TYPE_VIDEO);
// frame rate
fStream->codec->time_base.den = (int)format->u.raw_video.field_rate;
fStream->codec->time_base.num = 1;
// video size
fStream->codec->width = format->u.raw_video.display.line_width;
fStream->codec->height = format->u.raw_video.display.line_count;
// pixel aspect ratio
fStream->sample_aspect_ratio.num
= format->u.raw_video.pixel_width_aspect;
fStream->sample_aspect_ratio.den
= format->u.raw_video.pixel_height_aspect;
if (fStream->sample_aspect_ratio.num == 0
|| fStream->sample_aspect_ratio.den == 0) {
av_reduce(&fStream->sample_aspect_ratio.num,
&fStream->sample_aspect_ratio.den, fStream->codec->width,
fStream->codec->height, 255);
}
fStream->codec->sample_aspect_ratio = fStream->sample_aspect_ratio;
// TODO: Don't hard code this...
fStream->codec->pix_fmt = PIX_FMT_YUV420P;
} else if (format->type == B_MEDIA_RAW_AUDIO) {
avcodec_get_context_defaults2(fStream->codec, CODEC_TYPE_AUDIO);
// TODO: ...
}
// TODO: This is a hack for now! Use avcodec_find_encoder_by_name()
// or something similar...
fStream->codec->codec_id = (CodecID)codecInfo->sub_id;
return B_OK;
}
@ -116,12 +153,33 @@ status_t
AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
size_t chunkSize, media_encode_info* encodeInfo)
{
TRACE("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld)\n",
TRACE_PACKET("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld)\n",
chunkBuffer, chunkSize);
BAutolock _(fStreamLock);
return B_ERROR;
// TODO: Probably the AVCodecEncoder needs to pass packet data
// in encodeInfo...
fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
fPacket.size = chunkSize;
#if 0
// TODO: Eventually, we need to write interleaved packets, but
// maybe we are only supposed to use this if we have actually
// more than one stream. For the moment, this crashes in AVPacket
// shuffling inside libavformat. Maybe if we want to use this, we
// need to allocate a separate AVPacket and copy the chunk buffer.
int result = av_interleaved_write_frame(fContext, &fPacket);
if (result < 0)
TRACE(" av_interleaved_write_frame(): %d\n", result);
#else
int result = av_write_frame(fContext, &fPacket);
if (result < 0)
TRACE(" av_write_frame(): %d\n", result);
#endif
return result == 0 ? B_OK : B_ERROR;
}
@ -183,12 +241,10 @@ AVFormatWriter::Init(const media_file_format* fileFormat)
return B_ERROR;
}
// TODO: Is this how it works?
// TODO: Is the cookie stored in ByteIOContext? Or does it need to be
// stored in fContext->priv_data?
// Setup I/O hooks. This seems to be enough.
fContext->pb = &fIOContext;
// TODO: Set the AVOutputFormat according to fileFormat...
// Set the AVOutputFormat according to fileFormat...
fContext->oformat = guess_format(fileFormat->short_name,
fileFormat->file_extension, fileFormat->mime_type);
if (fContext->oformat == NULL) {
@ -263,7 +319,8 @@ AVFormatWriter::Close()
status_t
AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format)
AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format,
const media_codec_info* codecInfo)
{
TRACE("AVFormatWriter::AllocateCookie()\n");
@ -275,7 +332,14 @@ AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format)
StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext,
&fStreamLock);
return cookie->Init(format);
status_t ret = cookie->Init(format, codecInfo);
if (ret != B_OK) {
delete cookie;
return ret;
}
*_cookie = cookie;
return B_OK;
}
@ -327,7 +391,7 @@ AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
}
// #pragma mark -
// #pragma mark - I/O hooks
/*static*/ int

View File

@ -28,7 +28,8 @@ public:
virtual status_t Close();
virtual status_t AllocateCookie(void** cookie,
const media_format* format);
const media_format* format,
const media_codec_info* codecInfo);
virtual status_t FreeCookie(void* cookie);
virtual status_t SetCopyright(void* cookie,

View File

@ -20,22 +20,46 @@ const EncoderDescription gEncoderTable[] = {
CODEC_ID_MPEG4,
{ 0 }
},
B_ANY_FORMAT_FAMILY,
B_ANY_FORMAT_FAMILY, // TODO: Hm, actually not really /any/ family...
B_MEDIA_RAW_VIDEO,
B_MEDIA_ENCODED_VIDEO
},
{
{
"MP3 Audio",
"mp3",
"MPEG1 Video",
"mpeg1video",
0,
CODEC_ID_MP3,
CODEC_ID_MPEG1VIDEO,
{ 0 }
},
B_ANY_FORMAT_FAMILY,
B_MEDIA_RAW_AUDIO,
B_MEDIA_ENCODED_AUDIO
}
B_MPEG_FORMAT_FAMILY,
B_MEDIA_RAW_VIDEO,
B_MEDIA_ENCODED_VIDEO
},
{
{
"MPEG2 Video",
"mpeg2video",
0,
CODEC_ID_MPEG2VIDEO,
{ 0 }
},
B_MPEG_FORMAT_FAMILY,
B_MEDIA_RAW_VIDEO,
B_MEDIA_ENCODED_VIDEO
},
// {
// {
// "MP3 Audio",
// "mp3",
// 0,
// CODEC_ID_MP3,
// { 0 }
// },
// B_ANY_FORMAT_FAMILY,
// B_MEDIA_RAW_AUDIO,
// B_MEDIA_ENCODED_AUDIO
// }
};
const size_t gEncoderCount = sizeof(gEncoderTable) / sizeof(EncoderDescription);

View File

@ -24,6 +24,20 @@ const media_file_format gMuxerTable[] = {
"avi",
{ 0 }
},
{
media_file_format::B_WRITABLE
| media_file_format::B_KNOWS_ENCODED_VIDEO
| media_file_format::B_KNOWS_ENCODED_AUDIO,
{ 0 },
B_MPEG_FORMAT_FAMILY,
100,
{ 0 },
"video/mpeg",
"MPEG (Motion Picture Experts Group)",
"mpg",
"mpg",
{ 0 }
},
};
const size_t gMuxerCount = sizeof(gMuxerTable) / sizeof(media_file_format);

View File

@ -673,14 +673,14 @@
#define CONFIG_MP2_MUXER 0
#define CONFIG_MP3_MUXER 0
#define CONFIG_MP4_MUXER 0
#define CONFIG_MPEG1SYSTEM_MUXER 0
#define CONFIG_MPEG1SYSTEM_MUXER 1
#define CONFIG_MPEG1VCD_MUXER 0
#define CONFIG_MPEG1VIDEO_MUXER 0
#define CONFIG_MPEG1VIDEO_MUXER 1
#define CONFIG_MPEG2DVD_MUXER 0
#define CONFIG_MPEG2SVCD_MUXER 0
#define CONFIG_MPEG2VIDEO_MUXER 0
#define CONFIG_MPEG2VIDEO_MUXER 1
#define CONFIG_MPEG2VOB_MUXER 0
#define CONFIG_MPEGTS_MUXER 0
#define CONFIG_MPEGTS_MUXER 1
#define CONFIG_MPJPEG_MUXER 0
#define CONFIG_MXF_MUXER 0
#define CONFIG_MXF_D10_MUXER 0

View File

@ -2723,7 +2723,10 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt){
AVPacket opkt;
int ret= av_interleave_packet(s, &opkt, pkt, 0);
if(ret<=0) //FIXME cleanup needed for ret<0 ?
{
av_log(NULL, AV_LOG_ERROR, "av_interleaved_write_frame() - av_interleave_packet() failed.\n");
return ret;
}
ret= s->oformat->write_packet(s, &opkt);
@ -2731,9 +2734,15 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt){
pkt= NULL;
if(ret<0)
{
av_log(NULL, AV_LOG_ERROR, "av_interleaved_write_frame() - write_packet() failed.\n");
return ret;
}
if(url_ferror(s->pb))
{
av_log(NULL, AV_LOG_ERROR, "av_interleaved_write_frame() - url_ferror() failed.\n");
return url_ferror(s->pb);
}
}
}

View File

@ -28,7 +28,6 @@
*/
#include "libavutil/avutil.h"
#include "libavutil/internal.h"
#define LIBSWSCALE_VERSION_MAJOR 0
#define LIBSWSCALE_VERSION_MINOR 7

View File

@ -60,7 +60,8 @@ BMediaFile::BMediaFile(const entry_ref* ref, const media_file_format* mfi,
CALLED();
_Init();
fDeleteSource = true;
_InitWriter(new(std::nothrow) BFile(ref, O_WRONLY), mfi, flags);
_InitWriter(new(std::nothrow) BFile(ref, B_CREATE_FILE | B_ERASE_FILE
| B_WRITE_ONLY), mfi, flags);
}

View File

@ -110,7 +110,7 @@ MediaWriter::CreateEncoder(Encoder** _encoder,
}
StreamInfo info;
ret = fWriter->AllocateCookie(&info.cookie, format);
ret = fWriter->AllocateCookie(&info.cookie, format, codecInfo);
if (ret != B_OK) {
_plugin_manager.DestroyEncoder(encoder);
return ret;