feat: new AVC box implementation

This commit is contained in:
Martin Riedl 2024-12-08 11:10:26 +01:00
parent 1c406419a5
commit 309ecb9a79
Signed by: martinr92
GPG key ID: FB68DA65516A804C
3 changed files with 131 additions and 7 deletions

51
AVCVideoStream.go Normal file
View file

@ -0,0 +1,51 @@
// Copyright 2024 Martin Riedl
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomp4
type AVCSampleEntry struct {
*VisualSampleEntry
// TODO: some more
}
// BoxTypeAVCSampleEntry1 AVC Sample Entry Box
const BoxTypeAVCSampleEntry1 = "avc1"
// BoxTypeAVCSampleEntry3 AVC Sample Entry Box
const BoxTypeAVCSampleEntry3 = "avc3"
func init() {
BoxDefinitions = append(BoxDefinitions, BoxDefinition{
Type: BoxTypeAVCSampleEntry1,
ParentTypes: []string{BoxTypeSampleDescription},
Parser: ParseAVCSampleEntry,
}, BoxDefinition{
Type: BoxTypeAVCSampleEntry3,
ParentTypes: []string{BoxTypeSampleDescription},
Parser: ParseAVCSampleEntry,
})
}
// ParseAVCSampleEntry creates a new AVC sample entry box struct based on bytes
func ParseAVCSampleEntry(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
sampleEntry, position := ParseVisualSampleEntry(filePosition, headerSize, content)
box := &AVCSampleEntry{
VisualSampleEntry: sampleEntry,
}
// TODO: parse other fields
_ = position
return box, nil
}

View file

@ -3,7 +3,7 @@
[![pipeline status](https://gitlab.com/martinr92/gomp4/badges/main/pipeline.svg)](https://gitlab.com/martinr92/gomp4/commits/main) [![pipeline status](https://gitlab.com/martinr92/gomp4/badges/main/pipeline.svg)](https://gitlab.com/martinr92/gomp4/commits/main)
[![coverage report](https://gitlab.com/martinr92/gomp4/badges/main/coverage.svg)](https://gitlab.com/martinr92/gomp4/commits/main) [![coverage report](https://gitlab.com/martinr92/gomp4/badges/main/coverage.svg)](https://gitlab.com/martinr92/gomp4/commits/main)
mp4 implementation in golang based on spec ISO ICE 14496-12:2015 mp4 implementation in golang based on spec ISO/IEC 14496-12:2015
## Parser ## Parser
@ -16,7 +16,7 @@ if err := parser.Parse(); err != nil {
## Progress ## Progress
Implementation progress of ISO ICE 14496-12:2015: Implementation progress of ISO/IEC 14496-12:2015:
| Chapter | Box Types | Parser | | Chapter | Box Types | Parser |
|----------------------------------------------------|----------------|-------:| |----------------------------------------------------|----------------|-------:|
@ -118,6 +118,12 @@ Implementation progress of ISO ICE 14496-12:2015:
| 12.2.7 Audio stream loudness | ludt | - | | 12.2.7 Audio stream loudness | ludt | - |
| 12.4.2 Hint Media Header Box | hmhd | - | | 12.4.2 Hint Media Header Box | hmhd | - |
Implementation progress of ISO/IEC 14496-15:2017:
| Chapter | Box Types | Parser |
|----------------------------------------------------|------------------------------------------|-------:|
| 5.4.2 AVC video stream definition | avc1, avc2, avc3, avc4, avcC, m4ds, btrt | 10% |
## Helper Tools ## Helper Tools
- [Online MP4 file parser](https://www.onlinemp4parser.com/) - [Online MP4 file parser](https://www.onlinemp4parser.com/)

View file

@ -17,7 +17,6 @@ package gomp4
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"log"
) )
// SampleDescriptionBox sample description box struct // SampleDescriptionBox sample description box struct
@ -112,17 +111,85 @@ type SampleEntry struct {
DataReferenceIndex uint16 DataReferenceIndex uint16
} }
func ParseSampleEntry(filePosition uint64, headerSize uint32, content []byte) *SampleEntry { func ParseSampleEntry(filePosition uint64, headerSize uint32, content []byte) (*SampleEntry, int) {
box := &SampleEntry{ box := &SampleEntry{
Box: &Box{filePosition, headerSize}, Box: &Box{filePosition, headerSize},
} }
// skip reserved // skip reserved
position := 2 * 8 position := 6
// parse reference index // parse reference index
box.DataReferenceIndex = binary.BigEndian.Uint16(content[position : position+2]) box.DataReferenceIndex = binary.BigEndian.Uint16(content[position : position+2])
log.Println("data reference index", box.DataReferenceIndex) // TODO: remove me position += 2
return box return box, position
}
// VisualSampleEntry visual sample entry struct
//
// 12.1.3 Sample entry
//
// Video tracks use VisualSampleEntry.
//
// In video tracks, the frame_count field must be 1 unless the specification for the media format explicitly
// documents this template field and permits larger values. That specification must document both how
// the individual frames of video are found (their size information) and their timing established. That
// timing might be as simple as dividing the sample duration by the frame count to establish the frame
// duration.
type VisualSampleEntry struct {
*SampleEntry
Width uint16
Height uint16
// give the resolution of the image in pixelsperinch, as a fixed 16.16 number
HorizResolution Fixed1616
// give the resolution of the image in pixelsperinch, as a fixed 16.16 number
VertResolution Fixed1616
// indicates how many frames of compressed video are stored in each sample. The
// default is 1, for one frame per sample; it may be more than 1 for multiple frames per sample
FrameCount uint16
// is a name, for informative purposes. It is formatted in a fixed 32byte field, with
//the first byte set to the number of bytes to be displayed, followed by that number of bytes of
//displayable data, and then padding to complete 32 bytes total (including the size byte). The field
//may be set to 0.
CompressorName string
Depth uint16
}
func ParseVisualSampleEntry(filePosition uint64, headerSize uint32, content []byte) (*VisualSampleEntry, int) {
sampleEntry, position := ParseSampleEntry(filePosition, headerSize, content)
box := &VisualSampleEntry{
SampleEntry: sampleEntry,
}
// skip pre-defined and reserved bytes
position += 2 + 2 + 4*3
// parse resolution
box.Width = binary.BigEndian.Uint16(content[position : position+2])
box.Height = binary.BigEndian.Uint16(content[position+2 : position+4])
box.HorizResolution = NewFixed1616ByBytes(content[position+4 : position+8])
box.VertResolution = NewFixed1616ByBytes(content[position+8 : position+12])
position += 12
// skip reserved bytes
position += 4
// parse frame count
box.FrameCount = binary.BigEndian.Uint16(content[position : position+2])
position += 2
// parse compressor name length (32bytes total)
compressorNameLength := int(content[position])
box.CompressorName = string(content[position+1 : position+1+compressorNameLength])
position += 32
// parse depth
box.Depth = binary.BigEndian.Uint16(content[position : position+2])
position += 2
// skip pre-defined bytes
position += 2
return box, position
} }