首页 > 其他分享 >WebAssembly编译ffmpeg

WebAssembly编译ffmpeg

时间:2023-09-26 17:32:11浏览次数:39  
标签:WebAssembly ffmpeg videoCodecCtx pos Module 编译 let NULL data

编译ffmpeg

脚本 build.sh

export FFMPEG_PATH=./ffmpeg-snapshot/decoder_wasm/ffmpeg
echo "Running Emscripten..."
emcc -O1 ffmpeg_decode.c --pre-js decode.js \
-I ${FFMPEG_PATH}/include/ \
${FFMPEG_PATH}/lib/libavcodec.a  \
${FFMPEG_PATH}/lib/libavutil.a  \
${FFMPEG_PATH}/lib/libswscale.a  \
-o ffmpeg_decode.js \
-s EXPORTED_FUNCTIONS=_malloc,_free \
-s ALLOW_MEMORY_GROWTH=1 \
-s ASSERTIONS=1 \
-lworkerfs.js
# -s EXPORTED_RUNTIME_METHODS=ccall,cwrap,allocate,UTF8ToString,intArrayFromString \
# -s ENVIRONMENT=web \
# -s MODULARIZE=1 \


# -s FORCE_FILESYSTEM=1 \
# -s RESERVED_FUNCTION_POINTERS \
# -s EXPORT_ES6=1 \
# -s USE_ES6_IMPORT_META=0

echo "Finished Build"

源码

#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavutil/parseutils.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>

#ifndef EM_PORT_API
#if defined(__EMSCRIPTEN__)
#include <emscripten.h>
#if defined(__cplusplus)
#define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
#else
#define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
#endif
#else
#if defined(__cplusplus)
#define EM_PORT_API(rettype) extern "C" rettype
#else
#define EM_PORT_API(rettype) rettype
#endif
#endif
#endif

const AVCodec *videoCodec = NULL;
AVCodecContext *videoCodecCtx = NULL;
AVCodecParserContext *parser = NULL;
AVPacket *pkt = NULL;
AVFrame *yuvFrame = NULL;
AVFrame *rgbFrame = NULL;
struct SwsContext *img_ctx = NULL;
unsigned char *out_buffer = NULL;
int frameWidth = 0;
int frameHeight = 0;
uint8_t *frame = NULL;

EM_PORT_API(int)
getWidth()
{
  return frameWidth;
}

EM_PORT_API(int)
getHeight()
{
  return frameHeight;
}

EM_PORT_API(uint8_t *)
getFrame()
{
  return frame;
}

EM_PORT_API(void)
init(int codecID)
{
  pkt = av_packet_alloc();
  if (!pkt)
  {
    printf("pkt alloc failed.\n");
    return;
  }
  yuvFrame = av_frame_alloc();
  if (!yuvFrame)
  {
    printf("yuvFrame alloc failed.\n");
    return;
  }
  rgbFrame = av_frame_alloc();
  if (!rgbFrame)
  {
    printf("rgbFrame alloc failed.\n");
    return;
  }
  videoCodec = avcodec_find_decoder(codecID);
  if (!videoCodec)
  {
    printf("videoCodec find failed.\n");
    return;
  }
  parser = av_parser_init(codecID);
  if (!parser)
  {
    printf("parser init failed.\n");
    return;
  }
  videoCodecCtx = avcodec_alloc_context3(videoCodec);
  if (!videoCodecCtx)
  {
    printf("videoCodecCtx alloc failed.\n");
    return;
  }
  int ret = avcodec_open2(videoCodecCtx, videoCodec, NULL);
  if (ret < 0)
  {
    printf("videoCodecCtx alloc failed.\n");
    return;
  }
  printf("codec init success.\n");
}

EM_PORT_API(void)
close()
{
  if (parser)
  {
    av_parser_close(parser);
    parser = NULL;
  }

  if (pkt)
  {
    av_packet_free(&pkt);
    pkt = NULL;
  }

  if (yuvFrame)
  {
    av_frame_free(&yuvFrame);
    yuvFrame = NULL;
  }

  if (rgbFrame)
  {
    av_frame_free(&rgbFrame);
    rgbFrame = NULL;
  }

  if (videoCodecCtx)
  {
    avcodec_free_context(&videoCodecCtx);
  }

  if (videoCodecCtx)
  {
    avcodec_close(videoCodecCtx);
    videoCodecCtx = NULL;
  }

  if (out_buffer)
  {
    av_free(out_buffer);
    out_buffer = NULL;
  }
  if (img_ctx) {
    sws_freeContext(img_ctx);
    img_ctx = NULL;
  }
  printf("close %s\n", __FUNCTION__);
}

EM_PORT_API(void)
decode()
{
  int ret = avcodec_send_packet(videoCodecCtx, pkt);
  if (ret >= 0)
  {
    while ((ret = avcodec_receive_frame(videoCodecCtx, yuvFrame)) >= 0)
    {
      if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        break;
      else if (ret < 0)
      {
        fprintf(stderr, "Error during decoding\n");
        break;
      }
      if (!img_ctx)
      {
        printf("init img_ctx\n");
        img_ctx = sws_getContext(videoCodecCtx->width,
                                 videoCodecCtx->height,
                                 videoCodecCtx->pix_fmt,
                                 videoCodecCtx->width,
                                 videoCodecCtx->height,
                                 AV_PIX_FMT_RGB32,
                                 SWS_BICUBIC, NULL, NULL, NULL);
        
      }
      if (!out_buffer)
      {
        printf("init out_buffer\n");
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, videoCodecCtx->width, videoCodecCtx->height, 1);
        out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char));

        int res = av_image_fill_arrays(
            rgbFrame->data, rgbFrame->linesize,
            out_buffer, AV_PIX_FMT_RGB32,
            videoCodecCtx->width, videoCodecCtx->height, 1);
        if (res < 0)
        {
          break;
        }
        
      }

      sws_scale(img_ctx,
                yuvFrame->data, yuvFrame->linesize,
                0, videoCodecCtx->height,
                rgbFrame->data, rgbFrame->linesize);
      // printf("codec h264 success.\n");
      av_packet_unref(pkt);
      frameWidth = videoCodecCtx->width;
      frameHeight = videoCodecCtx->height;
      frame = rgbFrame->data[0];
    }
  }
  av_packet_unref(pkt);
}

EM_PORT_API(void)
parsePkt(uint8_t *data, int len)
{
  // printf("parsePkt:%d\n", len);
  int eof = !len;
  while (len > 0 || eof)
  {
    int ret = av_parser_parse2(parser, videoCodecCtx, &pkt->data, &pkt->size,
                               data, 4096, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
    if (ret < 0)
    {
      fprintf(stderr, "Error while parsing\n");
      continue;
    }

    data += ret;
    len -= ret;

    if (pkt->size)
    {
      decode();
    }
  }
}

js 胶水

var Module = typeof Module != 'undefined' ? Module : {};

// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
Module = {};
Module.onRuntimeInitialized = function () {
  console.log(Module);
  let message = {
    type: "message",
    info: "init"
  };
  postMessage(message);
};

let u8Array;
console.log("Worker: mission start.");
let allDataLength;
let pos = 0;
let startArray;
let dataArray;
let cnt = 0;
let time = "";
let runFlag = false;
let timeMap = new Map();
let codecId = 27;

function getTimesRange() {
  console.log("获取POS");
  timeMap.clear();
  let index = 0;
  timeMap.set(0, 0);
  while (true) {
    if (pos + startLength > allDataLength) {
      break;
    }
    startArray = u8Array.slice(pos, pos + startLength);
    if (pos + singleDataLength * cnt > allDataLength) {
      break;
    }
    pos += singleDataLength * cnt;
    index++;
    timeMap.set(pos, index);
  }
  let message = {
    type: "updatePos",
    info: pos,
    map: timeMap
  };

  postMessage(message);
  pos = 0;
}

function decodeArray() {
  // console.log(allDataLength, pos, new Date().getMilliseconds());
  if (pos + startLength > allDataLength) {
    console.log("Worker: mission finished.");
    pos = 0;
    return;
  }
  startArray = u8Array.slice(pos, pos + startLength);
  pos += startLength;
  if (pos + singleDataLength * cnt > allDataLength) {
    console.log("Worker: mission finished.");
    pos = 0;
    return;
  }
  dataArray = u8Array.slice(pos, pos + singleDataLength * cnt);
  pos += singleDataLength * cnt;
  var ptr = Module._malloc(1024 * cnt * dataArray.BYTES_PER_ELEMENT);
  Module.HEAPU8.set(dataArray, ptr);
  Module._parsePkt(ptr, 1024* cnt);
  let outputPtr = Module._getFrame();
  // console.log("_parsePkt end");
  Module._free(ptr);
  if (0 == outputPtr) {
    if (runFlag) {
      setTimeout(() => {
        decodeArray();
      }, 1);
      return;
    }
  }
  var rgbData = new Uint8ClampedArray(
    Module.HEAPU8.subarray(
      outputPtr,
      outputPtr + Module._getWidth() * Module._getHeight() * 4,
    ),
  );
  let rgbObj = {
    width: Module._getWidth(),
    height: Module._getHeight(),
    rgb: rgbData,
    time: time,
    currentPos: timeMap.get(pos)
  };
  let message = {
    type: "image",
    info: rgbObj
  };
  postMessage(message, [message.info.rgb.buffer]);
  if (runFlag) {
    setTimeout(() => {
      decodeArray();
    }, 1);
  }
}

onmessage = function (e) {
  if ("message" == e.data.type) {
    if ("start" == e.data.info) {
      runFlag = true;
      decodeArray();
    } else if ("stop" == e.data.info) {
      runFlag = false;
    }
  } else if ("updatePos" == e.data.type) { 
    pos = e.data.info;
    runFlag = false;
    decodeArray();
  } else if ("updateCodecId" == e.data.type) { 
    codecId = e.data.info;
    Module._close();
    Module._init(codecId);
    console.log(codecId);
  } else {
    u8Array = e.data;
    allDataLength = u8Array.length;
    pos = 0;
    Module._close();
    Module._init(codecId);
    getTimesRange();
  }
};

标签:WebAssembly,ffmpeg,videoCodecCtx,pos,Module,编译,let,NULL,data
From: https://blog.51cto.com/u_11997528/7610920

相关文章

  • 瑞芯微RK3568|SDK开发之Kernel编译
    1. Kernel手动编译1.1       kernel查询帮助 使用./build.sh -h kernel查看kernel的详细编译命令如下所示。图1.1编译内核 上图表示,单独编译kernel固件分为三步,进入kernel目录,选择默认配置文件,编译镜像。 1.2       kernel默认配置对应平台的默认配......
  • [Go 夜读 第 148 期] Excelize 构建 WebAssembly 版本跨语言支持实践
    Excelize是Go语言编写的用于操作电子表格文档的基础库,支持XLAM/XLSM/XLSX/XLTM/XLTX等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写支持,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统......
  • 瑞芯微RK3568|SDK开发之环境安装及编译操作
    1. SDK简介一个通用 Linux SDK 工程目录包含有buildroot、app、kernel、device、docs、external 等目录。其中一些特性芯片如RK3308/RV1108/RV1109/RV1126等,会有所不同。● app:存放上层应用 app,主要是 qcamera/qfm/qplayer/settings 等一些应用程序。● buildroot:基......
  • MacOS 使用 Asan 编译 C++报警告malloc: nano zone abandoned due to inability to re
    问题clang(llvm)编译c++程序,带内存问题检查工具选项-fsanitize=address-fsanitize=undefined之后出现:malloc:nanozoneabandonedduetoinabilitytoreservevmspace.解决vi~/.zshrc#加入:exportMallocNanoZone=0source~/.zshrc参考:ios-malloc:nanozonea......
  • 5-Linux操作系统 vi/vim编译器
    一、vi编译器介绍  Vi编辑器是所有Unix及Linux系统下标准的编辑器,类似于windows系统下的notepad(记事本)编辑器,由于在Unix及Linux系统的任何版本,Vi编辑器是完全相同的,因此可以在其他任何介绍vi的地方都能进一步了解它,Vi也是Linux中最基本的文本编辑器,学会它后,我们将在Linux的世......
  • win下编译libcurl x86静态库 (附带ssl)
     VisualStudio版本: 克隆libcurl项目:gitclonehttps://github.com/curl/curl.git添加依赖(ssl):在拷贝的项目下添加deps目录: 在deps下创建lib和include目录: 关于编译openssl参考:https://www.cnblogs.com/laremehpe/p/17712109.html将openssl下的include下的open......
  • 关于MRS编译生成数据代表含义
    当MRS编译完成后,编译界面显示信息如下,其中数据代表含义如下: text段是程序代码段,由编译器在编译链接时自动计算,表示程序代码段大小。data段包含已初始化的全局变量和静态变量。bss段是英文BlockStartedbySymbol的简称,通常是指用来存放程序中未初始化的全局变量的一块内存......
  • 编译器优化记录(死代码消除+“激进的”死代码消除)
    编译器优化记录(3)——死代码消除+”激进的“死代码消除0.什么是死代码消除相信大家在写C++的时候,如果你定义了一个变量但是没有对其使用,大部分IDE都会对这个变量进行灰色的染色。又或者说,当你开了一个空的循环,在里面定义并使用了一堆和输出值/返回值没有关系的变量,这个时候IDE......
  • IntelliJ Idea编译报错:请使用 -source 7 或更高版本以启用 diamond 运算符
    最近在使用IntelliJIdea遇到了挫折,分享出来给大家,问题由来是我导入了外部的java文件,结果就报错了错误的句子也提示出来了:KafkaConsumer<String,String>kafkaConsumer=newKafkaConsumer<>(props);网上搜了各种解决方法都不行,崩溃的节奏啊,终于皇天不负有心人,让我同事解决了,希......
  • Ubuntu18.04编译安装Ffmpeg6.0
    本文仅使用Ffmpeg来推RTSP流,其他用途请谨慎参考。1、安装基础库apt-getinstallyasmapt-getinstalllibsdl1.2-devapt-getinstalllibstdl2-devapt-getinstallbuild-essentialaptinstalllibspeex-dev2、安装pkg-configaptintallpkg-config设置环境变量(如果不知......