uint8_t * AndroidMediaReader::ImageBufferCallback::CreateI420Image(size_t aWidth, size_t aHeight) { mImage = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR); PlanarYCbCrImage *yuvImage = static_cast<PlanarYCbCrImage *>(mImage.get()); if (!yuvImage) { NS_WARNING("Could not create I420 image"); return nullptr; } size_t frameSize = aWidth * aHeight; // Allocate enough for one full resolution Y plane // and two quarter resolution Cb/Cr planes. uint8_t *buffer = yuvImage->AllocateAndGetNewBuffer(frameSize * 3 / 2); mozilla::layers::PlanarYCbCrData frameDesc; frameDesc.mYChannel = buffer; frameDesc.mCbChannel = buffer + frameSize; frameDesc.mCrChannel = buffer + frameSize * 5 / 4; frameDesc.mYSize = IntSize(aWidth, aHeight); frameDesc.mCbCrSize = IntSize(aWidth / 2, aHeight / 2); frameDesc.mYStride = aWidth; frameDesc.mCbCrStride = aWidth / 2; frameDesc.mYSkip = 0; frameDesc.mCbSkip = 0; frameDesc.mCrSkip = 0; frameDesc.mPicX = 0; frameDesc.mPicY = 0; frameDesc.mPicSize = IntSize(aWidth, aHeight); yuvImage->SetDataNoCopy(frameDesc); return buffer; }
int FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer( AVCodecContext* aCodecContext, AVFrame* aFrame) { bool needAlign = aCodecContext->codec->capabilities & CODEC_CAP_DR1; int edgeWidth = needAlign ? avcodec_get_edge_width() : 0; int decodeWidth = aCodecContext->width + edgeWidth * 2; // Make sure the decodeWidth is a multiple of 32, so a UV plane stride will be // a multiple of 16. FFmpeg uses SSE2 accelerated code to copy a frame line by // line. decodeWidth = (decodeWidth + 31) & ~31; int decodeHeight = aCodecContext->height + edgeWidth * 2; if (needAlign) { // Align width and height to account for CODEC_FLAG_EMU_EDGE. int stride_align[AV_NUM_DATA_POINTERS]; avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight, stride_align); } // Get strides for each plane. av_image_fill_linesizes(aFrame->linesize, aCodecContext->pix_fmt, decodeWidth); // Let FFmpeg set up its YUV plane pointers and tell us how much memory we // need. // Note that we're passing |nullptr| here as the base address as we haven't // allocated our image yet. We will adjust |aFrame->data| below. size_t allocSize = av_image_fill_pointers(aFrame->data, aCodecContext->pix_fmt, decodeHeight, nullptr /* base address */, aFrame->linesize); nsRefPtr<Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR); PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(image.get()); uint8_t* buffer = ycbcr->AllocateAndGetNewBuffer(allocSize + 64); // FFmpeg requires a 16/32 bytes-aligned buffer, align it on 64 to be safe buffer = reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(buffer) + 63) & ~63); if (!buffer) { NS_WARNING("Failed to allocate buffer for FFmpeg video decoding"); return -1; } // Now that we've allocated our image, we can add its address to the offsets // set by |av_image_fill_pointers| above. We also have to add |edgeWidth| // pixels of padding here. for (uint32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) { // The C planes are half the resolution of the Y plane, so we need to halve // the edge width here. uint32_t planeEdgeWidth = edgeWidth / (i ? 2 : 1); // Add buffer offset, plus a horizontal bar |edgeWidth| pixels high at the // top of the frame, plus |edgeWidth| pixels from the left of the frame. aFrame->data[i] += reinterpret_cast<ptrdiff_t>( buffer + planeEdgeWidth * aFrame->linesize[i] + planeEdgeWidth); } // Unused, but needs to be non-zero to keep ffmpeg happy. aFrame->type = GECKO_FRAME_TYPE; aFrame->extended_data = aFrame->data; aFrame->width = aCodecContext->width; aFrame->height = aCodecContext->height; aFrame->opaque = static_cast<void*>(image.forget().take()); return 0; }
int FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer( AVCodecContext* aCodecContext, AVFrame* aFrame) { bool needAlign = aCodecContext->codec->capabilities & CODEC_CAP_DR1; bool needEdge = !(aCodecContext->flags & CODEC_FLAG_EMU_EDGE); int edgeWidth = needEdge ? avcodec_get_edge_width() : 0; int decodeWidth = aCodecContext->width + edgeWidth * 2; int decodeHeight = aCodecContext->height + edgeWidth * 2; if (needAlign) { // Align width and height to account for CODEC_CAP_DR1. // Make sure the decodeWidth is a multiple of 64, so a UV plane stride will be // a multiple of 32. FFmpeg uses SSE3 accelerated code to copy a frame line by // line. // VP9 decoder uses MOVAPS/VEX.256 which requires 32-bytes aligned memory. decodeWidth = (decodeWidth + 63) & ~63; decodeHeight = (decodeHeight + 63) & ~63; } PodZero(&aFrame->data[0], AV_NUM_DATA_POINTERS); PodZero(&aFrame->linesize[0], AV_NUM_DATA_POINTERS); int pitch = decodeWidth; int chroma_pitch = (pitch + 1) / 2; int chroma_height = (decodeHeight +1) / 2; // Get strides for each plane. aFrame->linesize[0] = pitch; aFrame->linesize[1] = aFrame->linesize[2] = chroma_pitch; size_t allocSize = pitch * decodeHeight + (chroma_pitch * chroma_height) * 2; RefPtr<Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR); PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(image.get()); uint8_t* buffer = ycbcr->AllocateAndGetNewBuffer(allocSize + 64); // FFmpeg requires a 16/32 bytes-aligned buffer, align it on 64 to be safe buffer = reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(buffer) + 63) & ~63); if (!buffer) { NS_WARNING("Failed to allocate buffer for FFmpeg video decoding"); return -1; } int offsets[3] = { 0, pitch * decodeHeight, pitch * decodeHeight + chroma_pitch * chroma_height }; // Add a horizontal bar |edgeWidth| pixels high at the // top of the frame, plus |edgeWidth| pixels from the left of the frame. int planesEdgeWidth[3] = { edgeWidth * aFrame->linesize[0] + edgeWidth, edgeWidth / 2 * aFrame->linesize[1] + edgeWidth / 2, edgeWidth / 2 * aFrame->linesize[2] + edgeWidth / 2 }; for (uint32_t i = 0; i < 3; i++) { aFrame->data[i] = buffer + offsets[i] + planesEdgeWidth[i]; } aFrame->extended_data = aFrame->data; aFrame->width = aCodecContext->width; aFrame->height = aCodecContext->height; aFrame->opaque = static_cast<void*>(image.forget().take()); aFrame->type = FF_BUFFER_TYPE_USER; aFrame->reordered_opaque = aCodecContext->reordered_opaque; #if LIBAVCODEC_VERSION_MAJOR == 53 if (aCodecContext->pkt) { aFrame->pkt_pts = aCodecContext->pkt->pts; } #endif return 0; }
int FFmpegH264Decoder::AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext, AVFrame* aFrame) { // Older versions of ffmpeg require that edges be allocated* around* the // actual image. int edgeWidth = avcodec_get_edge_width(); int decodeWidth = aCodecContext->width + edgeWidth * 2; int decodeHeight = aCodecContext->height + edgeWidth * 2; // Align width and height to possibly speed up decode. int stride_align[AV_NUM_DATA_POINTERS]; avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight, stride_align); // Get strides for each plane. av_image_fill_linesizes(aFrame->linesize, aCodecContext->pix_fmt, decodeWidth); // Let FFmpeg set up its YUV plane pointers and tell us how much memory we // need. // Note that we're passing |nullptr| here as the base address as we haven't // allocated our image yet. We will adjust |aFrame->data| below. size_t allocSize = av_image_fill_pointers(aFrame->data, aCodecContext->pix_fmt, decodeHeight, nullptr /* base address */, aFrame->linesize); nsRefPtr<Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR); PlanarYCbCrImage* ycbcr = reinterpret_cast<PlanarYCbCrImage*>(image.get()); uint8_t* buffer = ycbcr->AllocateAndGetNewBuffer(allocSize); if (!buffer) { NS_WARNING("Failed to allocate buffer for FFmpeg video decoding"); return -1; } // Now that we've allocated our image, we can add its address to the offsets // set by |av_image_fill_pointers| above. We also have to add |edgeWidth| // pixels of padding here. for (uint32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) { // The C planes are half the resolution of the Y plane, so we need to halve // the edge width here. uint32_t planeEdgeWidth = edgeWidth / (i ? 2 : 1); // Add buffer offset, plus a horizontal bar |edgeWidth| pixels high at the // top of the frame, plus |edgeWidth| pixels from the left of the frame. aFrame->data[i] += reinterpret_cast<ptrdiff_t>( buffer + planeEdgeWidth * aFrame->linesize[i] + planeEdgeWidth); } // Unused, but needs to be non-zero to keep ffmpeg happy. aFrame->type = GECKO_FRAME_TYPE; aFrame->extended_data = aFrame->data; aFrame->width = aCodecContext->width; aFrame->height = aCodecContext->height; mozilla::layers::PlanarYCbCrData data; PlanarYCbCrDataFromAVFrame(data, aFrame); ycbcr->SetDataNoCopy(data); mCurrentImage.swap(image); return 0; }