mirror of
https://github.com/giorgisio/goav.git
synced 2024-04-21 12:31:56 +00:00
208 lines
5.7 KiB
Go
208 lines
5.7 KiB
Go
package main
|
|
|
|
// tutorial01.c
|
|
// Code based on a tutorial at http://dranger.com/ffmpeg/tutorial01.html
|
|
|
|
// A small sample program that shows how to use libavformat and libavcodec to
|
|
// read video from a file.
|
|
//
|
|
// Use
|
|
//
|
|
// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lswscale -lz
|
|
//
|
|
// to build (assuming libavformat and libavcodec are correctly installed
|
|
// your system).
|
|
//
|
|
// Run using
|
|
//
|
|
// tutorial01 myvideofile.mpg
|
|
//
|
|
// to write the first five frames from "myvideofile.mpg" to disk in PPM
|
|
// format.
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"unsafe"
|
|
|
|
"github.com/giorgisio/goav/swscale"
|
|
|
|
"github.com/giorgisio/goav/avcodec"
|
|
"github.com/giorgisio/goav/avformat"
|
|
"github.com/giorgisio/goav/avutil"
|
|
)
|
|
|
|
// SaveFrame writes a single frame to disk as a PPM file
|
|
func SaveFrame(frame *avutil.Frame, width, height, frameNumber int) {
|
|
// Open file
|
|
fileName := fmt.Sprintf("frame%d.ppm", frameNumber)
|
|
file, err := os.Create(fileName)
|
|
if err != nil {
|
|
log.Println("Error Reading")
|
|
}
|
|
defer file.Close()
|
|
|
|
// Write header
|
|
header := fmt.Sprintf("P6\n%d %d\n255\n", width, height)
|
|
file.Write([]byte(header))
|
|
|
|
// Write pixel data
|
|
for y := 0; y < height; y++ {
|
|
data0 := avutil.Data(frame)[0]
|
|
buf := make([]byte, width*3)
|
|
startPos := uintptr(unsafe.Pointer(data0)) + uintptr(y)*uintptr(avutil.Linesize(frame)[0])
|
|
for i := 0; i < width*3; i++ {
|
|
element := *(*uint8)(unsafe.Pointer(startPos + uintptr(i)))
|
|
buf[i] = element
|
|
}
|
|
file.Write(buf)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
fmt.Println("Please provide a movie file")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Open video file
|
|
pFormatContext := avformat.AvformatAllocContext()
|
|
if avformat.AvformatOpenInput(&pFormatContext, os.Args[1], nil, nil) != 0 {
|
|
fmt.Printf("Unable to open file %s\n", os.Args[1])
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Retrieve stream information
|
|
if pFormatContext.AvformatFindStreamInfo(nil) < 0 {
|
|
fmt.Println("Couldn't find stream information")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Dump information about file onto standard error
|
|
pFormatContext.AvDumpFormat(0, os.Args[1], 0)
|
|
|
|
// Find the first video stream
|
|
for i := 0; i < int(pFormatContext.NbStreams()); i++ {
|
|
switch pFormatContext.Streams()[i].CodecParameters().AvCodecGetType() {
|
|
case avformat.AVMEDIA_TYPE_VIDEO:
|
|
|
|
// Get a pointer to the codec context for the video stream
|
|
pCodecCtxOrig := pFormatContext.Streams()[i].Codec()
|
|
// Find the decoder for the video stream
|
|
pCodec := avcodec.AvcodecFindDecoder(avcodec.CodecId(pCodecCtxOrig.GetCodecId()))
|
|
if pCodec == nil {
|
|
fmt.Println("Unsupported codec!")
|
|
os.Exit(1)
|
|
}
|
|
// Copy context
|
|
pCodecCtx := pCodec.AvcodecAllocContext3()
|
|
if pCodecCtx.AvcodecCopyContext((*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig))) != 0 {
|
|
fmt.Println("Couldn't copy codec context")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Open codec
|
|
if pCodecCtx.AvcodecOpen2(pCodec, nil) < 0 {
|
|
fmt.Println("Could not open codec")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Allocate video frame
|
|
pFrame := avutil.AvFrameAlloc()
|
|
|
|
// Allocate an AVFrame structure
|
|
pFrameRGB := avutil.AvFrameAlloc()
|
|
if pFrameRGB == nil {
|
|
fmt.Println("Unable to allocate RGB Frame")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Determine required buffer size and allocate buffer
|
|
numBytes := uintptr(avcodec.AvpictureGetSize(avcodec.AV_PIX_FMT_RGB24, pCodecCtx.Width(),
|
|
pCodecCtx.Height()))
|
|
buffer := avutil.AvMalloc(numBytes)
|
|
|
|
// Assign appropriate parts of buffer to image planes in pFrameRGB
|
|
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
|
|
// of AVPicture
|
|
avp := (*avcodec.Picture)(unsafe.Pointer(pFrameRGB))
|
|
avp.AvpictureFill((*uint8)(buffer), avcodec.AV_PIX_FMT_RGB24, pCodecCtx.Width(), pCodecCtx.Height())
|
|
|
|
// initialize SWS context for software scaling
|
|
swsCtx := swscale.SwsGetcontext(
|
|
pCodecCtx.Width(),
|
|
pCodecCtx.Height(),
|
|
(swscale.PixelFormat)(pCodecCtx.PixFmt()),
|
|
pCodecCtx.Width(),
|
|
pCodecCtx.Height(),
|
|
avcodec.AV_PIX_FMT_RGB24,
|
|
avcodec.SWS_BILINEAR,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
)
|
|
|
|
// Read frames and save first five frames to disk
|
|
frameNumber := 1
|
|
packet := avcodec.AvPacketAlloc()
|
|
for pFormatContext.AvReadFrame(packet) >= 0 {
|
|
// Is this a packet from the video stream?
|
|
if packet.StreamIndex() == i {
|
|
// Decode video frame
|
|
response := pCodecCtx.AvcodecSendPacket(packet)
|
|
if response < 0 {
|
|
fmt.Printf("Error while sending a packet to the decoder: %s\n", avutil.ErrorFromCode(response))
|
|
}
|
|
for response >= 0 {
|
|
response = pCodecCtx.AvcodecReceiveFrame((*avcodec.Frame)(unsafe.Pointer(pFrame)))
|
|
if response == avutil.AvErrorEAGAIN || response == avutil.AvErrorEOF {
|
|
break
|
|
} else if response < 0 {
|
|
fmt.Printf("Error while receiving a frame from the decoder: %s\n", avutil.ErrorFromCode(response))
|
|
return
|
|
}
|
|
|
|
if frameNumber <= 5 {
|
|
// Convert the image from its native format to RGB
|
|
swscale.SwsScale2(swsCtx, avutil.Data(pFrame),
|
|
avutil.Linesize(pFrame), 0, pCodecCtx.Height(),
|
|
avutil.Data(pFrameRGB), avutil.Linesize(pFrameRGB))
|
|
|
|
// Save the frame to disk
|
|
fmt.Printf("Writing frame %d\n", frameNumber)
|
|
SaveFrame(pFrameRGB, pCodecCtx.Width(), pCodecCtx.Height(), frameNumber)
|
|
} else {
|
|
return
|
|
}
|
|
frameNumber++
|
|
}
|
|
}
|
|
|
|
// Free the packet that was allocated by av_read_frame
|
|
packet.AvFreePacket()
|
|
}
|
|
|
|
// Free the RGB image
|
|
avutil.AvFree(buffer)
|
|
avutil.AvFrameFree(pFrameRGB)
|
|
|
|
// Free the YUV frame
|
|
avutil.AvFrameFree(pFrame)
|
|
|
|
// Close the codecs
|
|
pCodecCtx.AvcodecClose()
|
|
(*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig)).AvcodecClose()
|
|
|
|
// Close the video file
|
|
pFormatContext.AvformatCloseInput()
|
|
|
|
// Stop after saving frames of first video straem
|
|
break
|
|
|
|
default:
|
|
fmt.Println("Didn't find a video stream")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|