Skip to content

ver.0.3.11

Pre-release
Pre-release
Compare
Choose a tag to compare
@momeemt momeemt released this 18 Apr 02:12

Release ver.0.3.11

The following code is now executable.

import ffmpeg
import streams, strformat, os, strutils

proc fetchFrame(fmt: ptr AVFormatContext, frame: ptr AVFrame,
                ctx: ptr AVCodecContext, pkt: ptr AVPacket): bool =
  template check: untyped =
    if retval < 0: return false

  var retval: cint
  retval = av_read_frame(fmt, pkt)
  check()
  retval = avcodec_send_packet(ctx, pkt)
  check()
  retval = avcodec_receive_frame(ctx, frame)
  check()
  true

proc saveFrameAsJpeg(fmt: ptr AVFormatContext, ctx: ptr AVCodecContext,
                    pkt: ptr AVPacket, streamid, frameno: int) =
  var retval: cint
  var jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG)
  if jpegCodec.isNil:
    echo "Cannot open jpeg codec"
    return
  var jpegCtx = avcodec_alloc_context3(jpegCodec)
  if jpegCtx.isNil:
    echo "Cannot open jpeg codec"
    return

  var frame = av_frame_alloc()
  var encpkt = av_packet_alloc()
  defer:
    av_packet_free(addr encpkt);
    av_frame_free(addr frame);
    avcodec_free_context(addr jpegCtx);

  retval = av_seek_frame(fmt, cint streamid, frameno, AVSEEK_FLAG_FRAME)
  if retval < 0:
    echo "error seeking"
    return

  while true:
    if fetchFrame(fmt, frame, ctx, pkt) and pkt[].stream_index == streamid:
        break
    else:
        continue
    av_packet_unref(pkt)
  av_packet_unref(pkt)

  var streams = cast[ptr UncheckedArray[ptr AVStream]](fmt[].streams)
  jpegCtx[].time_base = streams[streamid][].time_base
  jpegCtx[].pix_fmt = AV_PIX_FMT_YUVJ420P
  jpegCtx[].height = frame[].height
  jpegCtx[].width = frame[].width
  retval = avcodec_open2(jpegCtx, jpegCodec, nil)
  if retval < 0:
    echo "error avcodec_open2"
    return
  retval = avcodec_send_frame(jpegctx, frame)
  if retval < 0:
    echo "error avcodec_send_frame"
    return
  retval = avcodec_receive_packet(jpegctx, encpkt)
  if retval < 0:
    echo "error avcodec_receive_packet"
    return
  echo "size: ", encpkt[].size
  var f = newFileStream(&"dvr-{frameno:08d}.jpg", mode = fmWrite)
  f.writeData(encpkt[].data, encpkt[].size)
  close f

proc main =
  if paramCount() < 2:
    quit(&"Usage: {getAppFilename()} <input_file> <frameno = default 1>" &
        "example program to save a specific frame to jpg.\n")

  var frameno = 1
  if paramCount() == 2:
    frameno = try: paramStr(2).parseInt
              except: 1
  let fname = paramStr 1
  var retval: cint
  var fmtCtx = avformat_alloc_context()
  retval = avformat_open_input(addr fmtctx, fname, nil, nil)
  if retval < 0:
    echo &"error opening file {fname} with message"
    return
  retval = avformat_find_stream_info(fmtctx, nil)
  if retval < 0:
    echo &"error finding stream with message"
    return
  var streamid = -1
  var streams = cast[ptr UncheckedArray[ptr AVStream]](fmtctx[].streams)
  var codecParam: ptr AVCodecParameters
  var vidCodec: ptr AVCodec
  for i in 0 ..< fmtctx[].nb_streams:
    var locpar = streams[i][].codecpar
    var locdec = avcodec_find_decoder(locpar[].codec_id)
    if locpar[].codec_type == AVMEDIA_TYPE_VIDEO:
      vidCodec = locdec
      codecParam = locpar
      streamid = int i
      break
  var vidCtx = avcodec_alloc_context3(vidCodec)
  var packet = av_packet_alloc()
  defer:
    av_packet_free(addr packet);
    avcodec_free_context(addr vidCtx);
    avformat_close_input(addr fmtctx);
    avformat_free_context(fmtctx);

  retval = avcodec_parameters_to_context(vidCtx, codecParam)
  if retval < 0:
    echo &"failed param to context"
    return
  retval = avcodec_open2(vidCtx, vidCodec, nil)
  if retval < 0:
    echo &"failed open2 vidCtx"
    return
  saveFrameAsJpeg(fmtctx, vidCtx, packet, streamid, frameno)

main()