VirtualBox

Changeset 41953 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jun 28, 2012 10:22:52 AM (12 years ago)
Author:
vboxsync
Message:

VideoCapture: Support for Video capturing using libvpx library i.e using VP8 encoder.

Location:
trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.cpp

    r33540 r41953  
    7676 * it is called.
    7777 */
     78#ifdef VBOX_WITH_VPX
    7879FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate,
    7980                   com::Bstr filename) :
     
    8687    mFrameWidth(width), mFrameHeight(height),
    8788    mYUVFrameSize(width * height * 3 / 2),
    88     mRGBBuffer(0), mpFormatContext(0), mpStream(0),
     89    mRGBBuffer(0),
    8990    mOutOfMemory(false), mToggle(false)
     91#else
     92FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate,
     93                   com::Bstr filename) :
     94    mpFormatContext(0), mpStream(0),
     95    mfUrlOpen(false),
     96    mBitRate(bitrate),
     97    mPixelFormat(FramebufferPixelFormat_Opaque),
     98    mBitsPerPixel(0),
     99    mFileName(filename),
     100    mBytesPerLine(0),
     101    mFrameWidth(width), mFrameHeight(height),
     102    mYUVFrameSize(width * height * 3 / 2),mRGBBuffer(0),
     103    mOutOfMemory(false), mToggle(false)
     104#endif
    90105{
    91106    ULONG cPixels = width * height;
    92107
     108#ifdef VBOX_WITH_VPX
     109    Assert(width % 2 == 0 && height % 2 == 0);
     110    /* For temporary RGB frame we allocate enough memory to deal with
     111       RGB16 to RGB32 */
     112    mTempRGBBuffer = reinterpret_cast<uint8_t *>(malloc(cPixels * 4));
     113    if (mTempRGBBuffer == 0)
     114        goto nomem_temp_rgb_buffer;
     115    mYUVBuffer = reinterpret_cast<uint8_t *>(malloc(mYUVFrameSize));
     116    if (mYUVBuffer == 0)
     117        goto nomem_yuv_buffer;
     118    return;
     119
     120    /* C-based memory allocation and how to deal with it in C++ :) */
     121nomem_yuv_buffer:
     122    Log(("Failed to allocate memory for mYUVBuffer\n"));
     123    free(mYUVBuffer);
     124nomem_temp_rgb_buffer:
     125    Log(("Failed to allocate memory for mTempRGBBuffer\n"));
     126    free(mTempRGBBuffer);
     127    mOutOfMemory = true;
     128#else
    93129    LogFlow(("Creating FFmpegFB object %p, width=%lu, height=%lu\n",
    94130             this, (unsigned long) width,  (unsigned long) height));
     
    124160    Log(("Failed to allocate memory for mTempRGBBuffer\n"));
    125161    mOutOfMemory = true;
     162#endif
    126163}
    127164
     
    133170{
    134171    LogFlow(("Destroying FFmpegFB object %p\n", this));
     172#ifdef VBOX_WITH_VPX
     173        /* Dummy update to make sure we get all the frame (timing). */
     174    NotifyUpdate(0, 0, 0, 0);
     175    /* Write the last pending frame before exiting */
     176    int rc = do_rgb_to_yuv_conversion();
     177    if (rc == S_OK)
     178                do_encoding_and_write();
     179# if 1
     180    /* Add another 10 seconds. */
     181    for (int i = 10*25; i > 0; i--)
     182                do_encoding_and_write();
     183# endif
     184    Ebml_WriteWebMFileFooter(&ebml, 0);
     185        if(ebml.stream)
     186            fclose(ebml.stream);
     187    vpx_codec_destroy(&mVpxCodec);
     188    RTCritSectDelete(&mCritSect);
     189
     190        /* We have already freed the stream above */
     191    if (mTempRGBBuffer != 0)
     192        free(mTempRGBBuffer);
     193    if (mYUVBuffer != 0)
     194        free(mYUVBuffer);
     195    if (mRGBBuffer != 0)
     196        RTMemFree(mRGBBuffer);
     197#else
    135198    if (mpFormatContext != 0)
    136199    {
     
    143206            if (rc == S_OK)
    144207                do_encoding_and_write();
    145 #if 1
     208# if 1
    146209            /* Add another 10 seconds. */
    147210            for (int i = 10*25; i > 0; i--)
    148211                do_encoding_and_write();
    149 #endif
     212# endif
    150213            /* write a png file of the last frame */
    151214            write_png();
     
    158221            }
    159222/* Changed sometime between 50.5.0 and 52.7.0 */
    160 #if LIBAVFORMAT_VERSION_INT >= (52 << 16)
     223# if LIBAVFORMAT_VERSION_INT >= (52 << 16)
    161224            url_fclose(mpFormatContext->pb);
    162 #else /* older version */
     225# else /* older version */
    163226            url_fclose(&mpFormatContext->pb);
    164 #endif /* older version */
     227# endif /* older version */
    165228        }
    166229        av_free(mpFormatContext);
     
    179242    if (mRGBBuffer != 0)
    180243        RTMemFree(mRGBBuffer);
     244#endif
    181245}
    182246
     
    201265    if (mOutOfMemory == true)
    202266        return E_OUTOFMEMORY;
    203     int rc = RTCritSectInit(&mCritSect);
     267        int rc;
     268        int rcOpenFile;
     269        int rcOpenCodec;
     270
     271#ifdef VBOX_WITH_VPX
     272        mFrameCount = 0;
     273    memset(&ebml, 0, sizeof(struct EbmlGlobal));
     274    ebml.last_pts_ms = -1;
     275    rc = RTCritSectInit(&mCritSect);
     276    AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED);
     277    rcOpenFile = open_output_file();
     278    AssertReturn(rcOpenFile == S_OK, rcOpenFile);
     279    rcOpenCodec = open_codec();
     280    AssertReturn(rcOpenCodec == S_OK, rcOpenCodec);
     281#else
     282    rc = RTCritSectInit(&mCritSect);
    204283    AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED);
    205284    int rcSetupLibrary = setup_library();
     
    207286    int rcSetupFormat = setup_output_format();
    208287    AssertReturn(rcSetupFormat == S_OK, rcSetupFormat);
    209     int rcOpenCodec = open_codec();
     288    rcOpenCodec = open_codec();
    210289    AssertReturn(rcOpenCodec == S_OK, rcOpenCodec);
    211     int rcOpenFile = open_output_file();
     290    rcOpenFile = open_output_file();
    212291    AssertReturn(rcOpenFile == S_OK, rcOpenFile);
     292
    213293    /* Fill in the picture data for the AVFrame - not particularly
    214294       elegant, but that is the API. */
    215295    avpicture_fill((AVPicture *) mFrame, mYUVBuffer, PIX_FMT_YUV420P,
    216296                   mFrameWidth, mFrameHeight);
     297#endif
     298
    217299    /* Set the initial framebuffer size to the mpeg frame dimensions */
    218300    BOOL finished;
     
    536618        switch (bitsPerPixel)
    537619        {
     620#ifdef VBOX_WITH_VPX
     621            case 32:
     622                mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32;
     623                Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n"));
     624                break;
     625            case 24:
     626                mFFMPEGPixelFormat = VPX_IMG_FMT_RGB24;
     627                Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB24\n"));
     628                break;
     629            case 16:
     630                mFFMPEGPixelFormat = VPX_IMG_FMT_RGB565;
     631                Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB565\n"));
     632                break;
     633#else
    538634            case 32:
    539635                mFFMPEGPixelFormat = PIX_FMT_RGBA32;
     
    548644                Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGB565\n"));
    549645                break;
     646#endif
    550647            default:
    551648                fallback = true;
     
    572669        /* we always fallback to 32bpp RGB */
    573670        mPixelFormat = FramebufferPixelFormat_FOURCC_RGB;
     671#ifdef VBOX_WITH_VPX
     672        mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32;
     673        Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n"));
     674#else
    574675        mFFMPEGPixelFormat = PIX_FMT_RGBA32;
    575676        Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGBA32\n"));
     677#endif
    576678
    577679        mBytesPerLine = w * 4;
     
    637739//////////////////////////////////////////////////////////////////////////
    638740//
    639 
     741#ifndef VBOX_WITH_VPX
    640742HRESULT FFmpegFB::setup_library()
    641743{
     
    669771    AVOutputFormat *pOutFormat = guess_format(0, com::Utf8Str(mFileName).c_str(),
    670772                                              0);
    671 #ifdef VBOX_SHOW_AVAILABLE_FORMATS
     773# ifdef VBOX_SHOW_AVAILABLE_FORMATS
    672774    if (!pOutFormat)
    673775    {
     
    676778        list_formats();
    677779    }
    678 #endif
     780# endif
    679781    AssertMsgReturn(pOutFormat != 0,
    680782                    ("Could not deduce output format from file name\n"),
     
    692794    int rcSetParam = av_set_parameters(mpFormatContext, 0);
    693795    AssertReturn(rcSetParam >= 0, E_UNEXPECTED);
    694 #if 1 /* bird: This works for me on the mac, please review & test elsewhere. */
     796# if 1 /* bird: This works for me on the mac, please review & test elsewhere. */
    695797    /* Fill in any uninitialized parameters like opt_output_file in ffpmeg.c does.
    696798       This fixes most of the buffer underflow warnings:
     
    700802    if (!mpFormatContext->max_delay)
    701803        mpFormatContext->max_delay = (int)(0.7 * AV_TIME_BASE);
    702 #endif
     804# endif
    703805    return S_OK;
    704806}
     
    722824    return S_OK;
    723825}
    724 
     826#endif
    725827
    726828/**
     
    734836HRESULT FFmpegFB::open_codec()
    735837{
     838#ifdef VBOX_WITH_VPX
     839        vpx_codec_err_t      res;
     840    /* Populate encoder configuration */
     841    if ((res = vpx_codec_enc_config_default(interface, &mVpxConfig, 0)))
     842        {
     843                LogFlow(("Failed to configure codec \n"));
     844        AssertReturn(res == 0, E_UNEXPECTED);
     845        }
     846
     847    mVpxConfig.rc_target_bitrate = 512;
     848    mVpxConfig.g_w = mFrameWidth;
     849    mVpxConfig.g_h = mFrameHeight;
     850    mVpxConfig.g_timebase.den = 30;
     851    mVpxConfig.g_timebase.num = 1;
     852        mVpxConfig.g_threads = 8;
     853
     854    vpx_rational ebmlFPS = mVpxConfig.g_timebase;
     855    struct vpx_rational arg_framerate = {30, 1};
     856    Ebml_WriteWebMFileHeader(&ebml, &mVpxConfig, &arg_framerate);
     857    mDuration = (float)arg_framerate.den / (float)arg_framerate.num * 1000;
     858
     859    /* Initialize codec */
     860    if (vpx_codec_enc_init(&mVpxCodec, interface, &mVpxConfig, 0))
     861    {
     862        LogFlow(("Failed to initialize encoder"));
     863        AssertReturn(res == 0, E_UNEXPECTED);
     864    }
     865#else
    736866    Assert(mpFormatContext != 0);
    737867    Assert(mpStream != 0);
     
    740870    AssertReturn(pCodecContext != 0, E_UNEXPECTED);
    741871    AVCodec *pCodec = avcodec_find_encoder(pOutFormat->video_codec);
    742 #ifdef VBOX_SHOW_AVAILABLE_FORMATS
     872# ifdef VBOX_SHOW_AVAILABLE_FORMATS
    743873    if (!pCodec)
    744874    {
     
    747877        list_formats();
    748878    }
    749 #endif
     879# endif
    750880    AssertReturn(pCodec != 0, E_UNEXPECTED);
    751881    pCodecContext->codec_id = pOutFormat->video_codec;
     
    768898    int rcOpenCodec = avcodec_open(pCodecContext, pCodec);
    769899    AssertReturn(rcOpenCodec >= 0, E_UNEXPECTED);
     900#endif
    770901    return S_OK;
    771902}
     
    782913HRESULT FFmpegFB::open_output_file()
    783914{
     915#ifdef VBOX_WITH_VPX
     916    char szFileName[RTPATH_MAX];
     917    strcpy(szFileName, com::Utf8Str(mFileName).c_str());
     918        ebml.stream = fopen(szFileName, "wb");
     919        if (!ebml.stream)
     920        {
     921                LogFlow(("Failed to open the output File \n"));
     922        return E_FAIL;
     923        }
     924        return S_OK;
     925#else
    784926    char szFileName[RTPATH_MAX];
    785927    Assert(mpFormatContext);
     
    791933    mfUrlOpen = true;
    792934    av_write_header(mpFormatContext);
     935#endif
    793936    return S_OK;
    794937}
     
    8911034    switch (mFFMPEGPixelFormat)
    8921035    {
     1036#ifdef VBOX_WITH_VPX
     1037        case VPX_IMG_FMT_RGB32:
     1038            if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight,
     1039                                                      mYUVBuffer, mTempRGBBuffer))
     1040                return E_UNEXPECTED;
     1041            break;
     1042        case VPX_IMG_FMT_RGB24:
     1043            if (!FFmpegWriteYUV420p<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight,
     1044                                                     mYUVBuffer, mTempRGBBuffer))
     1045                return E_UNEXPECTED;
     1046            break;
     1047        case VPX_IMG_FMT_RGB565:
     1048            if (!FFmpegWriteYUV420p<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight,
     1049                                                      mYUVBuffer, mTempRGBBuffer))
     1050                return E_UNEXPECTED;
     1051            break;
     1052#else
    8931053        case PIX_FMT_RGBA32:
    8941054            if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight,
     
    9061066                return E_UNEXPECTED;
    9071067            break;
     1068
     1069#endif
    9081070        default:
    9091071            return E_UNEXPECTED;
     
    9121074}
    9131075
    914 
    9151076/**
    9161077 * Encode the YUV framebuffer as an MPEG frame and write it to the file.
     
    9201081HRESULT FFmpegFB::do_encoding_and_write()
    9211082{
    922     AVCodecContext *pContext = mpStream->codec;
     1083
    9231084
    9241085    /* A hack: ffmpeg mpeg2 only writes a frame if something has
     
    9301091        mYUVBuffer[0] &= 0xfe;
    9311092    mToggle = !mToggle;
     1093
     1094#ifdef VBOX_WITH_VPX
     1095    vpx_image_t          VpxRawImage;
     1096    vpx_codec_err_t      res;
     1097    const vpx_codec_cx_pkt_t *pkt;
     1098    vpx_codec_iter_t iter = NULL;
     1099
     1100    if (mFrameWidth < 16 || mFrameWidth%2 || mFrameHeight <16 || mFrameHeight%2)
     1101        LogFlow(("Invalid resolution: %ldx%ld", mFrameWidth, mFrameHeight));
     1102
     1103    if (!vpx_img_alloc(&VpxRawImage, VPX_IMG_FMT_YV12, mFrameWidth, mFrameHeight, 1))
     1104    {
     1105        LogFlow(("Faile tod allocate image", mFrameWidth, mFrameHeight));
     1106        return E_OUTOFMEMORY;
     1107    }
     1108
     1109    if (mYUVBuffer != NULL)
     1110    {
     1111                AssertReturn(VpxRawImage.w*VpxRawImage.h*3/2 <= sizeof(mYUVFrameSize), E_UNEXPECTED);
     1112                memcpy(VpxRawImage.planes[0], (uint8_t *)mYUVBuffer, VpxRawImage.w*VpxRawImage.h*3/2);
     1113    }
     1114
     1115        if ((res = vpx_codec_encode(&mVpxCodec, &VpxRawImage , mFrameCount,
     1116                  mDuration, 0, VPX_DL_REALTIME)))
     1117        {
     1118        LogFlow(("Failed to encode: %s\n", vpx_codec_err_to_string(res)));
     1119                AssertReturn(res != 0, E_UNEXPECTED);
     1120
     1121        }
     1122    while ((pkt = vpx_codec_get_cx_data(&mVpxCodec, &iter)))
     1123    {
     1124        switch (pkt->kind)
     1125        {
     1126            case VPX_CODEC_CX_FRAME_PKT:
     1127                Ebml_WriteWebMBlock(&ebml, &mVpxConfig, pkt);
     1128                break;
     1129            default:
     1130                break;
     1131        }
     1132   }
     1133   mFrameCount++;
     1134#else
     1135    AVCodecContext *pContext = mpStream->codec;
    9321136    int cSize = avcodec_encode_video(pContext, mOutBuf, mYUVFrameSize * 2,
    9331137                                  mFrame);
     
    9531157        AssertReturn(rcWriteFrame == 0, E_UNEXPECTED);
    9541158    }
    955     return S_OK;
    956 }
    957 
    958 
     1159#endif
     1160    return S_OK;
     1161}
     1162
     1163#ifndef VBOX_WITH_VPX
    9591164/**
    9601165 * Capture the current (i.e. the last) frame as a PNG file with the
     
    10631268    return errorCode;
    10641269}
    1065 
     1270#endif
    10661271
    10671272#ifdef VBOX_WITH_XPCOM
  • trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.h

    r41216 r41953  
    2121
    2222#include <VBox/com/VirtualBox.h>
    23 
     23#include "EbmlWriter.h"
    2424#include <iprt/uuid.h>
    2525
     
    3030#include <iprt/critsect.h>
    3131
    32 #ifdef DEBUG
    33 # define VBOX_DEBUG_FF DEBUG
    34 # include <avcodec.h>
    35 # include <avformat.h>
    36 # undef  DEBUG
    37 # define DEBUG VBOX_DEBUG_FF
    38 #else /* DEBUG not defined */
    39 # include <avcodec.h>
    40 # include <avformat.h>
    41 #endif /* DEBUG not defined */
     32#ifdef VBOX_WITH_VPX
     33#include <stdarg.h>
     34#include <string.h>
     35#define VPX_CODEC_DISABLE_COMPAT 1
     36#include <vp8cx.h>
     37#include <vpx_image.h>
     38#include <vpx_mem.h>
     39#define interface (vpx_codec_vp8_cx())
     40#else
     41# ifdef DEBUG
     42#  define VBOX_DEBUG_FF DEBUG
     43#  include <avcodec.h>
     44#  include <avformat.h>
     45#  undef  DEBUG
     46#  define DEBUG VBOX_DEBUG_FF
     47# else /* DEBUG not defined */
     48#  include <avcodec.h>
     49#  include <avformat.h>
     50# endif /* DEBUG not defined */
     51#endif
     52
    4253
    4354class FFmpegFB : VBOX_SCRIPTABLE_IMPL(IFramebuffer)
     
    89100
    90101    STDMETHOD(ProcessVHWACommand)(BYTE *pCommand);
    91 
    92 private:
     102public:
     103private:
     104#ifdef VBOX_WITH_VPX
     105        EbmlGlobal ebml;
     106        vpx_codec_ctx_t      mVpxCodec;
     107    vpx_codec_enc_cfg_t  mVpxConfig;
     108        FILE * mOutputFile;
     109        unsigned long mDuration;
     110        uint32_t  mFrameCount;
     111
     112#else
     113    /** Pointer to ffmpeg's format information context */
     114    AVFormatContext *mpFormatContext;
     115    /** ffmpeg context containing information about the stream */
     116    AVStream *mpStream;
     117    /** Information for ffmpeg describing the current frame */
     118    AVFrame *mFrame;
     119
     120        HRESULT setup_library();
     121    HRESULT setup_output_format();
     122    HRESULT list_formats();
     123#endif
    93124    /** true if url_fopen actually succeeded */
    94125    bool mfUrlOpen;
     
    131162    /** time at which the last "real" frame was created */
    132163    int64_t mLastTime;
    133     /** Pointer to ffmpeg's format information context */
    134     AVFormatContext *mpFormatContext;
    135     /** ffmpeg context containing information about the stream */
    136     AVStream *mpStream;
    137     /** Information for ffmpeg describing the current frame */
    138     AVFrame *mFrame;
    139164    /** ffmpeg pixel format of guest framebuffer */
    140165    int mFFMPEGPixelFormat;
     
    147172    bool mToggle;
    148173
    149     HRESULT setup_library();
    150     HRESULT setup_output_format();
    151     HRESULT list_formats();
     174
    152175    HRESULT open_codec();
    153176    HRESULT open_output_file();
  • trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture/Makefile.kmk

    r41477 r41953  
    2121DLLS += VBoxFFmpegFB
    2222VBoxFFmpegFB_TEMPLATE        = VBOXMAINCLIENTDLL
    23 VBoxFFmpegFB_SDKS            = VBOX_FFMPEG VBOX_LIBPNG VBOX_ZLIB
    24 VBoxFFmpegFB_SOURCES         = FFmpegFB.cpp
     23#VBoxFFmpegFB_DEFS      += \
     24                                 VBOX_WITH_VPX 
     25VBoxFFmpegFB_SDKS            = VBOX_FFMPEG VBOX_LIBPNG VBOX_ZLIB VBOX_VPX
     26VBoxFFmpegFB_SOURCES         +=  \
     27                                                        FFmpegFB.cpp \
     28                                                        EbmlWriter.cpp
    2529VBoxFFmpegFB_CXXFLAGS.linux += -fPIC
    2630
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette