diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp index 6fd5c35d07..36cd4b9226 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.cpp @@ -13,13 +13,10 @@ #include #include -//#include +#include #include -extern "C" { - #include "avformat.h" - #include "libavutil/avstring.h" -} +#include "gfx_util.h" //#include "RawFormats.h" @@ -36,11 +33,18 @@ extern "C" { #define ERROR(a...) fprintf(stderr, a) +static const size_t kIOBufferSize = 64 * 1024; + // TODO: This could depend on the BMediaFile creation flags, IIRC, + // the allow to specify a buffering mode. + + AVFormatReader::AVFormatReader() : - fContext(NULL) + fContext(NULL), + fIOBuffer(NULL) { TRACE("AVFormatReader::AVFormatReader\n"); + memset(&fFormatParameters, 0, sizeof(fFormatParameters)); } @@ -48,7 +52,8 @@ AVFormatReader::~AVFormatReader() { TRACE("AVFormatReader::~AVFormatReader\n"); - // TODO: Deallocate fContext + av_free(fContext); + free(fIOBuffer); } @@ -68,16 +73,17 @@ AVFormatReader::Sniff(int32* streamCount) { TRACE("AVFormatReader::Sniff\n"); - size_t bufferSize = 64 * 1024; + free(fIOBuffer); + fIOBuffer = (uint8*)malloc(kIOBufferSize); + size_t probeSize = 1024; - uint8 buffer[bufferSize]; AVProbeData probeData; probeData.filename = ""; - probeData.buf = buffer; + probeData.buf = fIOBuffer; probeData.buf_size = probeSize; // Read a bit of the input... - if (_ReadPacket(Source(), buffer, probeSize) != (ssize_t)probeSize) + if (_ReadPacket(Source(), fIOBuffer, probeSize) != (ssize_t)probeSize) return B_IO_ERROR; // ...and seek back to the beginning of the file. _Seek(Source(), 0, SEEK_SET); @@ -93,20 +99,15 @@ AVFormatReader::Sniff(int32* streamCount) TRACE("AVFormatReader::Sniff() - av_probe_input_format(): %s\n", inputFormat->name); - ByteIOContext ioContext; - - // Init io module for input - if (init_put_byte(&ioContext, buffer, bufferSize, 0, Source(), + // Init I/O context with buffer and hook functions + if (init_put_byte(&fIOContext, fIOBuffer, kIOBufferSize, 0, Source(), _ReadPacket, 0, _Seek) != 0) { TRACE("AVFormatReader::Sniff() - init_put_byte() failed!\n"); return B_ERROR; } - AVFormatParameters formatParameters; - memset(&formatParameters, 0, sizeof(formatParameters)); - - if (av_open_input_stream(&fContext, &ioContext, "", inputFormat, - &formatParameters) < 0) { + if (av_open_input_stream(&fContext, &fIOContext, "", inputFormat, + &fFormatParameters) < 0) { TRACE("AVFormatReader::Sniff() - av_open_input_stream() failed!\n"); return B_ERROR; } @@ -122,7 +123,11 @@ AVFormatReader::Sniff(int32* streamCount) // Dump information about stream onto standard error dump_format(fContext, 0, "", 0); - return B_ERROR; + if (streamCount != NULL) + *streamCount = fContext->nb_streams; + +// return B_OK; +return B_ERROR; // For now... } @@ -131,16 +136,41 @@ AVFormatReader::GetFileFormatInfo(media_file_format* mff) { TRACE("AVFormatReader::GetFileFormatInfo\n"); + if (fContext == NULL || fContext->iformat == NULL) { + TRACE(" no context or AVInputFormat!\n"); + return; + } + mff->capabilities = media_file_format::B_READABLE | media_file_format::B_KNOWS_ENCODED_VIDEO | media_file_format::B_KNOWS_ENCODED_AUDIO | media_file_format::B_IMPERFECTLY_SEEKABLE; mff->family = B_MISC_FORMAT_FAMILY; mff->version = 100; - strcpy(mff->mime_type, "video/mpg"); - strcpy(mff->file_extension, "mpg"); - strcpy(mff->short_name, "MPEG"); - strcpy(mff->pretty_name, "MPEG (Motion Picture Experts Group)"); + strcpy(mff->mime_type, ""); + // TODO: Would be nice to be able to provide this, maybe by extending + // the FFmpeg code itself (all demuxers, see AVInputFormat struct). + + if (fContext->iformat->extensions != NULL) + strcpy(mff->file_extension, fContext->iformat->extensions); + else { + TRACE(" no file extensions for AVInputFormat.\n"); + strcpy(mff->file_extension, ""); + } + + if (fContext->iformat->name != NULL) + strcpy(mff->short_name, fContext->iformat->name); + else { + TRACE(" no short name for AVInputFormat.\n"); + strcpy(mff->short_name, ""); + } + + if (fContext->iformat->long_name != NULL) + strcpy(mff->pretty_name, fContext->iformat->long_name); + else { + TRACE(" no long name for AVInputFormat.\n"); + strcpy(mff->pretty_name, ""); + } } @@ -148,18 +178,132 @@ AVFormatReader::GetFileFormatInfo(media_file_format* mff) status_t -AVFormatReader::AllocateCookie(int32 streamNumber, void** _cookie) +AVFormatReader::AllocateCookie(int32 streamIndex, void** _cookie) { - TRACE("AVFormatReader::AllocateCookie(%ld)\n", streamNumber); + TRACE("AVFormatReader::AllocateCookie(%ld)\n", streamIndex); - return B_ERROR; + if (fContext == NULL) + return B_NO_INIT; + + if (streamIndex < 0 || streamIndex >= (int32)fContext->nb_streams) + return B_BAD_INDEX; + + if (_cookie == NULL) + return B_BAD_VALUE; + + StreamCookie* cookie = new(std::nothrow) StreamCookie; + if (cookie == NULL) + return B_NO_MEMORY; + + // Get a pointer to the codec context for the stream at sreamIndex. + AVCodecContext* codecContext = fContext->streams[streamIndex]->codec; + AVCodec* codec = avcodec_find_decoder(codecContext->codec_id); + if (codec == NULL || avcodec_open(codecContext, codec) < 0) { + delete cookie; + return B_ERROR; + } + +// codecContext->get_buffer = _GetBuffer; +// codecContext->release_buffer = _ReleaseBuffer; + + AVStream* stream = fContext->streams[streamIndex]; + + cookie->stream = stream; + cookie->codecContext = codecContext; + cookie->codec = codec; + + media_format* format = &cookie->format; + memset(format, 0, sizeof(media_format)); + + BMediaFormats formats; + media_format_description description; + + if (stream->codec->codec_type == CODEC_TYPE_VIDEO) { + // TODO: Fix this up! Maybe do this for AVI demuxer and MOV + // demuxer and use B_MISC_FORMAT_FAMILY for all the others? + description.family = B_AVI_FORMAT_FAMILY; + description.u.avi.codec = codecContext->codec_tag; + TRACE(" fourcc '%.4s'\n", (char*)&codecContext->codec_tag); + + if (formats.GetFormatFor(description, format) < B_OK) + format->type = B_MEDIA_ENCODED_VIDEO; + +// format->require_flags = +// format->deny_flags = + + format->user_data_type = B_CODEC_TYPE_INFO; + *(uint32*)format->user_data = codecContext->codec_tag; + format->user_data[4] = 0; + + // TODO: We don't actually know the bitrate for this stream, + // only the total bitrate! + format->u.encoded_video.avg_bit_rate = codecContext->bit_rate; + format->u.encoded_video.max_bit_rate = codecContext->bit_rate + + codecContext->bit_rate_tolerance; + + format->u.encoded_video.encoding = media_encoded_video_format::B_ANY; + + format->u.encoded_video.frame_size = 1; +// format->u.encoded_video.forward_history = 0; +// format->u.encoded_video.backward_history = 0; + + format->u.encoded_video.output.field_rate + = av_q2d(stream->r_frame_rate); + format->u.encoded_video.output.interlace = 1; + // TODO: Fix up for interlaced video + format->u.encoded_video.output.first_active = 0; + format->u.encoded_video.output.last_active = 0; + // TODO: Maybe libavformat actually provides that info somewhere... + format->u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT; + + // TODO: Implement aspect ratio for real + format->u.encoded_video.output.pixel_width_aspect + = 1;//stream->sample_aspect_ratio.num; + format->u.encoded_video.output.pixel_height_aspect + = 1;//stream->sample_aspect_ratio.den; + + TRACE(" pixel width/height aspect: %d/%d or %.4f\n", + stream->sample_aspect_ratio.num, + stream->sample_aspect_ratio.den, + av_q2d(stream->sample_aspect_ratio)); + + format->u.encoded_video.output.display.format + = pixfmt_to_colorspace(codecContext->pix_fmt); + format->u.encoded_video.output.display.line_width = codecContext->width; + format->u.encoded_video.output.display.line_count = codecContext->height; + format->u.encoded_video.output.display.bytes_per_row = 0; + format->u.encoded_video.output.display.pixel_offset = 0; + format->u.encoded_video.output.display.line_offset = 0; + format->u.encoded_video.output.display.flags = 0; // TODO + + uint32 encoding = format->Encoding(); + TRACE(" encoding '%.4s'\n", (char*)&encoding); + + } else if (stream->codec->codec_type == CODEC_TYPE_AUDIO) { + format->type = B_MEDIA_ENCODED_AUDIO; +// format->require_flags = +// format->deny_flags = + +// format->u.encoded_audio. + } else { + return B_NOT_SUPPORTED; + } + + *_cookie = cookie; + + return B_OK; } status_t AVFormatReader::FreeCookie(void *_cookie) { - return B_ERROR; + StreamCookie* cookie = reinterpret_cast(_cookie); + + avcodec_close(cookie->codecContext); + delete cookie; + + return B_OK; } @@ -171,7 +315,35 @@ AVFormatReader::GetStreamInfo(void* _cookie, int64* frameCount, bigtime_t* duration, media_format* format, const void** infoBuffer, size_t* infoSize) { - return B_ERROR; + TRACE("AVFormatReader::GetStreamInfo()\n"); + + StreamCookie* cookie = reinterpret_cast(_cookie); + AVStream* stream = cookie->stream; + + double frameRate = av_q2d(stream->r_frame_rate); + + TRACE(" frameRate: %.4f\n", frameRate); + + *duration = (bigtime_t)(1000000LL * stream->duration + * av_q2d(stream->time_base)); + + TRACE(" duration: %lld\n", *duration); + + *frameCount = stream->nb_frames; + if (*frameCount == 0) { + // TODO: Calculate from duration and frame rate! + *frameCount = (int64)(*duration * frameRate / 1000000); + } + + TRACE(" frameCount: %lld\n", *frameCount); + + *format = cookie->format; + + // TODO: Possibly use stream->metadata for this: + *infoBuffer = 0; + *infoSize = 0; + + return B_OK; } diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h index e1b7d4d3b8..478d79489f 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatReader.h @@ -8,8 +8,9 @@ #include "ReaderPlugin.h" - -struct AVFormatContext; +extern "C" { + #include "avformat.h" +} class AVFormatReader : public Reader { @@ -49,6 +50,20 @@ private: off_t offset, int whence); AVFormatContext* fContext; + AVFormatParameters fFormatParameters; + ByteIOContext fIOContext; + uint8* fIOBuffer; + + struct StreamCookie { + AVStream* stream; + AVCodecContext* codecContext; + AVCodec* codec; + media_format format; + // TODO: Maybe we don't need the codec after all, maybe we do + // for getting stream information... + // TODO: Some form of packet queue + }; + }; diff --git a/src/add-ons/media/plugins/ffmpeg/gfx_util.cpp b/src/add-ons/media/plugins/ffmpeg/gfx_util.cpp index 6af7bd0848..ef8bc53c65 100644 --- a/src/add-ons/media/plugins/ffmpeg/gfx_util.cpp +++ b/src/add-ons/media/plugins/ffmpeg/gfx_util.cpp @@ -146,7 +146,8 @@ gfx_convert_func resolve_colorspace(color_space colorSpace, PixelFormat pixelFor } } -const char *pixfmt_to_string(int p) +const char* +pixfmt_to_string(int p) { switch(p) { case PIX_FMT_NONE: return "PIX_FMT_NONE"; @@ -205,6 +206,69 @@ const char *pixfmt_to_string(int p) } } + +color_space +pixfmt_to_colorspace(int p) +{ + switch(p) { + default: + case PIX_FMT_NONE: + return B_NO_COLOR_SPACE; + + case PIX_FMT_YUV420P: return B_YUV420; ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + case PIX_FMT_YUYV422: return B_YUV422; ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + case PIX_FMT_RGB24: return B_RGB24_BIG; ///< packed RGB 8:8:8, 24bpp, RGBRGB... + case PIX_FMT_BGR24: return B_RGB24; ///< packed RGB 8:8:8, 24bpp, BGRBGR... + case PIX_FMT_YUV422P: return B_YUV422; ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + case PIX_FMT_YUV444P: return B_YUV444; ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) + case PIX_FMT_RGB32: return B_RGBA32_BIG; ///< packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in CPU endianness + case PIX_FMT_YUV410P: return B_YUV9; ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + case PIX_FMT_YUV411P: return B_YUV12; ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) + case PIX_FMT_RGB565: return B_RGB16_BIG; ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), in CPU endianness + case PIX_FMT_RGB555: return B_RGB15_BIG; ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in CPU endianness, most significant bit to 0 + case PIX_FMT_GRAY8: return B_GRAY8; ///< Y , 8bpp +// case PIX_FMT_MONOWHITE: return B_GRAY1; ///< Y , 1bpp, 0 is white, 1 is black + case PIX_FMT_MONOBLACK: return B_GRAY1; ///< Y , 1bpp, 0 is black, 1 is white + case PIX_FMT_PAL8: return B_CMAP8; ///< 8 bit with PIX_FMT_RGB32 palette +// case PIX_FMT_YUVJ420P: return "PIX_FMT_YUVJ420P - YUV420P (Jpeg)"; ///< planar YUV 4:2:0, 12bpp, full scale (JPEG) +// case PIX_FMT_YUVJ422P: return "PIX_FMT_YUVJ422P - YUV422P (Jpeg)"; ///< planar YUV 4:2:2, 16bpp, full scale (JPEG) +// case PIX_FMT_YUVJ444P: return "PIX_FMT_YUVJ444P"; ///< planar YUV 4:4:4, 24bpp, full scale (JPEG) +// case PIX_FMT_XVMC_MPEG2_MC: return "PIX_FMT_XVMC_MPEG2_MC";///< XVideo Motion Acceleration via common packet passing +// case PIX_FMT_XVMC_MPEG2_IDCT: return "PIX_FMT_XVMC_MPEG2_IDCT"; +// case PIX_FMT_UYVY422: return "PIX_FMT_UYVY422"; ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 +// case PIX_FMT_UYYVYY411: return "PIX_FMT_UYYVYY411"; ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 + case PIX_FMT_BGR32: return B_RGB32; ///< packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in CPU endianness + case PIX_FMT_BGR565: return B_RGB16; ///< packed RGB 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), in CPU endianness + case PIX_FMT_BGR555: return B_RGB15; ///< packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in CPU endianness, most significant bit to 1 +// case PIX_FMT_BGR8: return "PIX_FMT_BGR8"; ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) +// case PIX_FMT_BGR4: return "PIX_FMT_BGR4"; ///< packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb) +// case PIX_FMT_BGR4_BYTE: return "PIX_FMT_BGR4_BYTE"; ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) +// case PIX_FMT_RGB8: return "PIX_FMT_RGB8"; ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) +// case PIX_FMT_RGB4: return "PIX_FMT_RGB4"; ///< packed RGB 1:2:1, 4bpp, (msb)1R 2G 1B(lsb) +// case PIX_FMT_RGB4_BYTE: return "PIX_FMT_RGB4_BYTE"; ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) +// case PIX_FMT_NV12: return "PIX_FMT_NV12"; ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV +// case PIX_FMT_NV21: return "PIX_FMT_NV21"; ///< as above, but U and V bytes are swapped +// case PIX_FMT_RGB32_1: return "PIX_FMT_RGB32_1"; ///< packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in CPU endianness +// case PIX_FMT_BGR32_1: return "PIX_FMT_BGR32_1"; ///< packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in CPU endianness +// case PIX_FMT_GRAY16BE: return "PIX_FMT_GRAY16BE"; ///< Y , 16bpp, big-endian +// case PIX_FMT_GRAY16LE: return "PIX_FMT_GRAY16LE"; ///< Y , 16bpp, little-endian +// case PIX_FMT_YUV440P: return "PIX_FMT_YUV440P"; ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) +// case PIX_FMT_YUVJ440P: return "PIX_FMT_YUVJ440P - YUV440P (Jpeg)"; ///< planar YUV 4:4:0 full scale (JPEG) +// case PIX_FMT_YUVA420P: return "PIX_FMT_YUVA420P - YUV420P (Alpha)"; ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) +// case PIX_FMT_VDPAU_H264: return "PIX_FMT_VDPAU_H264";///< H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +// case PIX_FMT_VDPAU_MPEG1: return "PIX_FMT_VDPAU_MPEG1";///< MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +// case PIX_FMT_VDPAU_MPEG2: return "PIX_FMT_VDPAU_MPEG2";///< MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +// case PIX_FMT_VDPAU_WMV3: return "PIX_FMT_VDPAU_WMV3";///< WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +// case PIX_FMT_VDPAU_VC1: return "PIX_FMT_VDPAU_VC1"; ///< VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +// case PIX_FMT_RGB48BE: return "PIX_FMT_RGB48BE"; ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, big-endian +// case PIX_FMT_RGB48LE: return "PIX_FMT_RGB48LE"; ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, little-endian +// case PIX_FMT_VAAPI_MOCO: return "PIX_FMT_VAAPI_MOCO"; ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[0] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers +// case PIX_FMT_VAAPI_IDCT: return "PIX_FMT_VAAPI_IDCT"; ///< HW acceleration through VA API at IDCT entry-point, Picture.data[0] contains a vaapi_render_state struct which contains fields extracted from headers +// case PIX_FMT_VAAPI_VLD: return "PIX_FMT_VAAPI_VLD"; ///< HW decoding through VA API, Picture.data[0] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + } +} + + #define BEGIN_TAG "\033[31m" #define END_TAG "\033[0m" diff --git a/src/add-ons/media/plugins/ffmpeg/gfx_util.h b/src/add-ons/media/plugins/ffmpeg/gfx_util.h index 9e4e0e95e7..abf7851162 100644 --- a/src/add-ons/media/plugins/ffmpeg/gfx_util.h +++ b/src/add-ons/media/plugins/ffmpeg/gfx_util.h @@ -36,6 +36,8 @@ gfx_convert_func resolve_colorspace(color_space cs, PixelFormat pixelFormat); const char *pixfmt_to_string(int p); +color_space pixfmt_to_colorspace(int p); + void dump_ffframe(AVFrame *frame, const char *name); #endif