mirror of
https://github.com/qrtc/ffmpeg-dev-go.git
synced 2025-10-05 07:37:20 +08:00
193 lines
4.8 KiB
Go
193 lines
4.8 KiB
Go
//go:build ffmpeg_hw_qsv
|
|
|
|
package main
|
|
|
|
/*
|
|
#include <stdlib.h>
|
|
|
|
struct AVCodecContext;
|
|
|
|
int get_format(struct AVCodecContext *avctx, int *pix_fmts) {
|
|
// TODO.
|
|
return -1;
|
|
}
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
ffmpeg "github.com/qrtc/ffmpeg-dev-go"
|
|
)
|
|
|
|
type decodeContext struct {
|
|
hwDeviceRef *ffmpeg.AVBufferRef
|
|
}
|
|
|
|
func decodePacket(decode *decodeContext, decoderCtx *ffmpeg.AVCodecContext,
|
|
frame *ffmpeg.AVFrame, swFrame *ffmpeg.AVFrame,
|
|
pkt *ffmpeg.AVPacket, outputCtx *ffmpeg.AVIOContext) (ret int32) {
|
|
var (
|
|
data [][]uint8
|
|
)
|
|
if ret = ffmpeg.AvCodecSendPacket(decoderCtx, pkt); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error during decoding\n")
|
|
return ret
|
|
}
|
|
|
|
for ret >= 0 {
|
|
ret = ffmpeg.AvCodecReceiveFrame(decoderCtx, frame)
|
|
if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF {
|
|
break
|
|
} else if ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error while decoding.\n")
|
|
return ret
|
|
}
|
|
|
|
// A real program would do something useful with the decoded frame here.
|
|
// We just retrieve the raw data and write it to a file, which is rather
|
|
// useless but pedagogic.
|
|
if ret = ffmpeg.AvHWFrameTransferData(swFrame, frame, 0); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error transferring the data to system memory\n")
|
|
goto fail
|
|
}
|
|
|
|
data = ffmpeg.SliceSlice(&swFrame.GetData()[0], len(swFrame.GetData()),
|
|
swFrame.GetWidth()*swFrame.GetHeight())
|
|
for i := 0; i < len(swFrame.GetData()) && data[i] != nil; i++ {
|
|
for j := 0; j < int(swFrame.GetHeight()>>ffmpeg.CondExpr(i > 0, 1, 0)); j++ {
|
|
ffmpeg.AvIOWrite(outputCtx, &data[i][j*int(swFrame.GetLinesize()[i])], swFrame.GetWidth())
|
|
}
|
|
}
|
|
fail:
|
|
ffmpeg.AvFrameUnref(swFrame)
|
|
ffmpeg.AvFrameUnref(frame)
|
|
|
|
if ret < 0 {
|
|
return ret
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
intputCtx *ffmpeg.AVFormatContext
|
|
videoSt *ffmpeg.AVStream
|
|
decoderCtx *ffmpeg.AVCodecContext
|
|
decoder *ffmpeg.AVCodec
|
|
pkt ffmpeg.AVPacket
|
|
frame, swFrame *ffmpeg.AVFrame
|
|
decode = &decodeContext{}
|
|
outputCtx *ffmpeg.AVIOContext
|
|
ret int32
|
|
)
|
|
|
|
if len(os.Args) < 3 {
|
|
fmt.Fprintf(os.Stderr, "Usage: %s <input file> <output file>\n", os.Args[0])
|
|
os.Exit(1)
|
|
}
|
|
|
|
// open the input file
|
|
if ret = ffmpeg.AvFormatOpenInput(&intputCtx, os.Args[1], nil, nil); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Cannot open input file '%s': ", os.Args[1])
|
|
goto finish
|
|
}
|
|
|
|
// find the first H.264 video stream
|
|
for i := 0; i < int(intputCtx.GetNbStreams()); i++ {
|
|
st := intputCtx.GetStreams()[i]
|
|
if st.GetCodecpar().GetCodecId() == ffmpeg.AV_CODEC_ID_H264 && videoSt == nil {
|
|
videoSt = st
|
|
} else {
|
|
st.SetDiscard(ffmpeg.AVDISCARD_ALL)
|
|
}
|
|
}
|
|
if videoSt == nil {
|
|
fmt.Fprintf(os.Stderr, "No H.264 video stream in the input file\n")
|
|
goto finish
|
|
}
|
|
|
|
// open the hardware device
|
|
if ret = ffmpeg.AvHWDeviceCtxCreate(&decode.hwDeviceRef, ffmpeg.AV_HWDEVICE_TYPE_QSV,
|
|
"auto", nil, 0); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Cannot open the hardware device\n")
|
|
goto finish
|
|
}
|
|
|
|
// initialize the decoder
|
|
if decoder = ffmpeg.AvCodecFindDecoderByName("h264_qsv"); decoder == nil {
|
|
fmt.Fprintf(os.Stderr, "The QSV decoder is not present in libavcodec\n")
|
|
goto finish
|
|
}
|
|
|
|
decoderCtx.SetCodecId(ffmpeg.AV_CODEC_ID_H264)
|
|
if videoSt.GetCodecpar().GetExtradataSize() != 0 {
|
|
decoderCtx.SetExtradata((*uint8)(ffmpeg.AvMallocz(videoSt.GetCodecpar().GetExtradataSize() +
|
|
ffmpeg.AV_INPUT_BUFFER_PADDING_SIZE)))
|
|
if decoderCtx.GetExtradata() == nil {
|
|
ret = ffmpeg.AVERROR(syscall.ENOMEM)
|
|
goto finish
|
|
}
|
|
copy(unsafe.Slice(decoderCtx.GetExtradata(), videoSt.GetCodecpar().GetExtradataSize()),
|
|
unsafe.Slice(videoSt.GetCodecpar().GetExtradata(), videoSt.GetCodecpar().GetExtradataSize()))
|
|
}
|
|
|
|
decoderCtx.SetOpaque(decode.hwDeviceRef)
|
|
decoderCtx.SetGetFormat((ffmpeg.AVCodecContextGetFormatFunc)(C.get_format))
|
|
|
|
if ret = ffmpeg.AvCodecOpen2(decoderCtx, nil, nil); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error opening the decoder: ")
|
|
goto finish
|
|
}
|
|
|
|
// open the output stream
|
|
if ret = ffmpeg.AvIOOpen(&outputCtx, os.Args[2], ffmpeg.AVIO_FLAG_WRITE); ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "Error opening the output context: ")
|
|
goto finish
|
|
}
|
|
|
|
frame = ffmpeg.AvFrameAlloc()
|
|
swFrame = ffmpeg.AvFrameAlloc()
|
|
if frame == nil || swFrame == nil {
|
|
ret = ffmpeg.AVERROR(syscall.ENOMEM)
|
|
goto finish
|
|
}
|
|
|
|
// actual decoding
|
|
for ret >= 0 {
|
|
if ret = ffmpeg.AvReadFrame(intputCtx, &pkt); ret < 0 {
|
|
break
|
|
}
|
|
|
|
if pkt.GetStreamIndex() == videoSt.GetIndex() {
|
|
ret = decodePacket(decode, decoderCtx, frame, swFrame, &pkt, outputCtx)
|
|
}
|
|
|
|
ffmpeg.AvPacketUnref(&pkt)
|
|
}
|
|
|
|
finish:
|
|
if ret < 0 {
|
|
fmt.Fprintf(os.Stderr, "%s\n", ffmpeg.AvErr2str(ret))
|
|
}
|
|
|
|
ffmpeg.AvFormatCloseInput(&intputCtx)
|
|
|
|
ffmpeg.AvFrameFree(&frame)
|
|
ffmpeg.AvFrameFree(&swFrame)
|
|
|
|
ffmpeg.AvCodecFreeContext(&decoderCtx)
|
|
|
|
ffmpeg.AvBufferUnref(&decode.hwDeviceRef)
|
|
|
|
ffmpeg.AvIOClose(outputCtx)
|
|
|
|
os.Exit(int(ret))
|
|
}
|