导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    【BIGTREETECH PI 全志H616开发板】多媒体开发应用

    H/F/TV Series
    1
    1
    1143
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • zznzzn
      zznzzn LV 6 最后由 编辑

      本文转载自:http://bbs.eeworld.com.cn/thread-1249107-1-1.html
      原文作者@qinyunti

      前言

      本开发板接口资源丰富,支持HDMI,板子虽小但是五脏俱全性能强大。

      特别适合多媒体终端的开发,本片演示基于ffmpeg进行多媒体开发,实现网络视频播放的Demo。

      ffmpeg安装

      由于系统运行的ubuntu所以系统就自带了ffmpeg,如果没有则可以sudo apt-get install ffmpeg安装,前提是已经按照之前配置好了wifi可以联网。

      wget https://github.com/BtbN/FFmpeg-B ... 4-gpl-shared.tar.xz
      
      sudo apt-get install xz-utils
      
      xz -d ffmpeg-master-latest-linuxARM64-gpl-shared.tar.xz
      
      tar -xvf ffmpeg-master-latest-linuxarm64-gpl-shared.tar
      

      Ffmpeg性能测试

      下载测试文件wget

      ffmpeg -benchmark -i big_buck_bunny_720p_20mb.mp4 -f null -
      

      分别在WSL和开发板中运行对比性能大概差10倍左右

      WSL中

      223632znndwnpp8yr3sz75.png.thumb.jpg

      开发板中

      223632rwnztzya6tkzh7n6.png.thumb.jpg

      Ffmpeg代码开发

      编码测试

      vi encode_video.c
      

      代码如下

      #include
      #include
      #include
      
      #include
      
      #include
      #include
      
      static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
                         FILE *outfile)
      {
          int ret;
      
          /* send the frame to the encoder */
          if (frame)
              printf("Send frame %3"PRId64"\n", frame->pts);
      
          ret = avcodec_send_frame(enc_ctx, frame);
          if (ret < 0) {
              fprintf(stderr, "Error sending a frame for encoding\n");
              exit(1);
          }
      
          while (ret >= 0) {
              ret = avcodec_receive_packet(enc_ctx, pkt);
              if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                  return;
              else if (ret < 0) {
                  fprintf(stderr, "Error during encoding\n");
                  exit(1);
              }
      
              printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);
              fwrite(pkt->data, 1, pkt->size, outfile);
              av_packet_unref(pkt);
          }
      }
      
      int main(int argc, char **argv)
      {
          const char *filename, *codec_name;
          const AVCodec *codec;
          AVCodecContext *c= NULL;
          int i, ret, x, y;
          FILE *f;
          AVFrame *frame;
          AVPacket *pkt;
          uint8_t endcode[] = { 0, 0, 1, 0xb7 };
      
          if (argc <= 2) {
              fprintf(stderr, "Usage: %s \n", argv[0]);
              exit(0);
          }
          filename = argv[1];
          codec_name = argv[2];
      
          /* find the mpeg1video encoder */
          codec = avcodec_find_encoder_by_name(codec_name);
          if (!codec) {
              fprintf(stderr, "Codec '%s' not found\n", codec_name);
              exit(1);
          }
      
          c = avcodec_alloc_context3(codec);
          if (!c) {
              fprintf(stderr, "Could not allocate video codec context\n");
              exit(1);
          }
      
          pkt = av_packet_alloc();
          if (!pkt)
              exit(1);
      
          /* put sample parameters */
          c->bit_rate = 400000;
          /* resolution must be a multiple of two */
          c->width = 352;
          c->height = 288;
          /* frames per second */
          c->time_base = (AVRational){1, 25};
          c->framerate = (AVRational){25, 1};
      
          /* emit one intra frame every ten frames
           * check frame pict_type before passing frame
           * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
           * then gop_size is ignored and the output of encoder
           * will always be I frame irrespective to gop_size
           */
          c->gop_size = 10;
          c->max_b_frames = 1;
          c->pix_fmt = AV_PIX_FMT_YUV420P;
      
          if (codec->id == AV_CODEC_ID_H264)
              av_opt_set(c->priv_data, "preset", "slow", 0);
      
          /* open it */
          ret = avcodec_open2(c, codec, NULL);
          if (ret < 0) {
              fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
              exit(1);
          }
      
          f = fopen(filename, "wb");
          if (!f) {
              fprintf(stderr, "Could not open %s\n", filename);
              exit(1);
          }
      
          frame = av_frame_alloc();
          if (!frame) {
              fprintf(stderr, "Could not allocate video frame\n");
              exit(1);
          }
          frame->format = c->pix_fmt;
          frame->width  = c->width;
          frame->height = c->height;
      
          ret = av_frame_get_buffer(frame, 0);
          if (ret < 0) {
              fprintf(stderr, "Could not allocate the video frame data\n");
              exit(1);
          }
      
          /* encode 1 second of video */
          for (i = 0; i < 25; i++) {
              fflush(stdout);
      
              /* Make sure the frame data is writable.
                 On the first round, the frame is fresh from av_frame_get_buffer()
                 and therefore we know it is writable.
                 But on the next rounds, encode() will have called
                 avcodec_send_frame(), and the codec may have kept a reference to
                 the frame in its internal structures, that makes the frame
                 unwritable.
                 av_frame_make_writable() checks that and allocates a new buffer
                 for the frame only if necessary.
               */
              ret = av_frame_make_writable(frame);
              if (ret < 0)
                  exit(1);
      
              /* Prepare a dummy image.
                 In real code, this is where you would have your own logic for
                 filling the frame. FFmpeg does not care what you put in the
                 frame.
               */
              /* Y */
              for (y = 0; y < c->height; y++) {
                  for (x = 0; x < c->width; x++) {
                      frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
                  }
              }
      
              /* Cb and Cr */
              for (y = 0; y < c->height/2; y++) {
                  for (x = 0; x < c->width/2; x++) {
                      frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                      frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
                  }
              }
      
              frame->pts = i;
      
              /* encode the image */
              encode(c, frame, pkt, f);
          }
      
          /* flush the encoder */
          encode(c, NULL, pkt, f);
      
          /* Add sequence end code to have a real MPEG file.
             It makes only sense because this tiny examples writes packets
             directly. This is called "elementary stream" and only works for some
             codecs. To create a valid file, you usually need to write packets
             into a proper file format or protocol; see mux.c.
           */
          if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO)
              fwrite(endcode, 1, sizeof(endcode), f);
          fclose(f);
      
          avcodec_free_context(&c);
          av_frame_free(&frame);
          av_packet_free(&pkt);
      
          return 0;
      }
      

      编译

      gcc encode_video.c  -Iffmpeg-master-latest-linuxarm64-gpl-shared/include -Lffmpeg-master-latest-linuxarm64-gpl-shared/lib -lavcodec -lavutil -lswresample -o encode
      

      运行

      export LD_LIBRARY_PATH=/root/ffmpeg-master-latest-linuxarm64-gpl-shared/lib:$LD_LIBRARY_PATH
      ./encode encode.bin mpeg1video
      

      播放编码的文件

      ffplay.exe -i encode.bin
      

      223632md3czo3mahmzfyu3.png.thumb.jpg

      解码测试

      vi decode_video.c
      

      代码如下

      #include
      #include
      #include
      
      #include
      
      #define INBUF_SIZE 4096
      
      static void pgm_save(unsigned char* buf, int wrap, int xsize, int ysize,
          char* filename)
      {
          FILE* f;
          int i;
      
          f = fopen(filename, "wb");
          fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
          for (i = 0; i < ysize; i++)
              fwrite(buf + i * wrap, 1, xsize, f);
          fclose(f);
      }
      
      static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt,
          const char* filename)
      {
          char buf[1024];
          int ret;
      
          ret = avcodec_send_packet(dec_ctx, pkt);
          if (ret < 0) {
              fprintf(stderr, "Error sending a packet for decoding\n");
              exit(1);
          }
      
          while (ret >= 0) {
              ret = avcodec_receive_frame(dec_ctx, frame);
              if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                  return;
              else if (ret < 0) {
                  fprintf(stderr, "Error during decoding\n");
                  exit(1);
              }
      
              printf("saving frame %3"PRId64"\n", dec_ctx->frame_num);
              fflush(stdout);
      
              /* the picture is allocated by the decoder. no need to
                 free it */
              snprintf(buf, sizeof(buf), "%s-%"PRId64, filename, dec_ctx->frame_num);
              pgm_save(frame->data[0], frame->linesize[0],
                  frame->width, frame->height, buf);
          }
      }
      
      int main(int argc, char** argv)
      {
          const char* filename, * outfilename;
          const AVCodec* codec;
          AVCodecParserContext* parser;
          AVCodecContext* c = NULL;
          FILE* f;
          AVFrame* frame;
          uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
          uint8_t* data;
          size_t   data_size;
          int ret;
          int eof;
          AVPacket* pkt;
      
          if (argc <= 2) {
              fprintf(stderr, "Usage: %s 
       \n"
                  "And check your input file is encoded by mpeg1video please.\n", argv[0]);
              exit(0);
          }
          filename = argv[1];
          outfilename = argv[2];
      
          pkt = av_packet_alloc();
          if (!pkt)
              exit(1);
      
          /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
          memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
      
          /* find the MPEG-1 video decoder */
          codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
          if (!codec) {
              fprintf(stderr, "Codec not found\n");
              exit(1);
          }
      
          parser = av_parser_init(codec->id);
          if (!parser) {
              fprintf(stderr, "parser not found\n");
              exit(1);
          }
      
          c = avcodec_alloc_context3(codec);
          if (!c) {
              fprintf(stderr, "Could not allocate video codec context\n");
              exit(1);
          }
      
          /* For some codecs, such as msmpeg4 and mpeg4, width and height
             MUST be initialized there because this information is not
             available in the bitstream. */
      
             /* open it */
          if (avcodec_open2(c, codec, NULL) < 0) {
              fprintf(stderr, "Could not open codec\n");
              exit(1);
          }
      
          f = fopen(filename, "rb");
          if (!f) {
              fprintf(stderr, "Could not open %s\n", filename);
              exit(1);
          }
      
          frame = av_frame_alloc();
          if (!frame) {
              fprintf(stderr, "Could not allocate video frame\n");
              exit(1);
          }
      
          do {
              /* read raw data from the input file */
              data_size = fread(inbuf, 1, INBUF_SIZE, f);
              if (ferror(f))
                  break;
              eof = !data_size;
      
              /* use the parser to split the data into frames */
              data = inbuf;
              while (data_size > 0 || eof) {
                  ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                      data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
                  if (ret < 0) {
                      fprintf(stderr, "Error while parsing\n");
                      exit(1);
                  }
                  data += ret;
                  data_size -= ret;
      
                  if (pkt->size)
                      decode(c, frame, pkt, outfilename);
                  else if (eof)
                      break;
              }
          } while (!eof);
      
          /* flush the decoder */
          decode(c, frame, NULL, outfilename);
      
          fclose(f);
      
          av_parser_close(parser);
          avcodec_free_context(&c);
          av_frame_free(&frame);
          av_packet_free(&pkt);
      
          return 0;
      }
      

      编译

      gcc decode_video.c  -Iffmpeg-master-latest-linuxarm64-gpl-shared/include -Lffmpeg-master-latest-linuxarm64-gpl-shared/lib -lavcodec -lavutil -lswresample -o decode
      

      运行

      export LD_LIBRARY_PATH=/root/ffmpeg-master-latest-linuxarm64-gpl-shared/lib:$LD_LIBRARY_PATH
      ./decode encode.bin decode.bin
      

      网络视频播放

      PC上从以下地址下载VLC安装https://www.videolan.org/vlc/

      电脑上确认IP地址我这里是192.168.31.64

      开发板上运行

      ffmpeg -re -i big_buck_bunny_720p_20mb.mp4 -an -vcodec copy -f rtp rtp://192.168.31.64:5004
      

      打印如下,复位红色框中的内容,不包括SDP:这一行

      223632oy27xb3230ybk5zs.png.thumb.jpg

      PC上新建demo.sdp文件,黏贴上述内容

      223632zhb4wvms56olvorv.png.thumb.jpg

      右键点击demo.sdp使用VLC打开

      即可看到开始播放视频

      223632kecfrrvzaeb5ovi5.png.thumb.jpg

      总结

      得益于开发板强劲的性能和运行完整的Ubuntu系统环境,可以方便的进行多媒体相关的开发。

      1 条回复 最后回复 回复 引用 分享 0
      • 1 / 1
      • First post
        Last post

      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

      行为准则 | 用户协议 | 隐私权政策